Skip to content

Commit 6ee53e7

Browse files
committed
refactor
1 parent 39e8ab9 commit 6ee53e7

File tree

25 files changed

+820
-631
lines changed

25 files changed

+820
-631
lines changed

docs/telegram-bot-playground/index.html

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,63 +5,40 @@
55
<title>Telegram Bot Playground</title>
66
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
77
<script defer src="https://cdn.jsdelivr.net/npm/@monaco-editor/[email protected]/lib/umd/monaco-loader.min.js"></script>
8-
<script defer src="./scripts/init.js?v=60sdgc" type="module"></script>
9-
<script defer src="./scripts/main.js?v=bgip" type="module"></script>
10-
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
118

129
<link rel="stylesheet" href="styles.css?v=oq5lin">
1310

1411
<link rel="icon" type="image/png" sizes="32x32" href="./favicon.ico">
1512

16-
<script>
17-
document.addEventListener('playgroundInitialized', async () => {
18-
await playground.start();
19-
playground.onCodeChange(() => {
20-
playground.runBot(botState);
21-
});
22-
playground.worker.onmessage = (e) => {
23-
console.log('got message from worker', e?.data);
24-
if (!e?.data) return;
25-
if (e.data.botState) {
26-
Object.assign(botState, e.data.botState)
27-
}
28-
botUpdates.push(e.data);
29-
console.log('Scrolling', $refs.updates.scrollHeight);
30-
$nextTick(() => { $refs.updates.scrollTop = $refs.updates.scrollHeight })
31-
};
32-
})
33-
</script>
13+
<script defer src="./scripts/_main.js?v=e7gy8f" type="module"></script>
14+
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
15+
3416
</head>
3517

36-
<body x-data="{
37-
selected_example: 'empty.ts',
38-
botUpdates: [],
39-
botState: {
40-
name: 'nameless',
41-
status: 'idle',
42-
token: ''
43-
}
44-
}">
18+
<body x-data="state">
4519

4620
<h1>Telegram Bot Playground 🤖 </h1>
4721

4822
<div style="display: flex; gap: 20px; justify-content: center;">
4923

5024
<label>
5125
Bot token
52-
<input type="text" x-model="botState.token" placeholder="your token"
53-
@input.debounce.500ms="playground.checkToken(botState)">
26+
<input type="text" x-model="$store.state.bot.token" placeholder="your token"
27+
@input.debounce.500ms="$dispatch('check-token')">
5428
</label>
5529

56-
<label>Bot name: <span x-text="botState.name"></span></label>
57-
<label>Status: <span x-text="botState.status"></span></label>
30+
<label>Bot name: <span x-text="$store.state.bot.name"></span></label>
31+
<label>Status: <span x-text="$store.state.bot.status"></span></label>
5832

5933
</div>
6034

