mirror of https://github.com/Nofated095/Q2TG.git
feat: 将 session 存储在数据库中
This commit is contained in:
parent
f71b04c62b
commit
a8b87a49dd
|
@ -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 {
|
||||
|
|
|
@ -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<boolean>;
|
||||
|
||||
|
@ -20,9 +21,9 @@ export default class Telegram {
|
|||
private readonly onMessageHandlers: Array<MessageHandler> = [];
|
||||
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({
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
12
src/index.ts
12
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue