Skip to content

Instantly share code, notes, and snippets.

@surma
Last active October 16, 2023 19:02
Show Gist options
  • Save surma/5d0767ec15d85188cf9c9ed0a6f5e0a4 to your computer and use it in GitHub Desktop.
Save surma/5d0767ec15d85188cf9c9ed0a6f5e0a4 to your computer and use it in GitHub Desktop.
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export class TelegramBot {
constructor(token) {
this.token = token;
return new Proxy(this, {
get(target, methodName, receiver) {
if (methodName === "then")
return null;
if (target[methodName]) {
let v = target[methodName];
if (typeof v === "function")
v = v.bind(receiver);
return v;
}
return (payload, extraParams) => target.callMethod(methodName.toString(), payload, extraParams);
}
});
}
async downloadFile(file) {
if (typeof file === "string")
file = await this.getFile({ file_id: file });
const { file_path, file_id } = file;
if (!file_path)
throw Error(`Could not get file path for ${file_id}`);
const url = `https://api.telegram.org/file/bot${this.token}/${file_path}`;
const resp = await fetch(url);
if (!resp.ok)
throw Error(`Could not download file: ${await resp.text()}`);
return resp;
}
async callMethod(methodName, payload, extraParams = {}) {
const url = this.methodURL(methodName);
for (const [key, val] of Object.entries(extraParams)) {
url.searchParams.append(key, val);
}
let resp;
if (!payload) {
resp = await fetch(url);
} else {
if (typeof payload !== "string")
payload = JSON.stringify(payload);
resp = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: payload
});
}
if (!resp.ok)
throw Error(
`Method ${methodName} failed: ${await resp.text().catch(() => `Status code ${resp.statusText} (${resp.status})`)}`
);
const json = await resp.json();
if (!json.ok)
throw Error(
`Method ${methodName} failed: ${json.description ?? "<No description>"}`
);
return json.result;
}
methodURL(method) {
return new URL(`https://api.telegram.org/bot${this.token}/${method}`);
}
updateStream() {
let lastId = 0;
const that = this;
return new ReadableStream({
async pull(controller) {
try {
let updates;
while (true) {
updates = await that.getUpdates(null, {
offset: lastId + 1,
limit: 1
});
if (containsMessage(updates))
break;
await sleep(1e3);
}
for (const update of updates) {
lastId = update.update_id;
controller.enqueue(update);
}
} catch (e) {
console.error(e.message);
}
}
});
}
}
function containsMessage(updates) {
return (updates?.length ?? 0) > 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment