A fast, local-first "redirection engine" for !bang users with a few extra features ^-^
1import rawBangs from "./bangs.json" with { type: "json" };
2
3// Developer script that converts ./bangs.json array to hashmap.
4// Only keeps fields needed at runtime: d (domain), ad (alt domain), s (name), u (url)
5
6type RuntimeBang = {
7 d: string;
8 ad?: string;
9 s: string;
10 u: string;
11};
12
13const hashbang: Record<string, RuntimeBang> = {
14 t3: {
15 d: "www.t3.chat",
16 s: "T3 Chat",
17 u: "https://www.t3.chat/new?q={{{s}}}",
18 },
19 m2: {
20 d: "meta.dunkirk.sh",
21 s: "metasearch2",
22 u: "https://meta.dunkirk.sh/search?q={{{s}}}",
23 },
24 tiktok: {
25 d: "www.tiktok.com",
26 s: "TikTok",
27 u: "https://www.tiktok.com/search?q={{{s}}}",
28 },
29 image: {
30 d: "duckduckgo.com",
31 s: "Duckduckgo images",
32 u: "https://duckduckgo.com/?q={{{s}}}&ia=images&iax=images&atb=v375-1",
33 },
34 k: {
35 d: "kagi.com",
36 s: "Kagi Search",
37 u: "https://kagi.com/search?q={{{s}}}",
38 },
39 kagi: {
40 d: "kagi.com",
41 s: "Kagi Search",
42 u: "https://kagi.com/search?q={{{s}}}",
43 },
44 ki: {
45 d: "kagi.com",
46 s: "Kagi Images",
47 u: "https://kagi.com/images?q={{{s}}}",
48 },
49 kagii: {
50 d: "kagi.com",
51 s: "Kagi Images",
52 u: "https://kagi.com/images?q={{{s}}}",
53 },
54 kv: {
55 d: "kagi.com",
56 s: "Kagi Videos",
57 u: "https://kagi.com/videos?q={{{s}}}",
58 },
59 kagiv: {
60 d: "kagi.com",
61 s: "Kagi Videos",
62 u: "https://kagi.com/videos?q={{{s}}}",
63 },
64 kn: {
65 d: "kagi.com",
66 s: "Kagi News",
67 u: "https://kagi.com/news?q={{{s}}}",
68 },
69 kagin: {
70 d: "kagi.com",
71 s: "Kagi News",
72 u: "https://kagi.com/news?q={{{s}}}",
73 },
74 km: {
75 d: "kagi.com",
76 s: "Kagi Maps",
77 u: "https://kagi.com/maps?q={{{s}}}",
78 },
79 kagim: {
80 d: "kagi.com",
81 s: "Kagi Maps",
82 u: "https://kagi.com/maps?q={{{s}}}",
83 },
84 kp: {
85 d: "kagi.com",
86 s: "Kagi Podcasts",
87 u: "https://kagi.com/podcasts?q={{{s}}}",
88 },
89 kagip: {
90 d: "kagi.com",
91 s: "Kagi Podcasts",
92 u: "https://kagi.com/podcasts?q={{{s}}}",
93 },
94 kf: {
95 d: "kagi.com",
96 s: "Kagi FastGPT",
97 u: "https://kagi.com/fastgpt?q={{{s}}}",
98 },
99 fastgpt: {
100 d: "kagi.com",
101 s: "Kagi FastGPT",
102 u: "https://kagi.com/fastgpt?q={{{s}}}",
103 },
104 ka: {
105 d: "kagi.com",
106 s: "Kagi Assistant",
107 u: "https://kagi.com/assistant?q={{{s}}}",
108 },
109 assistant: {
110 d: "kagi.com",
111 s: "Kagi Assistant",
112 u: "https://kagi.com/assistant?q={{{s}}}",
113 },
114};
115
116rawBangs.forEach((bang: any) => {
117 if (!bang.t || !bang.u || !bang.s || !bang.d) {
118 console.warn(`Skipping invalid bang: ${JSON.stringify(bang)}`);
119 return;
120 }
121
122 const entry: RuntimeBang = {
123 d: bang.d,
124 s: bang.s,
125 u: bang.u,
126 };
127 if (bang.ad) entry.ad = bang.ad;
128
129 hashbang[bang.t] = entry;
130
131 if (bang.ts) {
132 bang.ts.forEach((trigger: string) => {
133 hashbang[trigger] = entry;
134 });
135 }
136});
137
138Bun.write(
139 "./src/bangs/hashbang.ts",
140 `export const bangs: Record<string, { d: string; ad?: string; s: string; u: string }> = ${JSON.stringify(hashbang)};`,
141);