feat: 创建 Telegram UserBot

This commit is contained in:
凌莞 2022-02-17 21:09:12 +08:00
parent ad5d2f0a67
commit 6c5f84bc63
No known key found for this signature in database
GPG Key ID: 05F8479BA63A8E92
8 changed files with 181 additions and 25 deletions

View File

@ -16,6 +16,7 @@
},
"dependencies": {
"@prisma/client": "^3.9.2",
"log4js": "^6.4.1",
"oicq": "^2.2.0",
"telegram": "^2.5.0"
}

View File

@ -7,11 +7,14 @@ import { DeletedMessage, DeletedMessageEvent } from 'telegram/events/DeletedMess
import { Entity, EntityLike } from 'telegram/define';
import { SendMessageParams } from 'telegram/client/messages';
import { CustomFile } from 'telegram/client/uploads';
import WaitForInputHelper from '../helpers/WaitForInputHelper';
import WaitForMessageHelper from '../helpers/WaitForMessageHelper';
type MessageHandler = (message: Api.Message) => Promise<boolean>;
export class Telegram {
private readonly client: TelegramClient;
private waitForInputHelper: WaitForInputHelper;
private waitForMessageHelper: WaitForMessageHelper;
private readonly onMessageHandlers: Array<MessageHandler> = [];
private constructor(stringSession = '') {
this.client = new TelegramClient(
@ -32,7 +35,8 @@ export class Telegram {
public static async create(startArgs: UserAuthParams | BotAuthParams, stringSession = '') {
const bot = new this(stringSession);
await bot.client.start(startArgs);
bot.waitForInputHelper = new WaitForInputHelper(bot);
bot.waitForMessageHelper = new WaitForMessageHelper(bot);
bot.client.addEventHandler(bot.onMessage, new NewMessage({}));
return bot;
}
@ -42,9 +46,24 @@ export class Telegram {
return bot;
}
public addNewMessageEventHandler(handler: (event: Api.Message) => any) {
private onMessage = async (event: NewMessageEvent) => {
// 能用的东西基本都在 message 里面,直接调用 event 里的会 undefined
this.client.addEventHandler(event => handler(event.message), new NewMessage({}));
for (const handler of this.onMessageHandlers) {
const res = await handler(event.message);
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: (event: EditedMessageEvent) => any) {
@ -56,14 +75,14 @@ export class Telegram {
}
public async getChat(entity: EntityLike) {
return new TelegramChat(this.client, await this.client.getEntity(entity), this.waitForInputHelper);
return new TelegramChat(this.client, await this.client.getEntity(entity), this.waitForMessageHelper);
}
}
export class TelegramChat {
constructor(private client: TelegramClient,
private entity: Entity,
private waitForInputHelper: WaitForInputHelper) {
constructor(private readonly client: TelegramClient,
private readonly entity: Entity,
private readonly waitForInputHelper: WaitForMessageHelper) {
}
public async sendMessage(params: SendMessageParams) {
@ -83,6 +102,6 @@ export class TelegramChat {
}
public async waitForInput() {
return this.waitForInputHelper.waitForInput(this.entity.id);
return this.waitForInputHelper.waitForMessage(this.entity.id);
}
}

View File

@ -0,0 +1,48 @@
import { Telegram } from '../client/Telegram';
import SetupService from '../services/SetupService';
import { Api } from 'telegram';
import { getLogger } from 'log4js';
export default class SetupController {
private readonly setupService: SetupService;
private log = getLogger('SetupController');
private isInProgress = false;
// 创建的 UserBot
private tgUser: Telegram;
constructor(tgBot: Telegram) {
this.setupService = new SetupService(tgBot);
tgBot.addNewMessageEventHandler(this.handleMessage);
}
private handleMessage = async (message: Api.Message) => {
if (this.isInProgress) {
return true;
}
if (message.text === '/setup') {
this.isInProgress = true;
try {
const result = await this.setupService.claimOwner(message.sender.id);
if (!result) return true;
}
catch (e) {
this.log.error('Claim Owner 失败', e);
}
await this.setupService.informOwner('创建 Telegram UserBot请输入你的手机号码需要带国家区号例如+86');
try {
const phoneNumber = await this.setupService.waitForOwnerInput();
await this.setupService.informOwner('正在登录,请稍候…');
this.tgUser = await this.setupService.createUserBot(phoneNumber);
await this.setupService.informOwner(`登录成功`);
}
catch (e) {
this.log.error('创建 UserBot 失败', e);
}
this.isInProgress = false;
return true;
}
return false;
};
}

View File

@ -2,24 +2,25 @@ import { Telegram } from '../client/Telegram';
import { BigInteger } from 'big-integer';
import { Api } from 'telegram';
export default class WaitForInputHelper {
export default class WaitForMessageHelper {
// BugInteger 好像不能用 === 判断Telegram 的 ID 还没有超过 number
private map = new Map<number, (event: Api.Message) => any>();
constructor(private tg: Telegram) {
tg.addNewMessageEventHandler(e => {
tg.addNewMessageEventHandler(async e => {
const handler = this.map.get(Number(e.chat.id));
if (handler) {
this.map.delete(Number(e.chat.id));
handler(e);
return true;
}
return false;
});
}
public waitForInput(chatId: BigInteger | number) {
public waitForMessage(chatId: BigInteger | number) {
return new Promise<Api.Message>(resolve => {
chatId = Number(chatId);
console.log(chatId);
this.map.set(chatId, resolve);
});
}

View File

@ -1,15 +1,25 @@
import { Telegram } from './client/Telegram';
import { config } from './providers/userConfig';
import { getLogger, configure } from 'log4js';
import SetupController from './controllers/SetupController';
(async () => {
configure({
appenders: {
console: { type: 'console' },
},
categories: {
default: { level: 'debug', appenders: ['console'] },
},
});
const log = getLogger('Main');
log.debug('正在登录 TG Bot');
const bot = await Telegram.create({
botAuthToken: process.env.TG_BOT_TOKEN,
});
const me = await bot.getChat('@Clansty');
const a = await me.waitForInput();
console.log(a);
const b = await me.waitForInput();
console.log(b);
await me.sendMessage({
message: a.message + b.message,
});
log.debug('TG Bot 登录完成');
if (!config.isSetup) {
log.info('当前服务器未配置,请向 Bot 发送 /setup 来设置');
const setupController = new SetupController(bot);
}
})();

View File

@ -1,6 +1,8 @@
import fs from 'fs';
import fsP from 'fs/promises'
type UserConfig = {
owner: number
userBotSession: string;
qqUin: number;
qqPassword: string;
@ -10,6 +12,7 @@ type UserConfig = {
const CONFIG_PATH = './data/config.json';
const defaultConfig: UserConfig = {
owner: 0,
userBotSession: '',
qqUin: 0,
qqPassword: '',
@ -20,6 +23,6 @@ export const config: UserConfig = fs.existsSync(CONFIG_PATH) ?
JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')) :
defaultConfig;
export const saveConfig = () => {
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 0), 'utf8');
export const saveConfig = async () => {
await fsP.writeFile(CONFIG_PATH, JSON.stringify(config, null, 0), 'utf8');
};

View File

@ -0,0 +1,73 @@
import { Telegram, TelegramChat } from '../client/Telegram';
import { config, saveConfig } from '../providers/userConfig';
import { getLogger } from 'log4js';
import { BigInteger } from 'big-integer';
export default class SetupService {
private owner: TelegramChat;
private log = getLogger('SetupService');
constructor(private readonly tgBot: Telegram) {
}
/**
* start bot bot
* @param userId ID
* @return {boolean} false
*/
public async claimOwner(userId: number | BigInteger) {
userId = Number(userId);
if (!this.owner) {
config.owner = userId;
await saveConfig();
await this.setupOwner();
this.log.info(`用户 ID: ${userId} 成为了 Bot 主人`);
return true;
}
return false;
}
private async setupOwner() {
if (!this.owner && config.owner) {
this.owner = await this.tgBot.getChat(config.owner);
}
}
public async informOwner(message: string) {
if (!this.owner) {
throw new Error('应该不会运行到这里');
}
await this.owner.sendMessage({ message });
}
public async waitForOwnerInput() {
if (!this.owner) {
throw new Error('应该不会运行到这里');
}
const { message } = await this.owner.waitForInput();
return message;
}
public async createUserBot(phoneNumber: string) {
if (!this.owner) {
throw new Error('应该不会运行到这里');
}
const bot = await Telegram.create({
phoneNumber,
password: async (hint?: string) => {
await this.owner.sendMessage({
message: `请输入你的二步验证密码${hint ? '\n密码提示' + hint : ''}`,
});
return await this.waitForOwnerInput();
},
phoneCode: async (isCodeViaApp?: boolean) => {
await this.owner.sendMessage({
message: `请输入你${isCodeViaApp ? ' Telegram APP 中' : '手机上'}收到的验证码`,
});
return await this.waitForOwnerInput();
},
onError: (err) => this.log.error(err),
});
return bot;
}
}

View File

@ -1072,7 +1072,7 @@ __metadata:
languageName: node
linkType: hard
"log4js@npm:^6.3.0":
"log4js@npm:^6.3.0, log4js@npm:^6.4.1":
version: 6.4.1
resolution: "log4js@npm:6.4.1"
dependencies:
@ -1494,6 +1494,7 @@ __metadata:
dependencies:
"@prisma/client": ^3.9.2
"@types/node": ^17.0.18
log4js: ^6.4.1
oicq: ^2.2.0
prisma: ^3.9.2
telegram: ^2.5.0