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")
|
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 {
|
model Message {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
qqRoomId Int
|
qqRoomId Int
|
||||||
qqSenderId Int
|
qqSenderId Int
|
||||||
time Int
|
time Int
|
||||||
brief String
|
brief String?
|
||||||
seq Int
|
seq Int
|
||||||
rand Int
|
rand Int
|
||||||
pktnum Int
|
pktnum Int
|
||||||
|
@ -30,7 +54,7 @@ model ForwardPair {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
qqRoomId Int @unique
|
qqRoomId Int @unique
|
||||||
tgChatId Int @unique
|
tgChatId Int @unique
|
||||||
AvatarCache AvatarCache[]
|
avatarCache AvatarCache[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model File {
|
model File {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import CallbackQueryHelper from '../helpers/CallbackQueryHelper';
|
||||||
import { CallbackQuery } from 'telegram/events/CallbackQuery';
|
import { CallbackQuery } from 'telegram/events/CallbackQuery';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import TelegramChat from './TelegramChat';
|
import TelegramChat from './TelegramChat';
|
||||||
|
import TelegramSession from './TelegramSession';
|
||||||
|
|
||||||
type MessageHandler = (message: Api.Message) => Promise<boolean>;
|
type MessageHandler = (message: Api.Message) => Promise<boolean>;
|
||||||
|
|
||||||
|
@ -20,9 +21,9 @@ export default class Telegram {
|
||||||
private readonly onMessageHandlers: Array<MessageHandler> = [];
|
private readonly onMessageHandlers: Array<MessageHandler> = [];
|
||||||
public me: Api.User;
|
public me: Api.User;
|
||||||
|
|
||||||
private constructor(stringSession = '') {
|
private constructor(sessionId: string) {
|
||||||
this.client = new TelegramClient(
|
this.client = new TelegramClient(
|
||||||
new StringSession(stringSession),
|
new TelegramSession(sessionId),
|
||||||
parseInt(process.env.TG_API_ID),
|
parseInt(process.env.TG_API_ID),
|
||||||
process.env.TG_API_HASH,
|
process.env.TG_API_HASH,
|
||||||
{
|
{
|
||||||
|
@ -39,15 +40,15 @@ export default class Telegram {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async create(startArgs: UserAuthParams | BotAuthParams, stringSession = '') {
|
public static async create(startArgs: UserAuthParams | BotAuthParams, sessionId: string) {
|
||||||
const bot = new this(stringSession);
|
const bot = new this(sessionId);
|
||||||
await bot.client.start(startArgs);
|
await bot.client.start(startArgs);
|
||||||
await bot.config();
|
await bot.config();
|
||||||
return bot;
|
return bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async connect(stringSession: string) {
|
public static async connect(sessionId: string) {
|
||||||
const bot = new this(stringSession);
|
const bot = new this(sessionId);
|
||||||
await bot.client.connect();
|
await bot.client.connect();
|
||||||
await bot.config();
|
await bot.config();
|
||||||
return bot;
|
return bot;
|
||||||
|
@ -93,11 +94,6 @@ export default class Telegram {
|
||||||
return new TelegramChat(this, this.client, await this.client.getEntity(entity), this.waitForMessageHelper);
|
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) {
|
public async setCommands(commands: Api.BotCommand[], scope: Api.TypeBotCommandScope) {
|
||||||
return await this.client.invoke(
|
return await this.client.invoke(
|
||||||
new Api.bots.SetBotCommands({
|
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;
|
this.isInProgress = false;
|
||||||
throw e;
|
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
|
// 登录 tg UserBot
|
||||||
if (!createUserBot) {
|
|
||||||
this.setupService.saveUserBotSession('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const phoneNumber = await this.setupService.waitForOwnerInput('创建 Telegram UserBot,请输入你的手机号码(需要带国家区号,例如:+86)');
|
const phoneNumber = await this.setupService.waitForOwnerInput('创建 Telegram UserBot,请输入你的手机号码(需要带国家区号,例如:+86)');
|
||||||
await this.setupService.informOwner('正在登录,请稍候…');
|
await this.setupService.informOwner('正在登录,请稍候…');
|
||||||
this.tgUser = await this.setupService.createUserBot(phoneNumber);
|
this.tgUser = await this.setupService.createUserBot(phoneNumber);
|
||||||
await this.setupService.informOwner(`登录成功`);
|
await this.setupService.informOwner(`登录成功`);
|
||||||
this.setupService.saveUserBotSession(this.tgUser.getStringSession());
|
|
||||||
this.log.debug('StringSession 保存成功');
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
this.log.error('创建 UserBot 失败', e);
|
this.log.error('创建 UserBot 失败', e);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import ForwardController from './controllers/ForwardController';
|
||||||
console: { type: 'console' },
|
console: { type: 'console' },
|
||||||
},
|
},
|
||||||
categories: {
|
categories: {
|
||||||
default: { level: 'debug', appenders: ['console'] },
|
default: { level: 'trace', appenders: ['console'] },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const log = getLogger('Main');
|
const log = getLogger('Main');
|
||||||
|
@ -23,7 +23,7 @@ import ForwardController from './controllers/ForwardController';
|
||||||
log.debug('正在登录 TG Bot');
|
log.debug('正在登录 TG Bot');
|
||||||
const tgBot = await Telegram.create({
|
const tgBot = await Telegram.create({
|
||||||
botAuthToken: process.env.TG_BOT_TOKEN,
|
botAuthToken: process.env.TG_BOT_TOKEN,
|
||||||
});
|
}, 'bot');
|
||||||
|
|
||||||
let tgUser: Telegram, oicq: OicqClient;
|
let tgUser: Telegram, oicq: OicqClient;
|
||||||
log.debug('TG Bot 登录完成');
|
log.debug('TG Bot 登录完成');
|
||||||
|
@ -33,11 +33,9 @@ import ForwardController from './controllers/ForwardController';
|
||||||
({ tgUser, oicq } = await setupController.waitForFinish());
|
({ tgUser, oicq } = await setupController.waitForFinish());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (config.userBotSession) {
|
|
||||||
log.debug('正在登录 TG UserBot');
|
log.debug('正在登录 TG UserBot');
|
||||||
tgUser = await Telegram.connect(config.userBotSession);
|
tgUser = await Telegram.connect('user');
|
||||||
log.debug('TG UserBot 登录完成');
|
log.debug('TG UserBot 登录完成');
|
||||||
}
|
|
||||||
log.debug('正在登录 OICQ');
|
log.debug('正在登录 OICQ');
|
||||||
oicq = await OicqClient.create({
|
oicq = await OicqClient.create({
|
||||||
uin: config.qqUin,
|
uin: config.qqUin,
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { WorkMode } from '../types/definitions';
|
||||||
|
|
||||||
type UserConfig = {
|
type UserConfig = {
|
||||||
owner: number
|
owner: number
|
||||||
userBotSession: string;
|
|
||||||
qqUin: number;
|
qqUin: number;
|
||||||
qqPassword: string;
|
qqPassword: string;
|
||||||
qqPlatform: number
|
qqPlatform: number
|
||||||
|
@ -16,7 +15,6 @@ const CONFIG_PATH = './data/config.json';
|
||||||
|
|
||||||
const defaultConfig: UserConfig = {
|
const defaultConfig: UserConfig = {
|
||||||
owner: 0,
|
owner: 0,
|
||||||
userBotSession: '',
|
|
||||||
qqUin: 0,
|
qqUin: 0,
|
||||||
qqPassword: '',
|
qqPassword: '',
|
||||||
qqPlatform: 0,
|
qqPlatform: 0,
|
||||||
|
|
|
@ -72,11 +72,7 @@ export default class SetupService {
|
||||||
return await this.waitForOwnerInput(`请输入你${isCodeViaApp ? ' Telegram APP 中' : '手机上'}收到的验证码`);
|
return await this.waitForOwnerInput(`请输入你${isCodeViaApp ? ' Telegram APP 中' : '手机上'}收到的验证码`);
|
||||||
},
|
},
|
||||||
onError: (err) => this.log.error(err),
|
onError: (err) => this.log.error(err),
|
||||||
});
|
}, 'user');
|
||||||
}
|
|
||||||
|
|
||||||
public saveUserBotSession(session: string) {
|
|
||||||
config.userBotSession = session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createOicq(uin: number, password: string, platform: Platform) {
|
public async createOicq(uin: number, password: string, platform: Platform) {
|
||||||
|
|
Loading…
Reference in New Issue