update bangs

This commit is contained in:
Fabian Freund
2026-03-27 19:55:11 +01:00
parent 7253470715
commit e27eda6be6
16 changed files with 3670 additions and 23 deletions
@@ -0,0 +1,912 @@
{
"_meta": {
"description": "This file contains a serialized version of schema entities for drift.",
"version": "1.3.0"
},
"options": {
"store_date_time_values_as_text": false
},
"entities": [
{
"id": 0,
"references": [],
"type": "table",
"data": {
"name": "bang",
"was_declared_in_moor": true,
"columns": [
{
"name": "trigger",
"getter_name": "trigger",
"moor_type": "string",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "group",
"getter_name": "group",
"moor_type": "int",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
"dart_type_name": "BangGroup"
}
},
{
"name": "website_name",
"getter_name": "websiteName",
"moor_type": "string",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "domain",
"getter_name": "domain",
"moor_type": "string",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "url_template",
"getter_name": "urlTemplate",
"moor_type": "string",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "category",
"getter_name": "category",
"moor_type": "string",
"nullable": true,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "sub_category",
"getter_name": "subCategory",
"moor_type": "string",
"nullable": true,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "format",
"getter_name": "format",
"moor_type": "string",
"nullable": true,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const BangFormatConverter()",
"dart_type_name": "Set<BangFormat>?"
}
},
{
"name": "additional_triggers",
"getter_name": "additionalTriggers",
"moor_type": "string",
"nullable": true,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const TriggerListConverter()",
"dart_type_name": "Set<String>?"
}
},
{
"name": "searxng_api",
"getter_name": "searxngApi",
"moor_type": "bool",
"nullable": false,
"customConstraints": "NOT NULL DEFAULT FALSE",
"default_dart": "const CustomExpression('FALSE')",
"default_client_dart": null,
"dsl_features": []
},
{
"name": "snap_domain",
"getter_name": "snapDomain",
"moor_type": "string",
"nullable": true,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
],
"is_virtual": false,
"without_rowid": false,
"constraints": [
"PRIMARY KEY(\"trigger\", \"group\")"
],
"explicit_pk": [
"trigger",
"group"
]
}
},
{
"id": 1,
"references": [
0
],
"type": "table",
"data": {
"name": "bang_triggers",
"was_declared_in_moor": true,
"columns": [
{
"name": "trigger",
"getter_name": "trigger",
"moor_type": "string",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "group",
"getter_name": "group",
"moor_type": "int",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
"dart_type_name": "BangGroup"
}
},
{
"name": "additional_trigger",
"getter_name": "additionalTrigger",
"moor_type": "string",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
],
"is_virtual": false,
"without_rowid": false,
"constraints": [
"PRIMARY KEY(\"trigger\", \"group\", additional_trigger)",
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
],
"explicit_pk": [
"trigger",
"group",
"additional_trigger"
]
}
},
{
"id": 2,
"references": [
1
],
"type": "index",
"data": {
"on": 1,
"name": "idx_bang_triggers_lookup",
"sql": "CREATE INDEX idx_bang_triggers_lookup ON bang_triggers (additional_trigger, \"group\");",
"unique": false,
"columns": []
}
},
{
"id": 3,
"references": [
0,
1
],
"type": "trigger",
"data": {
"on": 0,
"references_in_body": [
0,
1
],
"name": "bang_triggers_after_insert",
"sql": "CREATE TRIGGER bang_triggers_after_insert AFTER INSERT ON bang \nWHEN new.additional_triggers IS NOT NULL\nBEGIN\n INSERT INTO bang_triggers(\"trigger\", \"group\", additional_trigger)\n SELECT \n new.\"trigger\",\n new.\"group\",\n json_each.value\n FROM json_each(new.additional_triggers);\nEND;"
}
},
{
"id": 4,
"references": [
0,
1
],
"type": "trigger",
"data": {
"on": 0,
"references_in_body": [
0,
1
],
"name": "bang_triggers_after_update",
"sql": "CREATE TRIGGER bang_triggers_after_update AFTER UPDATE ON bang BEGIN\n -- Delete old additional triggers\n DELETE FROM bang_triggers \n WHERE \"trigger\" = old.\"trigger\" AND \"group\" = old.\"group\";\n\n -- Insert new additional triggers if they exist\n INSERT INTO bang_triggers(\"trigger\", \"group\", additional_trigger)\n SELECT \n new.\"trigger\",\n new.\"group\",\n json_each.value\n FROM json_each(new.additional_triggers)\n WHERE new.additional_triggers IS NOT NULL;\nEND;"
}
},
{
"id": 5,
"references": [],
"type": "table",
"data": {
"name": "bang_sync",
"was_declared_in_moor": true,
"columns": [
{
"name": "group",
"getter_name": "group",
"moor_type": "int",
"nullable": false,
"customConstraints": "PRIMARY KEY NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
"primary-key"
],
"type_converter": {
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
"dart_type_name": "BangGroup"
}
},
{
"name": "last_sync",
"getter_name": "lastSync",
"moor_type": "dateTime",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
],
"is_virtual": false,
"without_rowid": false,
"constraints": []
}
},
{
"id": 6,
"references": [
0
],
"type": "table",
"data": {
"name": "bang_frequency",
"was_declared_in_moor": true,
"columns": [
{
"name": "trigger",
"getter_name": "trigger",
"moor_type": "string",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "group",
"getter_name": "group",
"moor_type": "int",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
"dart_type_name": "BangGroup"
}
},
{
"name": "frequency",
"getter_name": "frequency",
"moor_type": "int",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "last_used",
"getter_name": "lastUsed",
"moor_type": "dateTime",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
],
"is_virtual": false,
"without_rowid": false,
"constraints": [
"PRIMARY KEY(\"trigger\", \"group\")",
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
],
"explicit_pk": [
"trigger",
"group"
]
}
},
{
"id": 7,
"references": [
0
],
"type": "table",
"data": {
"name": "bang_history",
"was_declared_in_moor": true,
"columns": [
{
"name": "search_query",
"getter_name": "searchQuery",
"moor_type": "string",
"nullable": false,
"customConstraints": "UNIQUE NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
"unique"
]
},
{
"name": "trigger",
"getter_name": "trigger",
"moor_type": "string",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "group",
"getter_name": "group",
"moor_type": "int",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
"dart_type_name": "BangGroup"
}
},
{
"name": "search_date",
"getter_name": "searchDate",
"moor_type": "dateTime",
"nullable": false,
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
],
"is_virtual": false,
"without_rowid": false,
"constraints": [
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
]
}
},
{
"id": 8,
"references": [
0
],
"type": "table",
"data": {
"name": "bang_fts",
"was_declared_in_moor": true,
"columns": [
{
"name": "trigger",
"getter_name": "trigger",
"moor_type": "string",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "website_name",
"getter_name": "websiteName",
"moor_type": "string",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
],
"is_virtual": true,
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"bang_fts\" USING fts5(trigger, website_name, content=bang, prefix='2 3')",
"without_rowid": false,
"constraints": []
}
},
{
"id": 9,
"references": [
1
],
"type": "table",
"data": {
"name": "bang_triggers_fts",
"was_declared_in_moor": true,
"columns": [
{
"name": "additional_trigger",
"getter_name": "additionalTrigger",
"moor_type": "string",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
],
"is_virtual": true,
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"bang_triggers_fts\" USING fts5(additional_trigger, content=bang_triggers, prefix='2 3')",
"without_rowid": false,
"constraints": []
}
},
{
"id": 10,
"references": [
0,
6
],
"type": "view",
"data": {
"name": "bang_data_view",
"sql": "CREATE VIEW bang_data_view AS SELECT b.*, bf.frequency, bf.last_used FROM bang AS b LEFT JOIN bang_frequency AS bf ON b.\"trigger\" = bf.\"trigger\" AND b.\"group\" = bf.\"group\"",
"dart_info_name": "BangDataView",
"columns": [
{
"name": "trigger",
"getter_name": "trigger",
"moor_type": "string",
"nullable": false,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "group",
"getter_name": "group",
"moor_type": "int",
"nullable": false,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
"dart_type_name": "BangGroup"
}
},
{
"name": "website_name",
"getter_name": "websiteName",
"moor_type": "string",
"nullable": false,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "domain",
"getter_name": "domain",
"moor_type": "string",
"nullable": false,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "url_template",
"getter_name": "urlTemplate",
"moor_type": "string",
"nullable": false,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "category",
"getter_name": "category",
"moor_type": "string",
"nullable": true,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "sub_category",
"getter_name": "subCategory",
"moor_type": "string",
"nullable": true,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "format",
"getter_name": "format",
"moor_type": "string",
"nullable": true,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const BangFormatConverter()",
"dart_type_name": "Set<BangFormat>?"
}
},
{
"name": "additional_triggers",
"getter_name": "additionalTriggers",
"moor_type": "string",
"nullable": true,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": [],
"type_converter": {
"dart_expr": "const TriggerListConverter()",
"dart_type_name": "Set<String>?"
}
},
{
"name": "searxng_api",
"getter_name": "searxngApi",
"moor_type": "bool",
"nullable": false,
"customConstraints": null,
"defaultConstraints": "CHECK (\"searxng_api\" IN (0, 1))",
"dialectAwareDefaultConstraints": {
"sqlite": "CHECK (\"searxng_api\" IN (0, 1))"
},
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "snap_domain",
"getter_name": "snapDomain",
"moor_type": "string",
"nullable": true,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "frequency",
"getter_name": "frequency",
"moor_type": "int",
"nullable": true,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "last_used",
"getter_name": "lastUsed",
"moor_type": "dateTime",
"nullable": true,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
]
}
},
{
"id": 11,
"references": [
0,
8
],
"type": "trigger",
"data": {
"on": 0,
"references_in_body": [
0,
8
],
"name": "bang_after_insert",
"sql": "CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN\n INSERT INTO \n bang_fts(rowid, \"trigger\", website_name) \n VALUES (new.rowid, new.\"trigger\", new.website_name);\nEND;"
}
},
{
"id": 12,
"references": [
0,
8
],
"type": "trigger",
"data": {
"on": 0,
"references_in_body": [
0,
8
],
"name": "bang_after_delete",
"sql": "CREATE TRIGGER bang_after_delete AFTER DELETE ON bang BEGIN\n INSERT INTO \n bang_fts(bang_fts, rowid, \"trigger\", website_name) \n VALUES('delete', old.rowid, old.\"trigger\", old.website_name);\nEND;"
}
},
{
"id": 13,
"references": [
0,
8
],
"type": "trigger",
"data": {
"on": 0,
"references_in_body": [
0,
8
],
"name": "bang_after_update",
"sql": "CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN\n INSERT INTO \n bang_fts(bang_fts, rowid, \"trigger\", website_name) \n VALUES('delete', old.rowid, old.\"trigger\", old.website_name);\n INSERT INTO \n bang_fts(rowid, \"trigger\", website_name) \n VALUES (new.rowid, new.\"trigger\", new.website_name);\nEND;"
}
},
{
"id": 14,
"references": [
1,
9
],
"type": "trigger",
"data": {
"on": 1,
"references_in_body": [
1,
9
],
"name": "bang_triggers_after_insert_fts",
"sql": "CREATE TRIGGER bang_triggers_after_insert_fts AFTER INSERT ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(rowid, additional_trigger) \n VALUES (new.rowid, new.additional_trigger);\nEND;"
}
},
{
"id": 15,
"references": [
1,
9
],
"type": "trigger",
"data": {
"on": 1,
"references_in_body": [
1,
9
],
"name": "bang_triggers_after_delete_fts",
"sql": "CREATE TRIGGER bang_triggers_after_delete_fts AFTER DELETE ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(bang_triggers_fts, rowid, additional_trigger) \n VALUES('delete', old.rowid, old.additional_trigger);\nEND;"
}
},
{
"id": 16,
"references": [
1,
9
],
"type": "trigger",
"data": {
"on": 1,
"references_in_body": [
1,
9
],
"name": "bang_triggers_after_update_fts",
"sql": "CREATE TRIGGER bang_triggers_after_update_fts AFTER UPDATE ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(bang_triggers_fts, rowid, additional_trigger) \n VALUES('delete', old.rowid, old.additional_trigger);\n INSERT INTO \n bang_triggers_fts(rowid, additional_trigger) \n VALUES (new.rowid, new.additional_trigger);\nEND;"
}
}
],
"fixed_sql": [
{
"name": "bang",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TABLE IF NOT EXISTS \"bang\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"website_name\" TEXT NOT NULL, \"domain\" TEXT NOT NULL, \"url_template\" TEXT NOT NULL, \"category\" TEXT, \"sub_category\" TEXT, \"format\" TEXT, \"additional_triggers\" TEXT, \"searxng_api\" INTEGER NOT NULL DEFAULT FALSE, \"snap_domain\" TEXT, PRIMARY KEY(\"trigger\", \"group\"));"
}
]
},
{
"name": "bang_triggers",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TABLE IF NOT EXISTS \"bang_triggers\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"additional_trigger\" TEXT NOT NULL, PRIMARY KEY(\"trigger\", \"group\", additional_trigger), FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
}
]
},
{
"name": "idx_bang_triggers_lookup",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE INDEX idx_bang_triggers_lookup ON bang_triggers (additional_trigger, \"group\")"
}
]
},
{
"name": "bang_triggers_after_insert",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TRIGGER bang_triggers_after_insert AFTER INSERT ON bang WHEN new.additional_triggers IS NOT NULL BEGIN INSERT INTO bang_triggers (\"trigger\", \"group\", additional_trigger) SELECT new.\"trigger\", new.\"group\", json_each.value FROM json_each(new.additional_triggers);END"
}
]
},
{
"name": "bang_triggers_after_update",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TRIGGER bang_triggers_after_update AFTER UPDATE ON bang BEGIN DELETE FROM bang_triggers WHERE \"trigger\" = old.\"trigger\" AND \"group\" = old.\"group\";INSERT INTO bang_triggers (\"trigger\", \"group\", additional_trigger) SELECT new.\"trigger\", new.\"group\", json_each.value FROM json_each(new.additional_triggers)WHERE new.additional_triggers IS NOT NULL;END"
}
]
},
{
"name": "bang_sync",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TABLE IF NOT EXISTS \"bang_sync\" (\"group\" INTEGER PRIMARY KEY NOT NULL, \"last_sync\" INTEGER NOT NULL);"
}
]
},
{
"name": "bang_frequency",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TABLE IF NOT EXISTS \"bang_frequency\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"frequency\" INTEGER NOT NULL, \"last_used\" INTEGER NOT NULL, PRIMARY KEY(\"trigger\", \"group\"), FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
}
]
},
{
"name": "bang_history",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TABLE IF NOT EXISTS \"bang_history\" (\"search_query\" TEXT UNIQUE NOT NULL, \"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"search_date\" INTEGER NOT NULL, FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
}
]
},
{
"name": "bang_fts",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"bang_fts\" USING fts5(trigger, website_name, content=bang, prefix='2 3');"
}
]
},
{
"name": "bang_triggers_fts",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"bang_triggers_fts\" USING fts5(additional_trigger, content=bang_triggers, prefix='2 3');"
}
]
},
{
"name": "bang_data_view",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE VIEW bang_data_view AS SELECT b.*, bf.frequency, bf.last_used FROM bang AS b LEFT JOIN bang_frequency AS bf ON b.\"trigger\" = bf.\"trigger\" AND b.\"group\" = bf.\"group\""
}
]
},
{
"name": "bang_after_insert",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN INSERT INTO bang_fts (\"rowid\", \"trigger\", website_name) VALUES (new.\"rowid\", new.\"trigger\", new.website_name);END"
}
]
},
{
"name": "bang_after_delete",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TRIGGER bang_after_delete AFTER DELETE ON bang BEGIN INSERT INTO bang_fts (bang_fts, \"rowid\", \"trigger\", website_name) VALUES ('delete', old.\"rowid\", old.\"trigger\", old.website_name);END"
}
]
},
{
"name": "bang_after_update",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN INSERT INTO bang_fts (bang_fts, \"rowid\", \"trigger\", website_name) VALUES ('delete', old.\"rowid\", old.\"trigger\", old.website_name);INSERT INTO bang_fts (\"rowid\", \"trigger\", website_name) VALUES (new.\"rowid\", new.\"trigger\", new.website_name);END"
}
]
},
{
"name": "bang_triggers_after_insert_fts",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TRIGGER bang_triggers_after_insert_fts AFTER INSERT ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (\"rowid\", additional_trigger) VALUES (new.\"rowid\", new.additional_trigger);END"
}
]
},
{
"name": "bang_triggers_after_delete_fts",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TRIGGER bang_triggers_after_delete_fts AFTER DELETE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (bang_triggers_fts, \"rowid\", additional_trigger) VALUES ('delete', old.\"rowid\", old.additional_trigger);END"
}
]
},
{
"name": "bang_triggers_after_update_fts",
"sql": [
{
"dialect": "sqlite",
"sql": "CREATE TRIGGER bang_triggers_after_update_fts AFTER UPDATE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (bang_triggers_fts, \"rowid\", additional_trigger) VALUES ('delete', old.\"rowid\", old.additional_trigger);INSERT INTO bang_triggers_fts (\"rowid\", additional_trigger) VALUES (new.\"rowid\", new.additional_trigger);END"
}
]
}
]
}
@@ -25,12 +25,13 @@ import 'package:weblibre/features/bangs/data/database/daos/bang.dart';
import 'package:weblibre/features/bangs/data/database/daos/sync.dart'; import 'package:weblibre/features/bangs/data/database/daos/sync.dart';
import 'package:weblibre/features/bangs/data/database/database.drift.dart'; import 'package:weblibre/features/bangs/data/database/database.drift.dart';
import 'package:weblibre/features/bangs/data/database/database.steps.dart'; import 'package:weblibre/features/bangs/data/database/database.steps.dart';
import 'package:weblibre/features/bangs/data/database/definitions.drift.dart';
import 'package:weblibre/features/search/domain/fts_tokenizer.dart'; import 'package:weblibre/features/search/domain/fts_tokenizer.dart';
@DriftDatabase(include: {'definitions.drift'}, daos: [BangDao, SyncDao]) @DriftDatabase(include: {'definitions.drift'}, daos: [BangDao, SyncDao])
class BangDatabase extends $BangDatabase with PrefixQueryBuilderMixin { class BangDatabase extends $BangDatabase with PrefixQueryBuilderMixin {
@override @override
final int schemaVersion = 4; final int schemaVersion = 5;
@override @override
final int ftsTokenLimit = 6; final int ftsTokenLimit = 6;
@@ -71,13 +72,55 @@ class BangDatabase extends $BangDatabase with PrefixQueryBuilderMixin {
await validateDatabaseSchema(); await validateDatabaseSchema();
} }
await customStatement('PRAGMA foreign_keys = ON');
if (details.hadUpgrade && details.versionBefore != null) { if (details.hadUpgrade && details.versionBefore != null) {
if (details.versionBefore! < 3) { if (details.versionBefore! < 3) {
await bang.deleteWhere((t) => t.group.equals(3)); await bang.deleteWhere((t) => t.group.equals(3));
await bangTriggers.deleteWhere((t) => t.group.equals(3));
await bangSync.deleteWhere((t) => t.group.equals(3));
await bangFrequency.deleteWhere((t) => t.group.equals(3));
await bangHistory.deleteWhere((t) => t.group.equals(3));
} else if (details.versionBefore! < 5) {
await bang.deleteWhere((t) => t.group.equals(1));
await bangTriggers.deleteWhere((t) => t.group.equals(1));
await bangSync.deleteWhere((t) => t.group.equals(1));
await bangFrequency.deleteWhere((t) => t.group.equals(1));
await bangHistory.deleteWhere((t) => t.group.equals(1));
await (bang.update()..where((t) => t.group.isBiggerThanValue(0)))
.write(
BangCompanion.custom(group: bang.group - const Constant(1)),
);
await (bangTriggers.update()
..where((t) => t.group.isBiggerThanValue(0)))
.write(
BangTriggersCompanion.custom(
group: bangTriggers.group - const Constant(1),
),
);
await (bangSync.update()..where((t) => t.group.isBiggerThanValue(0)))
.write(
BangSyncCompanion.custom(
group: bangSync.group - const Constant(1),
),
);
await (bangFrequency.update()
..where((t) => t.group.isBiggerThanValue(0)))
.write(
BangFrequencyCompanion.custom(
group: bangFrequency.group - const Constant(1),
),
);
await (bangHistory.update()
..where((t) => t.group.isBiggerThanValue(0)))
.write(
BangHistoryCompanion.custom(
group: bangHistory.group - const Constant(1),
),
);
} }
} }
await customStatement('PRAGMA foreign_keys = ON');
}, },
); );
@@ -88,11 +131,13 @@ class BangDatabase extends $BangDatabase with PrefixQueryBuilderMixin {
//Too many changes, we switch to a new database //Too many changes, we switch to a new database
}, },
from2To3: (m, schema) async { from2To3: (m, schema) async {
final bangAtV3 = schema.bang; await m.addColumn(schema.bang, schema.bang.searxngApi);
await m.addColumn(bangAtV3, bangAtV3.searxngApi);
}, },
from3To4: (m, schema) async { from3To4: (m, schema) async {
await m.alterTable(TableMigration(schema.bangHistory)); await m.alterTable(TableMigration(schema.bangHistory));
}, },
from4To5: (m, schema) async {
await m.addColumn(schema.bang, schema.bang.snapDomain);
},
); );
} }
@@ -1045,10 +1045,263 @@ i1.GeneratedColumn<int> _column_40(String aliasedName) =>
true, true,
type: i1.DriftSqlType.int, type: i1.DriftSqlType.int,
); );
final class Schema5 extends i0.VersionedSchema {
Schema5({required super.database}) : super(version: 5);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
bang,
bangTriggers,
idxBangTriggersLookup,
bangTriggersAfterInsert,
bangTriggersAfterUpdate,
bangSync,
bangFrequency,
bangHistory,
bangFts,
bangTriggersFts,
bangDataView,
bangAfterInsert,
bangAfterDelete,
bangAfterUpdate,
bangTriggersAfterInsertFts,
bangTriggersAfterDeleteFts,
bangTriggersAfterUpdateFts,
];
late final Shape15 bang = Shape15(
source: i0.VersionedTable(
entityName: 'bang',
withoutRowId: false,
isStrict: false,
tableConstraints: ['PRIMARY KEY("trigger", "group")'],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_4,
_column_5,
_column_6,
_column_7,
_column_26,
_column_32,
_column_41,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape7 bangTriggers = Shape7(
source: i0.VersionedTable(
entityName: 'bang_triggers',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY("trigger", "group", additional_trigger)',
'FOREIGN KEY("trigger", "group")REFERENCES bang("trigger", "group")ON DELETE CASCADE',
],
columns: [_column_0, _column_1, _column_28],
attachedDatabase: database,
),
alias: null,
);
final i1.Index idxBangTriggersLookup = i1.Index(
'idx_bang_triggers_lookup',
'CREATE INDEX idx_bang_triggers_lookup ON bang_triggers (additional_trigger, "group")',
);
final i1.Trigger bangTriggersAfterInsert = i1.Trigger(
'CREATE TRIGGER bang_triggers_after_insert AFTER INSERT ON bang WHEN new.additional_triggers IS NOT NULL BEGIN INSERT INTO bang_triggers ("trigger", "group", additional_trigger) SELECT new."trigger", new."group", json_each.value FROM json_each(new.additional_triggers);END',
'bang_triggers_after_insert',
);
final i1.Trigger bangTriggersAfterUpdate = i1.Trigger(
'CREATE TRIGGER bang_triggers_after_update AFTER UPDATE ON bang BEGIN DELETE FROM bang_triggers WHERE "trigger" = old."trigger" AND "group" = old."group";INSERT INTO bang_triggers ("trigger", "group", additional_trigger) SELECT new."trigger", new."group", json_each.value FROM json_each(new.additional_triggers)WHERE new.additional_triggers IS NOT NULL;END',
'bang_triggers_after_update',
);
late final Shape11 bangSync = Shape11(
source: i0.VersionedTable(
entityName: 'bang_sync',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [_column_8, _column_33],
attachedDatabase: database,
),
alias: null,
);
late final Shape12 bangFrequency = Shape12(
source: i0.VersionedTable(
entityName: 'bang_frequency',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY("trigger", "group")',
'FOREIGN KEY("trigger", "group")REFERENCES bang("trigger", "group")ON DELETE CASCADE',
],
columns: [_column_0, _column_1, _column_10, _column_34],
attachedDatabase: database,
),
alias: null,
);
late final Shape13 bangHistory = Shape13(
source: i0.VersionedTable(
entityName: 'bang_history',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'FOREIGN KEY("trigger", "group")REFERENCES bang("trigger", "group")ON DELETE CASCADE',
],
columns: [_column_12, _column_0, _column_1, _column_35],
attachedDatabase: database,
),
alias: null,
);
late final Shape4 bangFts = Shape4(
source: i0.VersionedVirtualTable(
entityName: 'bang_fts',
moduleAndArgs:
'fts5(trigger, website_name, content=bang, prefix=\'2 3\')',
columns: [_column_36, _column_37],
attachedDatabase: database,
),
alias: null,
);
late final Shape8 bangTriggersFts = Shape8(
source: i0.VersionedVirtualTable(
entityName: 'bang_triggers_fts',
moduleAndArgs:
'fts5(additional_trigger, content=bang_triggers, prefix=\'2 3\')',
columns: [_column_38],
attachedDatabase: database,
),
alias: null,
);
late final Shape16 bangDataView = Shape16(
source: i0.VersionedView(
entityName: 'bang_data_view',
createViewStmt:
'CREATE VIEW bang_data_view AS SELECT b.*, bf.frequency, bf.last_used FROM bang AS b LEFT JOIN bang_frequency AS bf ON b."trigger" = bf."trigger" AND b."group" = bf."group";',
columns: [
_column_16,
_column_17,
_column_18,
_column_19,
_column_20,
_column_21,
_column_22,
_column_23,
_column_30,
_column_39,
_column_42,
_column_24,
_column_40,
],
attachedDatabase: database,
),
alias: null,
);
final i1.Trigger bangAfterInsert = i1.Trigger(
'CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN INSERT INTO bang_fts ("rowid", "trigger", website_name) VALUES (new."rowid", new."trigger", new.website_name);END',
'bang_after_insert',
);
final i1.Trigger bangAfterDelete = i1.Trigger(
'CREATE TRIGGER bang_after_delete AFTER DELETE ON bang BEGIN INSERT INTO bang_fts (bang_fts, "rowid", "trigger", website_name) VALUES (\'delete\', old."rowid", old."trigger", old.website_name);END',
'bang_after_delete',
);
final i1.Trigger bangAfterUpdate = i1.Trigger(
'CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN INSERT INTO bang_fts (bang_fts, "rowid", "trigger", website_name) VALUES (\'delete\', old."rowid", old."trigger", old.website_name);INSERT INTO bang_fts ("rowid", "trigger", website_name) VALUES (new."rowid", new."trigger", new.website_name);END',
'bang_after_update',
);
final i1.Trigger bangTriggersAfterInsertFts = i1.Trigger(
'CREATE TRIGGER bang_triggers_after_insert_fts AFTER INSERT ON bang_triggers BEGIN INSERT INTO bang_triggers_fts ("rowid", additional_trigger) VALUES (new."rowid", new.additional_trigger);END',
'bang_triggers_after_insert_fts',
);
final i1.Trigger bangTriggersAfterDeleteFts = i1.Trigger(
'CREATE TRIGGER bang_triggers_after_delete_fts AFTER DELETE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (bang_triggers_fts, "rowid", additional_trigger) VALUES (\'delete\', old."rowid", old.additional_trigger);END',
'bang_triggers_after_delete_fts',
);
final i1.Trigger bangTriggersAfterUpdateFts = i1.Trigger(
'CREATE TRIGGER bang_triggers_after_update_fts AFTER UPDATE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (bang_triggers_fts, "rowid", additional_trigger) VALUES (\'delete\', old."rowid", old.additional_trigger);INSERT INTO bang_triggers_fts ("rowid", additional_trigger) VALUES (new."rowid", new.additional_trigger);END',
'bang_triggers_after_update_fts',
);
}
class Shape15 extends i0.VersionedTable {
Shape15({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get trigger =>
columnsByName['trigger']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get group =>
columnsByName['group']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get websiteName =>
columnsByName['website_name']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get domain =>
columnsByName['domain']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get urlTemplate =>
columnsByName['url_template']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get category =>
columnsByName['category']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get subCategory =>
columnsByName['sub_category']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get format =>
columnsByName['format']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get additionalTriggers =>
columnsByName['additional_triggers']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get searxngApi =>
columnsByName['searxng_api']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get snapDomain =>
columnsByName['snap_domain']! as i1.GeneratedColumn<String>;
}
i1.GeneratedColumn<String> _column_41(String aliasedName) =>
i1.GeneratedColumn<String>(
'snap_domain',
aliasedName,
true,
type: i1.DriftSqlType.string,
$customConstraints: '',
);
class Shape16 extends i0.VersionedView {
Shape16({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get trigger =>
columnsByName['trigger']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get group =>
columnsByName['group']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get websiteName =>
columnsByName['website_name']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get domain =>
columnsByName['domain']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get urlTemplate =>
columnsByName['url_template']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get category =>
columnsByName['category']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get subCategory =>
columnsByName['sub_category']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get format =>
columnsByName['format']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get additionalTriggers =>
columnsByName['additional_triggers']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get searxngApi =>
columnsByName['searxng_api']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get snapDomain =>
columnsByName['snap_domain']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get frequency =>
columnsByName['frequency']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get lastUsed =>
columnsByName['last_used']! as i1.GeneratedColumn<int>;
}
i1.GeneratedColumn<String> _column_42(String aliasedName) =>
i1.GeneratedColumn<String>(
'snap_domain',
aliasedName,
true,
type: i1.DriftSqlType.string,
);
i0.MigrationStepWithVersion migrationSteps({ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2, required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3, required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4, required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
}) { }) {
return (currentVersion, database) async { return (currentVersion, database) async {
switch (currentVersion) { switch (currentVersion) {
@@ -1067,6 +1320,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema); final migrator = i1.Migrator(database, schema);
await from3To4(migrator, schema); await from3To4(migrator, schema);
return 4; return 4;
case 4:
final schema = Schema5(database: database);
final migrator = i1.Migrator(database, schema);
await from4To5(migrator, schema);
return 5;
default: default:
throw ArgumentError.value('Unknown migration from $currentVersion'); throw ArgumentError.value('Unknown migration from $currentVersion');
} }
@@ -1077,10 +1335,12 @@ i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2, required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3, required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4, required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
}) => i0.VersionedSchema.stepByStepHelper( }) => i0.VersionedSchema.stepByStepHelper(
step: migrationSteps( step: migrationSteps(
from1To2: from1To2, from1To2: from1To2,
from2To3: from2To3, from2To3: from2To3,
from3To4: from3To4, from3To4: from3To4,
from4To5: from4To5,
), ),
); );
@@ -16,6 +16,7 @@ CREATE TABLE bang (
format TEXT MAPPED BY `const BangFormatConverter()`, format TEXT MAPPED BY `const BangFormatConverter()`,
additional_triggers TEXT MAPPED BY `const TriggerListConverter()`, additional_triggers TEXT MAPPED BY `const TriggerListConverter()`,
searxng_api BOOL NOT NULL DEFAULT FALSE, searxng_api BOOL NOT NULL DEFAULT FALSE,
snap_domain TEXT,
PRIMARY KEY ("trigger", "group") PRIMARY KEY ("trigger", "group")
) WITH Bang; ) WITH Bang;
@@ -26,6 +26,7 @@ typedef $BangTableCreateCompanionBuilder =
i0.Value<Set<i1.BangFormat>?> format, i0.Value<Set<i1.BangFormat>?> format,
i0.Value<Set<String>?> additionalTriggers, i0.Value<Set<String>?> additionalTriggers,
i0.Value<bool> searxngApi, i0.Value<bool> searxngApi,
i0.Value<String?> snapDomain,
i0.Value<int> rowid, i0.Value<int> rowid,
}); });
typedef $BangTableUpdateCompanionBuilder = typedef $BangTableUpdateCompanionBuilder =
@@ -40,6 +41,7 @@ typedef $BangTableUpdateCompanionBuilder =
i0.Value<Set<i1.BangFormat>?> format, i0.Value<Set<i1.BangFormat>?> format,
i0.Value<Set<String>?> additionalTriggers, i0.Value<Set<String>?> additionalTriggers,
i0.Value<bool> searxngApi, i0.Value<bool> searxngApi,
i0.Value<String?> snapDomain,
i0.Value<int> rowid, i0.Value<int> rowid,
}); });
@@ -108,6 +110,11 @@ class $BangTableFilterComposer
column: $table.searxngApi, column: $table.searxngApi,
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
i0.ColumnFilters<String> get snapDomain => $composableBuilder(
column: $table.snapDomain,
builder: (column) => i0.ColumnFilters(column),
);
} }
class $BangTableOrderingComposer class $BangTableOrderingComposer
@@ -168,6 +175,11 @@ class $BangTableOrderingComposer
column: $table.searxngApi, column: $table.searxngApi,
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
i0.ColumnOrderings<String> get snapDomain => $composableBuilder(
column: $table.snapDomain,
builder: (column) => i0.ColumnOrderings(column),
);
} }
class $BangTableAnnotationComposer class $BangTableAnnotationComposer
@@ -219,6 +231,11 @@ class $BangTableAnnotationComposer
column: $table.searxngApi, column: $table.searxngApi,
builder: (column) => column, builder: (column) => column,
); );
i0.GeneratedColumn<String> get snapDomain => $composableBuilder(
column: $table.snapDomain,
builder: (column) => column,
);
} }
class $BangTableTableManager class $BangTableTableManager
@@ -263,6 +280,7 @@ class $BangTableTableManager
i0.Value<Set<String>?> additionalTriggers = i0.Value<Set<String>?> additionalTriggers =
const i0.Value.absent(), const i0.Value.absent(),
i0.Value<bool> searxngApi = const i0.Value.absent(), i0.Value<bool> searxngApi = const i0.Value.absent(),
i0.Value<String?> snapDomain = const i0.Value.absent(),
i0.Value<int> rowid = const i0.Value.absent(), i0.Value<int> rowid = const i0.Value.absent(),
}) => i3.BangCompanion( }) => i3.BangCompanion(
trigger: trigger, trigger: trigger,
@@ -275,6 +293,7 @@ class $BangTableTableManager
format: format, format: format,
additionalTriggers: additionalTriggers, additionalTriggers: additionalTriggers,
searxngApi: searxngApi, searxngApi: searxngApi,
snapDomain: snapDomain,
rowid: rowid, rowid: rowid,
), ),
createCompanionCallback: createCompanionCallback:
@@ -290,6 +309,7 @@ class $BangTableTableManager
i0.Value<Set<String>?> additionalTriggers = i0.Value<Set<String>?> additionalTriggers =
const i0.Value.absent(), const i0.Value.absent(),
i0.Value<bool> searxngApi = const i0.Value.absent(), i0.Value<bool> searxngApi = const i0.Value.absent(),
i0.Value<String?> snapDomain = const i0.Value.absent(),
i0.Value<int> rowid = const i0.Value.absent(), i0.Value<int> rowid = const i0.Value.absent(),
}) => i3.BangCompanion.insert( }) => i3.BangCompanion.insert(
trigger: trigger, trigger: trigger,
@@ -302,6 +322,7 @@ class $BangTableTableManager
format: format, format: format,
additionalTriggers: additionalTriggers, additionalTriggers: additionalTriggers,
searxngApi: searxngApi, searxngApi: searxngApi,
snapDomain: snapDomain,
rowid: rowid, rowid: rowid,
), ),
withReferenceMapper: (p0) => p0 withReferenceMapper: (p0) => p0
@@ -1394,6 +1415,14 @@ class BangTable extends i0.Table with i0.TableInfo<BangTable, i1.Bang> {
$customConstraints: 'NOT NULL DEFAULT FALSE', $customConstraints: 'NOT NULL DEFAULT FALSE',
defaultValue: const i0.CustomExpression('FALSE'), defaultValue: const i0.CustomExpression('FALSE'),
); );
late final i0.GeneratedColumn<String> snapDomain = i0.GeneratedColumn<String>(
'snap_domain',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
$customConstraints: '',
);
@override @override
List<i0.GeneratedColumn> get $columns => [ List<i0.GeneratedColumn> get $columns => [
trigger, trigger,
@@ -1406,6 +1435,7 @@ class BangTable extends i0.Table with i0.TableInfo<BangTable, i1.Bang> {
format, format,
additionalTriggers, additionalTriggers,
searxngApi, searxngApi,
snapDomain,
]; ];
@override @override
String get aliasedName => _alias ?? actualTableName; String get aliasedName => _alias ?? actualTableName;
@@ -1464,6 +1494,10 @@ class BangTable extends i0.Table with i0.TableInfo<BangTable, i1.Bang> {
data['${effectivePrefix}additional_triggers'], data['${effectivePrefix}additional_triggers'],
), ),
), ),
snapDomain: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}snap_domain'],
),
); );
} }
@@ -1497,6 +1531,7 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
final i0.Value<Set<i1.BangFormat>?> format; final i0.Value<Set<i1.BangFormat>?> format;
final i0.Value<Set<String>?> additionalTriggers; final i0.Value<Set<String>?> additionalTriggers;
final i0.Value<bool> searxngApi; final i0.Value<bool> searxngApi;
final i0.Value<String?> snapDomain;
final i0.Value<int> rowid; final i0.Value<int> rowid;
const BangCompanion({ const BangCompanion({
this.trigger = const i0.Value.absent(), this.trigger = const i0.Value.absent(),
@@ -1509,6 +1544,7 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
this.format = const i0.Value.absent(), this.format = const i0.Value.absent(),
this.additionalTriggers = const i0.Value.absent(), this.additionalTriggers = const i0.Value.absent(),
this.searxngApi = const i0.Value.absent(), this.searxngApi = const i0.Value.absent(),
this.snapDomain = const i0.Value.absent(),
this.rowid = const i0.Value.absent(), this.rowid = const i0.Value.absent(),
}); });
BangCompanion.insert({ BangCompanion.insert({
@@ -1522,6 +1558,7 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
this.format = const i0.Value.absent(), this.format = const i0.Value.absent(),
this.additionalTriggers = const i0.Value.absent(), this.additionalTriggers = const i0.Value.absent(),
this.searxngApi = const i0.Value.absent(), this.searxngApi = const i0.Value.absent(),
this.snapDomain = const i0.Value.absent(),
this.rowid = const i0.Value.absent(), this.rowid = const i0.Value.absent(),
}) : trigger = i0.Value(trigger), }) : trigger = i0.Value(trigger),
group = i0.Value(group), group = i0.Value(group),
@@ -1539,6 +1576,7 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
i0.Expression<String>? format, i0.Expression<String>? format,
i0.Expression<String>? additionalTriggers, i0.Expression<String>? additionalTriggers,
i0.Expression<bool>? searxngApi, i0.Expression<bool>? searxngApi,
i0.Expression<String>? snapDomain,
i0.Expression<int>? rowid, i0.Expression<int>? rowid,
}) { }) {
return i0.RawValuesInsertable({ return i0.RawValuesInsertable({
@@ -1552,6 +1590,7 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
if (format != null) 'format': format, if (format != null) 'format': format,
if (additionalTriggers != null) 'additional_triggers': additionalTriggers, if (additionalTriggers != null) 'additional_triggers': additionalTriggers,
if (searxngApi != null) 'searxng_api': searxngApi, if (searxngApi != null) 'searxng_api': searxngApi,
if (snapDomain != null) 'snap_domain': snapDomain,
if (rowid != null) 'rowid': rowid, if (rowid != null) 'rowid': rowid,
}); });
} }
@@ -1567,6 +1606,7 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
i0.Value<Set<i1.BangFormat>?>? format, i0.Value<Set<i1.BangFormat>?>? format,
i0.Value<Set<String>?>? additionalTriggers, i0.Value<Set<String>?>? additionalTriggers,
i0.Value<bool>? searxngApi, i0.Value<bool>? searxngApi,
i0.Value<String?>? snapDomain,
i0.Value<int>? rowid, i0.Value<int>? rowid,
}) { }) {
return i3.BangCompanion( return i3.BangCompanion(
@@ -1580,6 +1620,7 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
format: format ?? this.format, format: format ?? this.format,
additionalTriggers: additionalTriggers ?? this.additionalTriggers, additionalTriggers: additionalTriggers ?? this.additionalTriggers,
searxngApi: searxngApi ?? this.searxngApi, searxngApi: searxngApi ?? this.searxngApi,
snapDomain: snapDomain ?? this.snapDomain,
rowid: rowid ?? this.rowid, rowid: rowid ?? this.rowid,
); );
} }
@@ -1625,6 +1666,9 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
if (searxngApi.present) { if (searxngApi.present) {
map['searxng_api'] = i0.Variable<bool>(searxngApi.value); map['searxng_api'] = i0.Variable<bool>(searxngApi.value);
} }
if (snapDomain.present) {
map['snap_domain'] = i0.Variable<String>(snapDomain.value);
}
if (rowid.present) { if (rowid.present) {
map['rowid'] = i0.Variable<int>(rowid.value); map['rowid'] = i0.Variable<int>(rowid.value);
} }
@@ -1644,6 +1688,7 @@ class BangCompanion extends i0.UpdateCompanion<i1.Bang> {
..write('format: $format, ') ..write('format: $format, ')
..write('additionalTriggers: $additionalTriggers, ') ..write('additionalTriggers: $additionalTriggers, ')
..write('searxngApi: $searxngApi, ') ..write('searxngApi: $searxngApi, ')
..write('snapDomain: $snapDomain, ')
..write('rowid: $rowid') ..write('rowid: $rowid')
..write(')')) ..write(')'))
.toString(); .toString();
@@ -3033,6 +3078,7 @@ class BangDataView extends i0.ViewInfo<i3.BangDataView, i6.BangData>
format, format,
additionalTriggers, additionalTriggers,
searxngApi, searxngApi,
snapDomain,
frequency, frequency,
lastUsed, lastUsed,
]; ];
@@ -3097,6 +3143,10 @@ class BangDataView extends i0.ViewInfo<i3.BangDataView, i6.BangData>
data['${effectivePrefix}additional_triggers'], data['${effectivePrefix}additional_triggers'],
), ),
), ),
snapDomain: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}snap_domain'],
),
frequency: attachedDatabase.typeMapping.read( frequency: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, i0.DriftSqlType.int,
data['${effectivePrefix}frequency'], data['${effectivePrefix}frequency'],
@@ -3177,6 +3227,12 @@ class BangDataView extends i0.ViewInfo<i3.BangDataView, i6.BangData>
'CHECK ("searxng_api" IN (0, 1))', 'CHECK ("searxng_api" IN (0, 1))',
), ),
); );
late final i0.GeneratedColumn<String> snapDomain = i0.GeneratedColumn<String>(
'snap_domain',
aliasedName,
true,
type: i0.DriftSqlType.string,
);
late final i0.GeneratedColumn<int> frequency = i0.GeneratedColumn<int>( late final i0.GeneratedColumn<int> frequency = i0.GeneratedColumn<int>(
'frequency', 'frequency',
aliasedName, aliasedName,
@@ -3265,6 +3321,7 @@ class DefinitionsDrift extends i7.ModularAccessor {
additionalTriggers: i3.BangTable.$converteradditionalTriggers.fromSql( additionalTriggers: i3.BangTable.$converteradditionalTriggers.fromSql(
row.readNullable<String>('additional_triggers'), row.readNullable<String>('additional_triggers'),
), ),
snapDomain: row.readNullable<String>('snap_domain'),
frequency: row.readNullable<int>('frequency'), frequency: row.readNullable<int>('frequency'),
lastUsed: row.readNullable<DateTime>('last_used'), lastUsed: row.readNullable<DateTime>('last_used'),
), ),
@@ -3292,6 +3349,7 @@ class DefinitionsDrift extends i7.ModularAccessor {
additionalTriggers: i3.BangTable.$converteradditionalTriggers.fromSql( additionalTriggers: i3.BangTable.$converteradditionalTriggers.fromSql(
row.readNullable<String>('additional_triggers'), row.readNullable<String>('additional_triggers'),
), ),
snapDomain: row.readNullable<String>('snap_domain'),
frequency: row.readNullable<int>('frequency'), frequency: row.readNullable<int>('frequency'),
lastUsed: row.readNullable<DateTime>('last_used'), lastUsed: row.readNullable<DateTime>('last_used'),
), ),
+24 -4
View File
@@ -21,6 +21,7 @@ import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:drift/drift.dart' show Expression, Insertable, Value; import 'package:drift/drift.dart' show Expression, Insertable, Value;
import 'package:fast_equatable/fast_equatable.dart'; import 'package:fast_equatable/fast_equatable.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:nullability/nullability.dart';
import 'package:weblibre/extensions/uri.dart'; import 'package:weblibre/extensions/uri.dart';
import 'package:weblibre/features/bangs/data/database/definitions.drift.dart'; import 'package:weblibre/features/bangs/data/database/definitions.drift.dart';
import 'package:weblibre/features/bangs/data/models/bang_group.dart'; import 'package:weblibre/features/bangs/data/models/bang_group.dart';
@@ -42,6 +43,10 @@ enum BangFormat {
///with one or the other. ///with one or the other.
@JsonValue('url_encode_space_to_plus') @JsonValue('url_encode_space_to_plus')
urlEncodeSpaceToPlus, urlEncodeSpaceToPlus,
///When the bang is invoked with no query, open the snap domain (ad) instead of any path given in the template
@JsonValue('open_snap_domain')
openSnapDomain,
} }
@JsonSerializable() @JsonSerializable()
@@ -84,6 +89,10 @@ class Bang with FastEquatable implements Insertable<Bang> {
@JsonKey(name: 'ts') @JsonKey(name: 'ts')
final Set<String>? additionalTriggers; final Set<String>? additionalTriggers;
///Additional triggers that invoke this bang
@JsonKey(name: 'ad')
final String? snapDomain;
@JsonKey(defaultValue: false) @JsonKey(defaultValue: false)
final bool searxngApi; final bool searxngApi;
@@ -102,8 +111,16 @@ class Bang with FastEquatable implements Insertable<Bang> {
} }
Uri getTemplateUrl(String? query) { Uri getTemplateUrl(String? query) {
final url = (query != null) final queryEmpty = query.isEmpty;
? urlTemplate.replaceAll(_templateQueryPlaceholder, formatQuery(query))
if (queryEmpty && format?.contains(BangFormat.openSnapDomain) == true) {
if (snapDomain.isNotEmpty) {
return Uri.parse(snapDomain!);
}
}
final url = (!queryEmpty)
? urlTemplate.replaceAll(_templateQueryPlaceholder, formatQuery(query!))
: urlTemplate; : urlTemplate;
var template = Uri.parse(url); var template = Uri.parse(url);
@@ -113,8 +130,8 @@ class Bang with FastEquatable implements Insertable<Bang> {
).replace(path: template.path, query: template.query); ).replace(path: template.path, query: template.query);
} }
if (format?.contains(BangFormat.openBasePath) == true) { if (queryEmpty && format?.contains(BangFormat.openBasePath) == true) {
return template.base; template = template.base;
} }
return template; return template;
@@ -139,6 +156,7 @@ class Bang with FastEquatable implements Insertable<Bang> {
this.subCategory, this.subCategory,
this.format, this.format,
this.additionalTriggers, this.additionalTriggers,
this.snapDomain,
}); });
factory Bang.fromJson(Map<String, dynamic> json) => _$BangFromJson(json); factory Bang.fromJson(Map<String, dynamic> json) => _$BangFromJson(json);
@@ -156,6 +174,7 @@ class Bang with FastEquatable implements Insertable<Bang> {
subCategory, subCategory,
format, format,
additionalTriggers, additionalTriggers,
snapDomain,
searxngApi, searxngApi,
]; ];
@@ -171,6 +190,7 @@ class Bang with FastEquatable implements Insertable<Bang> {
subCategory: Value.absentIfNull(subCategory), subCategory: Value.absentIfNull(subCategory),
format: Value.absentIfNull(format), format: Value.absentIfNull(format),
additionalTriggers: Value.absentIfNull(additionalTriggers), additionalTriggers: Value.absentIfNull(additionalTriggers),
snapDomain: Value.absentIfNull(snapDomain),
).toColumns(nullToAbsent); ).toColumns(nullToAbsent);
} }
} }
@@ -27,6 +27,8 @@ abstract class _$BangCWProxy {
Bang additionalTriggers(Set<String>? additionalTriggers); Bang additionalTriggers(Set<String>? additionalTriggers);
Bang snapDomain(String? snapDomain);
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `Bang(...).copyWith.fieldName(value)`. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `Bang(...).copyWith.fieldName(value)`.
/// ///
@@ -45,6 +47,7 @@ abstract class _$BangCWProxy {
String? subCategory, String? subCategory,
Set<BangFormat>? format, Set<BangFormat>? format,
Set<String>? additionalTriggers, Set<String>? additionalTriggers,
String? snapDomain,
}); });
} }
@@ -86,6 +89,9 @@ class _$BangCWProxyImpl implements _$BangCWProxy {
Bang additionalTriggers(Set<String>? additionalTriggers) => Bang additionalTriggers(Set<String>? additionalTriggers) =>
call(additionalTriggers: additionalTriggers); call(additionalTriggers: additionalTriggers);
@override
Bang snapDomain(String? snapDomain) => call(snapDomain: snapDomain);
@override @override
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `Bang(...).copyWith.fieldName(value)`. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `Bang(...).copyWith.fieldName(value)`.
@@ -105,6 +111,7 @@ class _$BangCWProxyImpl implements _$BangCWProxy {
Object? subCategory = const $CopyWithPlaceholder(), Object? subCategory = const $CopyWithPlaceholder(),
Object? format = const $CopyWithPlaceholder(), Object? format = const $CopyWithPlaceholder(),
Object? additionalTriggers = const $CopyWithPlaceholder(), Object? additionalTriggers = const $CopyWithPlaceholder(),
Object? snapDomain = const $CopyWithPlaceholder(),
}) { }) {
return Bang( return Bang(
websiteName: websiteName:
@@ -150,6 +157,10 @@ class _$BangCWProxyImpl implements _$BangCWProxy {
? _value.additionalTriggers ? _value.additionalTriggers
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: additionalTriggers as Set<String>?, : additionalTriggers as Set<String>?,
snapDomain: snapDomain == const $CopyWithPlaceholder()
? _value.snapDomain
// ignore: cast_nullable_to_non_nullable
: snapDomain as String?,
); );
} }
} }
@@ -179,6 +190,7 @@ Bang _$BangFromJson(Map<String, dynamic> json) => Bang(
additionalTriggers: (json['ts'] as List<dynamic>?) additionalTriggers: (json['ts'] as List<dynamic>?)
?.map((e) => e as String) ?.map((e) => e as String)
.toSet(), .toSet(),
snapDomain: json['ad'] as String?,
); );
Map<String, dynamic> _$BangToJson(Bang instance) => <String, dynamic>{ Map<String, dynamic> _$BangToJson(Bang instance) => <String, dynamic>{
@@ -190,6 +202,7 @@ Map<String, dynamic> _$BangToJson(Bang instance) => <String, dynamic>{
'sc': instance.subCategory, 'sc': instance.subCategory,
'fmt': instance.format?.map((e) => _$BangFormatEnumMap[e]!).toList(), 'fmt': instance.format?.map((e) => _$BangFormatEnumMap[e]!).toList(),
'ts': instance.additionalTriggers?.toList(), 'ts': instance.additionalTriggers?.toList(),
'ad': instance.snapDomain,
'searxngApi': instance.searxngApi, 'searxngApi': instance.searxngApi,
}; };
@@ -197,4 +210,5 @@ const _$BangFormatEnumMap = {
BangFormat.openBasePath: 'open_base_path', BangFormat.openBasePath: 'open_base_path',
BangFormat.urlEncodePlaceholder: 'url_encode_placeholder', BangFormat.urlEncodePlaceholder: 'url_encode_placeholder',
BangFormat.urlEncodeSpaceToPlus: 'url_encode_space_to_plus', BangFormat.urlEncodeSpaceToPlus: 'url_encode_space_to_plus',
BangFormat.openSnapDomain: 'open_snap_domain',
}; };
@@ -46,6 +46,7 @@ class BangData extends Bang {
super.subCategory, super.subCategory,
super.format, super.format,
super.additionalTriggers, super.additionalTriggers,
super.snapDomain,
int? frequency, int? frequency,
this.lastUsed, this.lastUsed,
this.icon, this.icon,
@@ -62,6 +63,7 @@ class BangData extends Bang {
super.subCategory, super.subCategory,
super.format, super.format,
super.additionalTriggers, super.additionalTriggers,
super.snapDomain,
int? frequency, int? frequency,
this.lastUsed, this.lastUsed,
this.icon, this.icon,
@@ -25,6 +25,8 @@ abstract class _$BangDataCWProxy {
BangData additionalTriggers(Set<String>? additionalTriggers); BangData additionalTriggers(Set<String>? additionalTriggers);
BangData snapDomain(String? snapDomain);
BangData frequency(int? frequency); BangData frequency(int? frequency);
BangData lastUsed(DateTime? lastUsed); BangData lastUsed(DateTime? lastUsed);
@@ -48,6 +50,7 @@ abstract class _$BangDataCWProxy {
String? subCategory, String? subCategory,
Set<BangFormat>? format, Set<BangFormat>? format,
Set<String>? additionalTriggers, Set<String>? additionalTriggers,
String? snapDomain,
int? frequency, int? frequency,
DateTime? lastUsed, DateTime? lastUsed,
BrowserIcon? icon, BrowserIcon? icon,
@@ -89,6 +92,9 @@ class _$BangDataCWProxyImpl implements _$BangDataCWProxy {
BangData additionalTriggers(Set<String>? additionalTriggers) => BangData additionalTriggers(Set<String>? additionalTriggers) =>
call(additionalTriggers: additionalTriggers); call(additionalTriggers: additionalTriggers);
@override
BangData snapDomain(String? snapDomain) => call(snapDomain: snapDomain);
@override @override
BangData frequency(int? frequency) => call(frequency: frequency); BangData frequency(int? frequency) => call(frequency: frequency);
@@ -116,6 +122,7 @@ class _$BangDataCWProxyImpl implements _$BangDataCWProxy {
Object? subCategory = const $CopyWithPlaceholder(), Object? subCategory = const $CopyWithPlaceholder(),
Object? format = const $CopyWithPlaceholder(), Object? format = const $CopyWithPlaceholder(),
Object? additionalTriggers = const $CopyWithPlaceholder(), Object? additionalTriggers = const $CopyWithPlaceholder(),
Object? snapDomain = const $CopyWithPlaceholder(),
Object? frequency = const $CopyWithPlaceholder(), Object? frequency = const $CopyWithPlaceholder(),
Object? lastUsed = const $CopyWithPlaceholder(), Object? lastUsed = const $CopyWithPlaceholder(),
Object? icon = const $CopyWithPlaceholder(), Object? icon = const $CopyWithPlaceholder(),
@@ -160,6 +167,10 @@ class _$BangDataCWProxyImpl implements _$BangDataCWProxy {
? _value.additionalTriggers ? _value.additionalTriggers
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: additionalTriggers as Set<String>?, : additionalTriggers as Set<String>?,
snapDomain: snapDomain == const $CopyWithPlaceholder()
? _value.snapDomain
// ignore: cast_nullable_to_non_nullable
: snapDomain as String?,
frequency: frequency == const $CopyWithPlaceholder() frequency: frequency == const $CopyWithPlaceholder()
? _value.frequency ? _value.frequency
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
@@ -23,11 +23,6 @@ enum BangGroup {
'https://raw.githubusercontent.com/FaFre/bangs/main/data/bangs.json', 'https://raw.githubusercontent.com/FaFre/bangs/main/data/bangs.json',
bundled: 'assets/bangs/bangs.json', bundled: 'assets/bangs/bangs.json',
), ),
assistant(
remote:
'https://raw.githubusercontent.com/FaFre/bangs/main/data/assistant_bangs.json',
bundled: 'assets/bangs/assistant_bangs.json',
),
kagi( kagi(
remote: remote:
'https://raw.githubusercontent.com/FaFre/bangs/main/data/kagi_bangs.json', 'https://raw.githubusercontent.com/FaFre/bangs/main/data/kagi_bangs.json',
@@ -46,7 +46,13 @@ class BangKey {
static BangKey? tryFromString(String key) { static BangKey? tryFromString(String key) {
try { try {
final [group, trigger] = key.split('::'); var [group, trigger] = key.split('::');
//Migrate to schema v5
if (group == 'assistant') {
group = BangGroup.kagi.name;
}
return BangKey( return BangKey(
group: BangGroup.values.firstWhere((g) => g.name == group), group: BangGroup.values.firstWhere((g) => g.name == group),
trigger: trigger, trigger: trigger,
@@ -107,6 +107,7 @@ class EditBangScreen extends HookConsumerWidget {
category: category.value, category: category.value,
subCategory: subCategory.value, subCategory: subCategory.value,
additionalTriggers: initialBang?.additionalTriggers, additionalTriggers: initialBang?.additionalTriggers,
snapDomain: initialBang?.snapDomain,
format: formatFlags.value.isNotEmpty format: formatFlags.value.isNotEmpty
? formatFlags.value ? formatFlags.value
: null, : null,
@@ -59,11 +59,6 @@ class BangSettingsScreen extends HookConsumerWidget {
title: 'General Bangs', title: 'General Bangs',
subtitle: 'Sync on demand from GitHub', subtitle: 'Sync on demand from GitHub',
), ),
const BangGroupListTile(
group: BangGroup.assistant,
title: 'Assistant Bangs',
subtitle: 'Sync on-demand from GitHub',
),
const BangGroupListTile( const BangGroupListTile(
group: BangGroup.kagi, group: BangGroup.kagi,
title: 'Kagi Bangs', title: 'Kagi Bangs',
+4 -1
View File
@@ -8,6 +8,7 @@ import 'schema_v1.dart' as v1;
import 'schema_v2.dart' as v2; import 'schema_v2.dart' as v2;
import 'schema_v3.dart' as v3; import 'schema_v3.dart' as v3;
import 'schema_v4.dart' as v4; import 'schema_v4.dart' as v4;
import 'schema_v5.dart' as v5;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@@ -21,10 +22,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v3.DatabaseAtV3(db); return v3.DatabaseAtV3(db);
case 4: case 4:
return v4.DatabaseAtV4(db); return v4.DatabaseAtV4(db);
case 5:
return v5.DatabaseAtV5(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [1, 2, 3, 4]; static const versions = const [1, 2, 3, 4, 5];
} }
File diff suppressed because it is too large Load Diff
-2
View File
@@ -60,8 +60,6 @@ update_bangs() {
fetch "https://raw.githubusercontent.com/FaFre/bangs/main/data/bangs.json" \ fetch "https://raw.githubusercontent.com/FaFre/bangs/main/data/bangs.json" \
"$dir/bangs.json" "$dir/bangs.json"
fetch "https://raw.githubusercontent.com/FaFre/bangs/main/data/assistant_bangs.json" \
"$dir/assistant_bangs.json"
fetch "https://raw.githubusercontent.com/FaFre/bangs/main/data/kagi_bangs.json" \ fetch "https://raw.githubusercontent.com/FaFre/bangs/main/data/kagi_bangs.json" \
"$dir/kagi_bangs.json" "$dir/kagi_bangs.json"
fetch "https://raw.githubusercontent.com/FaFre/bangs/refs/heads/custom/data/custom.json" \ fetch "https://raw.githubusercontent.com/FaFre/bangs/refs/heads/custom/data/custom.json" \