6135
<div style="display: flex; align-items: start;">
6236
<label>
6337
Select example
64-
<select x-model="selected_example" @change="playground.loadExample(selected_example)">
38+
<select
39+
x-model="$store.state.selectedExample"
40+
@change="$dispatch('change-example')"
41+
>
6542
<option value="empty.ts">Empty</option>
6643
<option value="command.ts">Command Bot</option>
6744
<option value="file.ts">File Bot</option>
@@ -73,7 +50,7 @@ <h1>Telegram Bot Playground 🤖 </h1>
7350
<div class="flex-container">
7451
<div id="container" x-cloak></div>
7552
<div id="worker-updates" x-ref="updates">
76-
<template x-for="update in botUpdates" :key="update.id">
53+
<template x-for="update in $store.state.botUpdates" :key="update.messageId">
7754
<div x-text="JSON.stringify(update, undefined, 2)"></div>
7855
</template>
7956
</div>
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"styles.css": "oq5lin",
3-
"init.js": "60sdgc",
4-
"main.js": "bgip",
5-
"web-worker.js": "7hqmn5"
3+
"_main.js": "e7gy8f",
4+
"web-worker.js": "f5lxb1"
65
}
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
// src/tg-bot-playground/bot-launcher/load.ts
2+
async function loadWorker() {
3+
const version = await fetch("./metadata.json", { cache: "no-cache" }).then((_) => _.json()).then((_) => _["web-worker.js"]);
4+
if (!version) {
5+
console.warn("Cannot get version from metadata");
6+
return;
7+
}
8+
;
9+
const worker = new Worker(`./scripts/worker/web-worker.js?v=${version}`, { type: "module" });
10+
console.log("web worker has been loaded");
11+
return worker;
12+
}
13+
14+
// src/tg-bot-playground/bot-launcher/run.ts
15+
var makeRunnableBot = (tsTextModel, worker) => (state) => tsTextModel.getJsCode().then((code) => {
16+
if (!state.bot.token || state.bot.token.length < 10) return;
17+
if (!code.serialized) {
18+
console.warn("Serizalized js code not defined");
19+
return;
20+
}
21+
worker.postMessage({
22+
command: "run-bot",
23+
token: state.bot.token,
24+
code: code.serialized
25+
});
26+
state.bot.status = "active";
27+
}).catch((error) => {
28+
console.warn("cannot run bot", error);
29+
});
30+
var checkTokenAndRun = (state, runnableBot) => {
31+
const token = state.bot.token;
32+
if (!token) return;
33+
fetch(`https://api.telegram.org/bot${token}/getMe`).then((_) => _.json()).then((info) => {
34+
if (info.ok) {
35+
state.bot.name = info.result.first_name;
36+
console.log("Running bot");
37+
runnableBot(state);
38+
} else {
39+
state.bot.name = "nameless";
40+
}
41+
}).catch((error) => {
42+
console.warn("check token error", error);
43+
});
44+
};
45+
46+
// src/tg-bot-playground/bot-launcher/_main.ts
47+
var makeBotLauncher = async (tsTextModel) => {
48+
const worker = await loadWorker();
49+
if (!worker) return;
50+
const runnableBot = makeRunnableBot(tsTextModel, worker);
51+
return {
52+
worker,
53+
runBot: (state) => runnableBot(state),
54+
checkTokenAndRun: (state) => checkTokenAndRun(state, runnableBot)
55+
};
56+
};
57+
58+
// src/tg-bot-playground/utils.ts
59+
var fetchText = (path) => fetch(path).then((_) => _.text());
60+
var getMonacoLoader = () => {
61+
if (!("monaco_loader" in window) || typeof window.monaco_loader != "object" || window.monaco_loader == null) {
62+
console.warn("monaco loader is not available");
63+
return;
64+
}
65+
return window.monaco_loader;
66+
};
67+
function getAlpine() {
68+
if (!("Alpine" in window) || typeof window.Alpine != "object" || window.Alpine == null) {
69+
console.warn("Alpine is not available");
70+
return;
71+
}
72+
return window.Alpine;
73+
}
74+
75+
// src/tg-bot-playground/editor/setup.ts
76+
var setupDts = async (monaco) => {
77+
const dts = await fetchText("https://cdn.jsdelivr.net/npm/@effect-ak/[email protected]/dist/index.d.ts");
78+
monaco.languages.typescript.typescriptDefaults.setExtraLibs([
79+
{
80+
content: dts,
81+
filePath: "node_modules/@effect-ak/tg-bot-client/index.d.ts"
82+
}
83+
]);
84+
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
85+
...monaco.languages.typescript.typescriptDefaults.getCompilerOptions(),
86+
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
87+
typeRoots: [
88+
"node_modules"
89+
],
90+
strict: true
91+
});
92+
};
93+
94+
// src/tg-bot-playground/editor/ts-text-model.ts
95+
var makeTsTextModel = async (monaco) => {
96+
const emptyExample = await fetchText("./example/empty.ts");
97+
const tsModel = monaco.editor.createModel(emptyExample, "typescript");
98+
let cachedWorkerPromise = null;
99+
const getTsCode = async () => {
100+
if (!cachedWorkerPromise) {
101+
cachedWorkerPromise = (async () => {
102+
const tsWorker = await monaco.languages.typescript.getTypeScriptWorker();
103+
return tsWorker(tsModel.uri);
104+
})();
105+
}
106+
return cachedWorkerPromise.then((_) => _.getEmitOutput(tsModel.uri.toString()));
107+
};
108+
return {
109+
tsModel,
110+
getJsCode: async () => {
111+
const output = await getTsCode();
112+
const code = output.outputFiles[0].text;
113+
const defaultExport = await getDefaultExport(code);
114+
return {
115+
defaultExport,
116+
serialized: serialize(defaultExport?.default)
117+
};
118+
}
119+
};
120+
};
121+
async function getDefaultExport(code) {
122+
try {
123+
const encodedCode = encodeURIComponent(code);
124+
const module = await import(`data:text/javascript,${encodedCode}`);
125+
const result = module.default;
126+
return { default: result };
127+
} catch (e) {
128+
console.warn("get default error", e);
129+
return void 0;
130+
}
131+
}
132+
var serialize = (input) => {
133+
if (typeof input != "object" || !input) {
134+
return void 0;
135+
}
136+
const result = [];
137+
for (const [key, value] of Object.entries(input)) {
138+
if (typeof value != "function") {
139+
continue;
140+
}
141+
result.push([key, value.toString()]);
142+
}
143+
return JSON.stringify(Object.fromEntries(result));
144+
};
145+
146+
// src/tg-bot-playground/editor/init.ts
147+
var initEditor = async (loader) => {
148+
const container = document.getElementById("container");
149+
if (!container) {
150+
console.warn("container not found");
151+
return;
152+
}
153+
loader.config({
154+
paths: {
155+
vs: "https://cdn.jsdelivr.net/npm/monaco-editor@latest/min/vs"
156+
}
157+
});
158+
const monaco = await loader.init();
159+
const tsTextModel = await makeTsTextModel(monaco);
160+
const editor = monaco.editor.create(container, {
161+
model: tsTextModel.tsModel,
162+
contextmenu: false,
163+
minimap: {
164+
enabled: false
165+
}
166+
});
167+
return {
168+
tsTextModel,
169+
editor,
170+
monaco
171+
};
172+
};
173+
174+
// src/tg-bot-playground/editor/_editor.ts
175+
var makeEditor = async (state) => {
176+
const loader = getMonacoLoader();
177+
if (!loader) return;
178+
const editor = await initEditor(loader);
179+
if (!editor) return;
180+
await setupDts(editor.monaco);
181+
return {
182+
tsTextModel: editor.tsTextModel,
183+
loadExample: () => {
184+
if (!state.selectedExample) return;
185+
fetchText(`./example/${state.selectedExample}`).then((_) => editor.tsTextModel.tsModel.setValue(_));
186+
},
187+
onCodeChange: (f) => {
188+
let timeoutId;
189+
const debounceDelay = 1e3;
190+
editor.tsTextModel.tsModel.onDidChangeContent(() => {
191+
if (timeoutId !== void 0) {
192+
clearTimeout(timeoutId);
193+
}
194+
timeoutId = window.setTimeout(() => {
195+
const markers = editor.monaco.editor.getModelMarkers({
196+
resource: editor.tsTextModel.tsModel.uri
197+
});
198+
const hasError = markers.find((_) => _.severity.valueOf() == 8) != null;
199+
if (!hasError) {
200+
f();
201+
} else {
202+
console.debug("Code contains errors");
203+
}
204+
}, debounceDelay);
205+
});
206+
}
207+
};
208+
};
209+
210+
// src/tg-bot-playground/_main.ts
211+
var makeGlobalState = (alpine) => {
212+
const state = alpine.reactive({
213+
bot: {
214+
name: "nameless",
215+
status: "idle",
216+
token: ""
217+
},
218+
selectedExample: "empty.ts",
219+
botUpdates: []
220+
});
221+
return state;
222+
};
223+
document.addEventListener("alpine:init", async () => {
224+
const Alpine = getAlpine();
225+
if (!Alpine) return;
226+
Alpine.data("state", () => ({
227+
bot: {
228+
a: 1
229+
}
230+
}));
231+
const state = makeGlobalState(Alpine);
232+
Alpine.store("state", state);
233+
const editor = await makeEditor(state);
234+
if (!editor) return;
235+
const botLauncher = await makeBotLauncher(editor.tsTextModel);
236+
if (!botLauncher) return;
237+
editor.onCodeChange(() => {
238+
botLauncher.runBot(state);
239+
});
240+
document.addEventListener("check-token", () => {
241+
botLauncher.checkTokenAndRun(state);
242+
});
243+
document.addEventListener("change-example", () => {
244+
editor.loadExample();
245+
});
246+
botLauncher.worker.onmessage = (event) => {
247+
const data = event.data;
248+
console.log("got message from worker", data);
249+
if (!data) return;
250+
if (data.botState) {
251+
Object.assign(state.bot, data.botState);
252+
}
253+
state.botUpdates.push(data);
254+
};
255+
});
256+
export {
257+
makeGlobalState
258+
};

docs/telegram-bot-playground/scripts/init.js

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)