diff --git a/package.json b/package.json index 60ca0271..42bbf55a 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "eslint": "^9.8.0", "eslint-plugin-format": "^0.1.2", "rollup-plugin-cleanup": "^3.2.1", - "telegram-bot-api-types": "^7.9.1", + "telegram-bot-api-types": "^7.9.2", "typescript": "^5.5.4", "vite": "^5.2.10", "vite-plugin-checker": "^0.7.2", diff --git a/src/route/route.ts b/src/route/route.ts index 826596c0..2cab1310 100644 --- a/src/route/route.ts +++ b/src/route/route.ts @@ -4,7 +4,7 @@ import { commandsBindScope, commandsDocument } from '../telegram/command'; import type { RouterRequest } from '../utils/router'; import { Router } from '../utils/router'; import type { Telegram } from '../types/telegram'; -import { TelegramBotAPI } from '../telegram/api/api'; +import { createTelegramBotAPI } from '../telegram/api/api'; import { errorToString, makeResponse200, renderHTML } from './utils'; const helpLink = 'https://github.com/TBXark/ChatGPT-Telegram-Workers/blob/master/doc/en/DEPLOY.md'; @@ -23,7 +23,7 @@ async function bindWebHookAction(request: RouterRequest): Promise { const hookMode = API_GUARD ? 'safehook' : 'webhook'; const scope = commandsBindScope(); for (const token of ENV.TELEGRAM_AVAILABLE_TOKENS) { - const api = new TelegramBotAPI(token); + const api = createTelegramBotAPI(token); const url = `https://${domain}/telegram/${token.trim()}/${hookMode}`; const id = token.split(':')[0]; result[id] = {}; diff --git a/src/telegram/api/api.ts b/src/telegram/api/api.ts index 481b4e97..7494355c 100644 --- a/src/telegram/api/api.ts +++ b/src/telegram/api/api.ts @@ -1,20 +1,8 @@ import type { Telegram } from '../../types/telegram'; -export class TelegramBotAPI implements - Telegram.SendMessageRequest, - Telegram.EditMessageTextRequest, - Telegram.SendPhotoRequest, - Telegram.SendChatActionRequest, - Telegram.SetWebhookRequest, - Telegram.DeleteWebhookRequest, - Telegram.SetMyCommandsRequest, - Telegram.GetMeRequest, - Telegram.GetFileRequest, - Telegram.GetChatAdministratorsRequest, - Telegram.GetUpdatesRequest { +class APIClientBase { readonly token: string; readonly baseURL: string = `https://api.telegram.org/`; - constructor(token: string, baseURL?: string) { this.token = token; if (baseURL) { @@ -22,11 +10,7 @@ export class TelegramBotAPI implements } } - static from(token: string, baseURL?: string): TelegramBotAPI { - return new TelegramBotAPI(token, baseURL); - } - - jsonRequest(method: Telegram.TelegramBotMethod, params: T): Promise { + private jsonRequest(method: Telegram.BotMethod, params: T): Promise { return fetch(`${this.baseURL}bot${this.token}/${method}`, { method: 'POST', headers: { @@ -36,7 +20,7 @@ export class TelegramBotAPI implements }); } - formDataRequest(method: Telegram.TelegramBotMethod, params: T): Promise { + private formDataRequest(method: Telegram.BotMethod, params: T): Promise { const formData = new FormData(); for (const key in params) { const value = params[key]; @@ -56,47 +40,39 @@ export class TelegramBotAPI implements }); } - sendMessage(params: Telegram.SendMessageParams): Promise { - return this.jsonRequest('sendMessage', params); - } - - editMessageText(params: Telegram.EditMessageTextParams): Promise { - return this.jsonRequest('editMessageText', params); - } - - sendPhoto(params: Telegram.SendPhotoParams): Promise { - return this.formDataRequest('sendPhoto', params); - } - - sendChatAction(params: Telegram.SendChatActionParams): Promise { - return this.jsonRequest('sendChatAction', params); - } - - setWebhook(params: Telegram.SetWebhookParams): Promise { - return this.jsonRequest('setWebhook', params); - } - - deleteWebhook(): Promise { - return this.jsonRequest('deleteWebhook', {}); - } - - setMyCommands(params: Telegram.SetMyCommandsParams): Promise { - return this.jsonRequest('setMyCommands', params); - } - - getMe(): Promise { - return this.jsonRequest('getMe', {}); - } - - getFile(params: Telegram.GetFileParams): Promise { - return this.jsonRequest('getFile', params); - } - - getChatAdministrators(params: Telegram.GetChatAdministratorsParams): Promise { - return this.jsonRequest('getChatAdministrators', params); + request(method: Telegram.BotMethod, params: T): Promise { + for (const key in params) { + if (params[key] instanceof File || params[key] instanceof Blob) { + return this.formDataRequest(method, params); + } + } + return this.jsonRequest(method, params); } +} - getUpdates(params: Telegram.GetUpdatesParams): Promise { - return this.jsonRequest('getUpdates', params); - } +export type TelegramBotAPI = APIClientBase & + Telegram.SendMessageRequest & + Telegram.EditMessageTextRequest & + Telegram.SendPhotoRequest & + Telegram.SendChatActionRequest & + Telegram.SetWebhookRequest & + Telegram.DeleteWebhookRequest & + Telegram.SetMyCommandsRequest & + Telegram.GetMeRequest & + Telegram.GetFileRequest & + Telegram.GetChatAdministratorsRequest & + Telegram.GetUpdatesRequest; + +export function createTelegramBotAPI(token: string): TelegramBotAPI { + const client = new APIClientBase(token); + return new Proxy(client, { + get(target, prop, receiver) { + if (prop in target) { + return Reflect.get(target, prop, receiver); + } + return (...args: any[]) => { + return target.request(prop as Telegram.BotMethod, args[0]); + }; + }, + }) as TelegramBotAPI; } diff --git a/src/telegram/command/auth.ts b/src/telegram/command/auth.ts index 838a567c..b9679565 100644 --- a/src/telegram/command/auth.ts +++ b/src/telegram/command/auth.ts @@ -1,6 +1,6 @@ import type { WorkerContext } from '../../config/context'; import { DATABASE } from '../../config/env'; -import { TelegramBotAPI } from '../api/api'; +import { createTelegramBotAPI } from '../api/api'; import type { Telegram, TelegramAPISuccess } from '../../types/telegram'; export async function loadChatRoleWithContext(context: WorkerContext): Promise { @@ -22,7 +22,7 @@ export async function loadChatRoleWithContext(context: WorkerContext): Promise res.json()).catch(() => null) as TelegramAPISuccess; + const result = await createTelegramBotAPI(token).getChatAdministrators({ chat_id: chatId }).then(res => res.json()).catch(() => null) as TelegramAPISuccess; if (result == null) { return null; } diff --git a/src/telegram/command/system.ts b/src/telegram/command/system.ts index d97c12bd..6a1bea34 100644 --- a/src/telegram/command/system.ts +++ b/src/telegram/command/system.ts @@ -16,7 +16,7 @@ import { isTelegramChatTypeGroup } from '../utils/utils'; import type { HistoryItem, HistoryModifierResult } from '../../agent/types'; import { chatWithLLM } from '../handler/chat'; import { loadChatLLM, loadImageGen } from '../../agent/agents'; -import { TelegramBotAPI } from '../api/api'; +import { createTelegramBotAPI } from '../api/api'; import type { CommandHandler } from './type'; export const COMMAND_AUTH_CHECKER = { @@ -47,7 +47,7 @@ export class ImgCommandHandler implements CommandHandler { return sendMessageToTelegramWithContext(context)(ENV.I18N.command.help.img); } try { - const api = TelegramBotAPI.from(context.SHARE_CONTEXT.currentBotToken); + const api = createTelegramBotAPI(context.SHARE_CONTEXT.currentBotToken); const agent = loadImageGen(context.USER_CONFIG); if (!agent) { return sendMessageToTelegramWithContext(context)('ERROR: Image generator not found'); @@ -115,7 +115,7 @@ class BaseNewCommandHandler { selective: true, }; } - return TelegramBotAPI.from(context.SHARE_CONTEXT.currentBotToken).sendMessage(params); + return createTelegramBotAPI(context.SHARE_CONTEXT.currentBotToken).sendMessage(params); } } diff --git a/src/telegram/handler/chat.ts b/src/telegram/handler/chat.ts index 7549d8af..e2aecf8d 100644 --- a/src/telegram/handler/chat.ts +++ b/src/telegram/handler/chat.ts @@ -7,7 +7,7 @@ import type { WorkerContext } from '../../config/context'; import { sendMessageToTelegramWithContext } from '../utils/send'; import type { Telegram, TelegramAPISuccess } from '../../types/telegram'; import { uploadImageToTelegraph } from '../../utils/image'; -import { TelegramBotAPI } from '../api/api'; +import { createTelegramBotAPI } from '../api/api'; import type { MessageHandler } from './type'; export async function chatWithLLM(params: LLMChatRequestParams, context: WorkerContext, modifier: HistoryModifier | null): Promise { @@ -18,7 +18,7 @@ export async function chatWithLLM(params: LLMChatRequestParams, context: WorkerC } catch (e) { console.error(e); } - const api = TelegramBotAPI.from(context.SHARE_CONTEXT.currentBotToken); + const api = createTelegramBotAPI(context.SHARE_CONTEXT.currentBotToken); setTimeout(() => api.sendChatAction({ chat_id: context.CURRENT_CHAT_CONTEXT.chat_id, action: 'typing', @@ -100,7 +100,7 @@ export class ChatHandler implements MessageHandler { if (message.photo && message.photo.length > 0) { const id = findPhotoFileID(message.photo, ENV.TELEGRAM_PHOTO_SIZE_OFFSET); - const api = TelegramBotAPI.from(context.SHARE_CONTEXT.currentBotToken); + const api = createTelegramBotAPI(context.SHARE_CONTEXT.currentBotToken); const file = await api.getFile({ file_id: id }).then(res => res.json()) as TelegramAPISuccess; let url = file.result.file_path; if (url) { diff --git a/src/telegram/handler/group.ts b/src/telegram/handler/group.ts index 291a3f13..9738031d 100644 --- a/src/telegram/handler/group.ts +++ b/src/telegram/handler/group.ts @@ -3,7 +3,7 @@ import type { Telegram, TelegramAPISuccess } from '../../types/telegram'; import type { WorkerContext } from '../../config/context'; import { isTelegramChatTypeGroup } from '../utils/utils'; import { ENV } from '../../config/env'; -import { TelegramBotAPI } from '../api/api'; +import { createTelegramBotAPI } from '../api/api'; import type { MessageHandler } from './type'; function checkMention(content: string, entities: Telegram.MessageEntity[], botName: string, botId: number): { @@ -62,7 +62,7 @@ export class GroupMention implements MessageHandler { // 处理群组消息,过滤掉AT部分 let botName = context.SHARE_CONTEXT.currentBotName; if (!botName) { - const res = await TelegramBotAPI.from(context.SHARE_CONTEXT.currentBotToken).getMe().then(res => res.json()) as TelegramAPISuccess; + const res = await createTelegramBotAPI(context.SHARE_CONTEXT.currentBotToken).getMe().then(res => res.json()) as TelegramAPISuccess; botName = res.result.username || null; context.SHARE_CONTEXT.currentBotName = botName; } diff --git a/src/telegram/utils/send.ts b/src/telegram/utils/send.ts index 54f10f9e..0a8b002d 100644 --- a/src/telegram/utils/send.ts +++ b/src/telegram/utils/send.ts @@ -1,6 +1,7 @@ import type { CurrentChatContext, WorkerContext } from '../../config/context'; import type { Telegram } from '../../types/telegram'; -import { TelegramBotAPI } from '../api/api'; +import type { TelegramBotAPI } from '../api/api'; +import { createTelegramBotAPI } from '../api/api'; async function sendMessage(api: TelegramBotAPI, message: string, token: string, context: CurrentChatContext): Promise { if (context?.message_id) { @@ -42,7 +43,7 @@ async function sendLongMessage(message: string, token: string, context: CurrentC const chatContext = context; const originMessage = message; const limit = 4096; - const api = TelegramBotAPI.from(token); + const api = createTelegramBotAPI(token); if (message.length <= limit) { const resp = await sendMessage(api, message, token, chatContext); @@ -83,7 +84,7 @@ export function sendMessageToTelegramWithContext(context: WorkerContext): (messa export function sendPhotoToTelegramWithContext(context: WorkerContext): (photo: string | Blob) => Promise { return async (photo) => { - const api = TelegramBotAPI.from(context.SHARE_CONTEXT.currentBotToken); + const api = createTelegramBotAPI(context.SHARE_CONTEXT.currentBotToken); const chatContext = context.CURRENT_CHAT_CONTEXT; const params: Telegram.SendPhotoParams = { chat_id: chatContext.chat_id, diff --git a/yarn.lock b/yarn.lock index d7093952..fc45a36a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2867,10 +2867,10 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -telegram-bot-api-types@^7.9.1: - version "7.9.1" - resolved "https://registry.yarnpkg.com/telegram-bot-api-types/-/telegram-bot-api-types-7.9.1.tgz#2466ba6c3ff028a4b7a2ed3f9a21e86d35ba0a6a" - integrity sha512-aI1jKgB+zs3t9Mbt58tQNc3EGuafuQG/FwJIkBNgU5CYiBa2u4Ona9S6nQbP81isCMyiXvsam+sLpPoxEFULbg== +telegram-bot-api-types@^7.9.2: + version "7.9.2" + resolved "https://registry.yarnpkg.com/telegram-bot-api-types/-/telegram-bot-api-types-7.9.2.tgz#c2952ba4d947939695c404c7e567209eaff3c55d" + integrity sha512-k0VsiOUJwBJy1Lkr43g5QFzmBkiL7rj5BvKOMYe4m6zCHeyomGPoLJV1z1GaC5LeW3jIn6P+5A1+zXYaQX/i1g== text-table@^0.2.0: version "0.2.0"