diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 664699a..e4dd703 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -10,12 +10,36 @@ datasource db { url = env("DATABASE_URL") } +model Session { + id Int @id @default(autoincrement()) + name String @unique + dcId Int? + port Int? + serverAddress String? + authKey Bytes? + entities Entity[] +} + +model Entity { + id Int @id @default(autoincrement()) + // 源代码里面大概支持 string 和 BigInteger,不如先全都存 String + entityId String + sessionId Int + session Session @relation(fields: [sessionId], references: [id]) + hash String? + username String? + phone String? + name String? + + @@unique([entityId, sessionId]) +} + model Message { id Int @id @default(autoincrement()) qqRoomId Int qqSenderId Int time Int - brief String + brief String? seq Int rand Int pktnum Int @@ -30,7 +54,7 @@ model ForwardPair { id Int @id @default(autoincrement()) qqRoomId Int @unique tgChatId Int @unique - AvatarCache AvatarCache[] + avatarCache AvatarCache[] } model File { diff --git a/src/client/Telegram.ts b/src/client/Telegram.ts index 04c6e2a..100aa90 100644 --- a/src/client/Telegram.ts +++ b/src/client/Telegram.ts @@ -10,6 +10,7 @@ import CallbackQueryHelper from '../helpers/CallbackQueryHelper'; import { CallbackQuery } from 'telegram/events/CallbackQuery'; import os from 'os'; import TelegramChat from './TelegramChat'; +import TelegramSession from './TelegramSession'; type MessageHandler = (message: Api.Message) => Promise; @@ -20,9 +21,9 @@ export default class Telegram { private readonly onMessageHandlers: Array = []; public me: Api.User; - private constructor(stringSession = '') { + private constructor(sessionId: string) { this.client = new TelegramClient( - new StringSession(stringSession), + new TelegramSession(sessionId), parseInt(process.env.TG_API_ID), process.env.TG_API_HASH, { @@ -39,15 +40,15 @@ export default class Telegram { ); } - public static async create(startArgs: UserAuthParams | BotAuthParams, stringSession = '') { - const bot = new this(stringSession); + public static async create(startArgs: UserAuthParams | BotAuthParams, sessionId: string) { + const bot = new this(sessionId); await bot.client.start(startArgs); await bot.config(); return bot; } - public static async connect(stringSession: string) { - const bot = new this(stringSession); + public static async connect(sessionId: string) { + const bot = new this(sessionId); await bot.client.connect(); await bot.config(); return bot; @@ -93,11 +94,6 @@ export default class Telegram { return new TelegramChat(this, this.client, await this.client.getEntity(entity), this.waitForMessageHelper); } - public getStringSession() { - // 上游定义不好好写 - return (this.client.session as StringSession).save(); - } - public async setCommands(commands: Api.BotCommand[], scope: Api.TypeBotCommandScope) { return await this.client.invoke( new Api.bots.SetBotCommands({ diff --git a/src/client/TelegramSession.ts b/src/client/TelegramSession.ts new file mode 100644 index 0000000..ded461b --- /dev/null +++ b/src/client/TelegramSession.ts @@ -0,0 +1,100 @@ +import { MemorySession } from 'telegram/sessions'; +import db from '../providers/db'; +import { AuthKey } from 'telegram/crypto/AuthKey'; +import { returnBigInt } from 'telegram/Helpers'; +import { getLogger } from 'log4js'; + +export default class TelegramSession extends MemorySession { + private dbId: number; + private log = getLogger('TelegramSession'); + + constructor(private readonly sessionName: string) { + super(); + } + + async load() { + this.log.trace('load'); + const dbEntry = await db.session.findFirst({ + where: { name: this.sessionName }, + include: { entities: true }, + }); + if (!dbEntry) { + this.log.debug('Session 不存在,创建'); + // 创建并返回 + const newDbEntry = await db.session.create({ data: { name: this.sessionName } }); + this.dbId = newDbEntry.id; + return; + } + this.dbId = dbEntry.id; + + const { authKey, dcId, port, serverAddress } = dbEntry; + + if (authKey && typeof authKey === 'object') { + this._authKey = new AuthKey(); + await this._authKey.setKey(authKey); + } + if (dcId) { + this._dcId = dcId; + } + if (port) { + this._port = port; + } + if (serverAddress) { + this._serverAddress = serverAddress; + } + + // id, hash, username, phone, name + this._entities = new Set( + dbEntry.entities.map(e => [returnBigInt(e.entityId), returnBigInt(e.hash), e.username, e.phone, e.name])); + } + + setDC(dcId: number, serverAddress: string, port: number) { + this.log.trace('setDC', dcId, serverAddress, port); + super.setDC(dcId, serverAddress, port); + db.session.update({ + where: { id: this.dbId }, + data: { dcId, serverAddress, port }, + }) + .then(e => this.log.trace('DC update result', e)); + } + + set authKey(value: AuthKey | undefined) { + this.log.trace('authKey', value); + this._authKey = value; + db.session.update({ + where: { id: this.dbId }, + data: { authKey: value?.getKey() || null }, + }) + .then(e => this.log.trace('authKey update result', e)); + } + + processEntities(tlo: any) { + this.log.trace('processEntities'); + const entitiesSet = this._entitiesToRows(tlo); + for (const e of entitiesSet) { + this.log.trace('processEntity', e); + this._entities.add(e); + db.entity.upsert({ + // id, hash, username, phone, name + where: { + entityId_sessionId: { sessionId: this.dbId, entityId: e[0].toString() }, + }, + create: { + sessionId: this.dbId, + entityId: e[0] && e[0].toString(), + hash: e[1] && e[1].toString(), + username: e[2] && e[2].toString(), + phone: e[3] && e[3].toString(), + name: e[4] && e[4].toString(), + }, + update: { + hash: e[1] && e[1].toString(), + username: e[2] && e[2].toString(), + phone: e[3] && e[3].toString(), + name: e[4] && e[4].toString(), + }, + }) + .then(e => this.log.trace('Entity update result', e)); + } + } +} diff --git a/src/controllers/SetupController.ts b/src/controllers/SetupController.ts index 74225c6..77b85ed 100644 --- a/src/controllers/SetupController.ts +++ b/src/controllers/SetupController.ts @@ -99,30 +99,12 @@ export default class SetupController { this.isInProgress = false; throw e; } - let createUserBot: boolean; - if (workMode === 'group') { - const createUserBotChoice = await this.setupService.waitForOwnerInput('是否创建一个 Telegram UserBot\n' + - '将 UserBot 加入转发 Bot 所在的群可以监控原生的【删除消息】操作,方便用户直接删除消息', [ - [Button.text('是', true, true)], - [Button.text('否', true, true)], - ]); - createUserBot = createUserBotChoice === '是'; - } - else { - createUserBot = true; - } // 登录 tg UserBot - if (!createUserBot) { - this.setupService.saveUserBotSession(''); - return; - } try { const phoneNumber = await this.setupService.waitForOwnerInput('创建 Telegram UserBot,请输入你的手机号码(需要带国家区号,例如:+86)'); await this.setupService.informOwner('正在登录,请稍候…'); this.tgUser = await this.setupService.createUserBot(phoneNumber); await this.setupService.informOwner(`登录成功`); - this.setupService.saveUserBotSession(this.tgUser.getStringSession()); - this.log.debug('StringSession 保存成功'); } catch (e) { this.log.error('创建 UserBot 失败', e); diff --git a/src/index.ts b/src/index.ts index a7fd540..ddf357a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,7 @@ import ForwardController from './controllers/ForwardController'; console: { type: 'console' }, }, categories: { - default: { level: 'debug', appenders: ['console'] }, + default: { level: 'trace', appenders: ['console'] }, }, }); const log = getLogger('Main'); @@ -23,7 +23,7 @@ import ForwardController from './controllers/ForwardController'; log.debug('正在登录 TG Bot'); const tgBot = await Telegram.create({ botAuthToken: process.env.TG_BOT_TOKEN, - }); + }, 'bot'); let tgUser: Telegram, oicq: OicqClient; log.debug('TG Bot 登录完成'); @@ -33,11 +33,9 @@ import ForwardController from './controllers/ForwardController'; ({ tgUser, oicq } = await setupController.waitForFinish()); } else { - if (config.userBotSession) { - log.debug('正在登录 TG UserBot'); - tgUser = await Telegram.connect(config.userBotSession); - log.debug('TG UserBot 登录完成'); - } + log.debug('正在登录 TG UserBot'); + tgUser = await Telegram.connect('user'); + log.debug('TG UserBot 登录完成'); log.debug('正在登录 OICQ'); oicq = await OicqClient.create({ uin: config.qqUin, diff --git a/src/providers/userConfig.ts b/src/providers/userConfig.ts index 944fcd5..05dec22 100644 --- a/src/providers/userConfig.ts +++ b/src/providers/userConfig.ts @@ -4,7 +4,6 @@ import { WorkMode } from '../types/definitions'; type UserConfig = { owner: number - userBotSession: string; qqUin: number; qqPassword: string; qqPlatform: number @@ -16,7 +15,6 @@ const CONFIG_PATH = './data/config.json'; const defaultConfig: UserConfig = { owner: 0, - userBotSession: '', qqUin: 0, qqPassword: '', qqPlatform: 0, diff --git a/src/services/SetupService.ts b/src/services/SetupService.ts index 10f6150..2a70697 100644 --- a/src/services/SetupService.ts +++ b/src/services/SetupService.ts @@ -72,11 +72,7 @@ export default class SetupService { return await this.waitForOwnerInput(`请输入你${isCodeViaApp ? ' Telegram APP 中' : '手机上'}收到的验证码`); }, onError: (err) => this.log.error(err), - }); - } - - public saveUserBotSession(session: string) { - config.userBotSession = session; + }, 'user'); } public async createOicq(uin: number, password: string, platform: Platform) {