import { Api, TelegramClient } from 'telegram'; import { BotAuthParams, UserAuthParams } from 'telegram/client/auth'; import { NewMessage, NewMessageEvent, Raw } from 'telegram/events'; import { EditedMessage, EditedMessageEvent } from 'telegram/events/EditedMessage'; import { DeletedMessage, DeletedMessageEvent } from 'telegram/events/DeletedMessage'; import { EntityLike } from 'telegram/define'; import WaitForMessageHelper from '../helpers/WaitForMessageHelper'; import CallbackQueryHelper from '../helpers/CallbackQueryHelper'; import { CallbackQuery, CallbackQueryEvent } from 'telegram/events/CallbackQuery'; import os from 'os'; import TelegramChat from './TelegramChat'; import TelegramSession from '../models/TelegramSession'; import { LogLevel } from 'telegram/extensions/Logger'; import { BigInteger } from 'big-integer'; import { EditMessageParams, IterMessagesParams } from 'telegram/client/messages'; import { PromisedNetSockets, PromisedWebSockets } from 'telegram/extensions'; import { ConnectionTCPFull, ConnectionTCPObfuscated } from 'telegram/network'; import env from '../models/env'; type MessageHandler = (message: Api.Message) => Promise; type ServiceMessageHandler = (message: Api.MessageService) => Promise; export default class Telegram { private readonly client: TelegramClient; private waitForMessageHelper: WaitForMessageHelper; private callbackQueryHelper: CallbackQueryHelper = new CallbackQueryHelper(); private readonly onMessageHandlers: Array = []; private readonly onEditedMessageHandlers: Array = []; private readonly onServiceMessageHandlers: Array = []; public me: Api.User; private static existedBots = {} as { [id: number]: Telegram }; public get sessionId() { return (this.client.session as TelegramSession).dbId; } public get isOnline() { return this.client.connected; } private constructor(appName: string, sessionId?: number) { this.client = new TelegramClient( new TelegramSession(sessionId), env.TG_API_ID, env.TG_API_HASH, { connectionRetries: 20, langCode: 'zh', deviceModel: `${appName} On ${os.hostname()}`, appVersion: 'rainbowcat', useIPV6: !!env.IPV6, proxy: env.PROXY_IP ? { socksType: 5, ip: env.PROXY_IP, port: env.PROXY_PORT, username: env.PROXY_USERNAME, password: env.PROXY_PASSWORD, } : undefined, autoReconnect: true, networkSocket: env.TG_CONNECTION === 'websocket' ? PromisedWebSockets : PromisedNetSockets, connection: env.TG_CONNECTION === 'websocket' ? ConnectionTCPObfuscated : ConnectionTCPFull, }, ); // this.client.logger.setLevel(LogLevel.WARN); } public static async create(startArgs: UserAuthParams | BotAuthParams, appName = 'Q2TG') { const bot = new this(appName); await bot.client.start(startArgs); this.existedBots[bot.sessionId] = bot; await bot.config(); return bot; } public static async connect(sessionId: number, appName = 'Q2TG') { if (this.existedBots[sessionId]) { // 已经创建过就不会再次创建,可用于两个 instance 共享 user bot return this.existedBots[sessionId]; } const bot = new this(appName, sessionId); this.existedBots[sessionId] = bot; await bot.client.connect(); await bot.config(); return bot; } private async config() { this.client.setParseMode('html'); this.waitForMessageHelper = new WaitForMessageHelper(this); this.client.addEventHandler(this.onMessage, new NewMessage({})); this.client.addEventHandler(this.onEditedMessage, new EditedMessage({})); this.client.addEventHandler(this.onServiceMessage, new Raw({ types: [Api.UpdateNewMessage], func: (update: Api.UpdateNewMessage) => update.message instanceof Api.MessageService, })); this.client.addEventHandler(this.callbackQueryHelper.onCallbackQuery, new CallbackQuery()); this.me = await this.client.getMe() as Api.User; } private onMessage = async (event: NewMessageEvent) => { // 能用的东西基本都在 message 里面,直接调用 event 里的会 undefined for (const handler of this.onMessageHandlers) { const res = await handler(event.message); if (res) return; } }; private onEditedMessage = async (event: EditedMessageEvent) => { for (const handler of this.onEditedMessageHandlers) { const res = await handler(event.message); if (res) return; } }; private onServiceMessage = async (event: Api.UpdateNewMessage) => { for (const handler of this.onServiceMessageHandlers) { const res = await handler(event.message as Api.MessageService); if (res) return; } }; /** * 注册消息处理器 * @param handler 此方法返回 true 可以阻断下面的处理器 */ public addNewMessageEventHandler(handler: MessageHandler) { this.onMessageHandlers.push(handler); } public removeNewMessageEventHandler(handler: MessageHandler) { this.onMessageHandlers.includes(handler) && this.onMessageHandlers.splice(this.onMessageHandlers.indexOf(handler), 1); } public addEditedMessageEventHandler(handler: MessageHandler) { this.onEditedMessageHandlers.push(handler); } public removeEditedMessageEventHandler(handler: MessageHandler) { this.onEditedMessageHandlers.includes(handler) && this.onEditedMessageHandlers.splice(this.onEditedMessageHandlers.indexOf(handler), 1); } public addNewServiceMessageEventHandler(handler: ServiceMessageHandler) { this.onServiceMessageHandlers.push(handler); } public removeNewServiceMessageEventHandler(handler: ServiceMessageHandler) { this.onServiceMessageHandlers.includes(handler) && this.onServiceMessageHandlers.splice(this.onServiceMessageHandlers.indexOf(handler), 1); } public addDeletedMessageEventHandler(handler: (event: DeletedMessageEvent) => any) { this.client.addEventHandler(handler, new DeletedMessage({})); } public addChannelParticipantEventHandler(handler: (event: Api.UpdateChannelParticipant) => any) { this.client.addEventHandler(handler, new Raw({ types: [Api.UpdateChannelParticipant], })); } public async getChat(entity: EntityLike) { return new TelegramChat(this, this.client, await this.client.getEntity(entity), this.waitForMessageHelper); } public async setCommands(commands: Api.BotCommand[], scope: Api.TypeBotCommandScope) { return await this.client.invoke( new Api.bots.SetBotCommands({ commands, langCode: '', scope, }), ); } public registerCallback(cb: (event: CallbackQueryEvent) => any) { return this.callbackQueryHelper.registerCallback(cb); } public async getDialogFilters() { return await this.client.invoke(new Api.messages.GetDialogFilters()); } public async updateDialogFilter(params: Partial>) { return await this.client.invoke(new Api.messages.UpdateDialogFilter(params)); } public async createChat(title: string, about = '') { const updates = await this.client.invoke(new Api.channels.CreateChannel({ title, about, megagroup: true, forImport: true, })) as Api.Updates; const newChat = updates.chats[0]; return new TelegramChat(this, this.client, newChat, this.waitForMessageHelper); } public async getCustomEmoji(documentId: BigInteger) { const ids = await this.client.invoke(new Api.messages.GetCustomEmojiDocuments({ documentId: [documentId], })); const document = ids[0] as Api.Document; return await this.client.downloadFile(new Api.InputDocumentFileLocation({ id: document.id, accessHash: document.accessHash, fileReference: document.fileReference, thumbSize: '', }), { dcId: document.dcId, }); } public async getInputPeerUserFromMessage(chatId: EntityLike, userId: BigInteger, msgId: number) { const inputPeerOfChat = await this.client.getInputEntity(chatId); return new Api.InputUserFromMessage({ peer: inputPeerOfChat, userId, msgId, }); } public getMessage(entity: EntityLike | undefined, getMessagesParams?: Partial) { return this.client.getMessages(entity, getMessagesParams); } public downloadEntityPhoto(entity: EntityLike, isBig = false) { return this.client.downloadProfilePhoto(entity, { isBig }); } public downloadThumb(document: Api.Document, thumbSize = 'm') { return this.client.downloadFile(new Api.InputDocumentFileLocation({ id: document.id, accessHash: document.accessHash, fileReference: document.fileReference, thumbSize, }), { dcId: document.dcId, }); } }