feat: 支持翻页的群组选择器

This commit is contained in:
凌莞 2022-02-20 16:25:30 +08:00
parent 10f4963131
commit c6ba18123f
No known key found for this signature in database
GPG Key ID: 05F8479BA63A8E92
10 changed files with 164 additions and 11 deletions

View File

@ -6,8 +6,8 @@ generator client {
}
datasource db {
provider = "sqlite"
url = "file:../data/data.db"
provider = "postgresql"
url = env("DATABASE_URL")
}
model Message {

View File

@ -4,17 +4,22 @@ import { BotAuthParams, UserAuthParams } from 'telegram/client/auth';
import { NewMessage, NewMessageEvent } from 'telegram/events';
import { EditedMessage, EditedMessageEvent } from 'telegram/events/EditedMessage';
import { DeletedMessage, DeletedMessageEvent } from 'telegram/events/DeletedMessage';
import { Entity, EntityLike } from 'telegram/define';
import { ButtonLike, Entity, EntityLike } from 'telegram/define';
import { SendMessageParams } from 'telegram/client/messages';
import { CustomFile } from 'telegram/client/uploads';
import WaitForMessageHelper from '../helpers/WaitForMessageHelper';
import createPaginatedInlineSelector from '../utils/paginatedInlineSelector';
import CallbackQueryHelper from '../helpers/CallbackQueryHelper';
import { CallbackQuery } from 'telegram/events/CallbackQuery';
type MessageHandler = (message: Api.Message) => Promise<boolean>;
export class Telegram {
private readonly client: TelegramClient;
private waitForMessageHelper: WaitForMessageHelper;
private callbackQueryHelper: CallbackQueryHelper = new CallbackQueryHelper();
private readonly onMessageHandlers: Array<MessageHandler> = [];
public me: Api.User;
private constructor(stringSession = '') {
this.client = new TelegramClient(
@ -35,18 +40,25 @@ export class Telegram {
public static async create(startArgs: UserAuthParams | BotAuthParams, stringSession = '') {
const bot = new this(stringSession);
await bot.client.start(startArgs);
bot.client.setParseMode('html');
bot.waitForMessageHelper = new WaitForMessageHelper(bot);
bot.client.addEventHandler(bot.onMessage, new NewMessage({}));
await bot.config();
return bot;
}
public static async connect(stringSession: string) {
const bot = new this(stringSession);
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.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) {
@ -76,7 +88,7 @@ export class Telegram {
}
public async getChat(entity: EntityLike) {
return new TelegramChat(this.client, await this.client.getEntity(entity), this.waitForMessageHelper);
return new TelegramChat(this, this.client, await this.client.getEntity(entity), this.waitForMessageHelper);
}
public getStringSession() {
@ -93,10 +105,15 @@ export class Telegram {
}),
);
}
public registerCallback(cb: () => any) {
return this.callbackQueryHelper.registerCallback(cb);
}
}
export class TelegramChat {
constructor(private readonly client: TelegramClient,
constructor(public readonly parent: Telegram,
private readonly client: TelegramClient,
private readonly entity: Entity,
private readonly waitForInputHelper: WaitForMessageHelper) {
}
@ -120,4 +137,12 @@ export class TelegramChat {
public async waitForInput() {
return this.waitForInputHelper.waitForMessage(this.entity.id);
}
public cancelWait() {
this.waitForInputHelper.cancel(this.entity.id);
}
public createPaginatedInlineSelector(message: string, choices: ButtonLike[][]) {
return createPaginatedInlineSelector(this, message, choices);
}
}

View File

@ -0,0 +1,28 @@
import { Api } from 'telegram';
import { Telegram } from '../client/Telegram';
import { Client as OicqClient } from 'oicq';
import ConfigService from '../services/ConfigService';
import { config } from '../providers/userConfig';
export default class ConfigController {
private readonly configService: ConfigService;
constructor(private readonly tgBot: Telegram,
private readonly tgUser: Telegram,
private readonly oicq: OicqClient) {
this.configService = new ConfigService(tgBot, tgUser, oicq);
tgBot.addNewMessageEventHandler(this.handleMessage);
tgBot.setCommands([], new Api.BotCommandScopeUsers());
}
private handleMessage = async (message: Api.Message) => {
if (!message.chat.id.eq(config.owner)) {
return false;
}
switch (message.message){
case '/add':
this.configService.add()
return true
}
};
}

View File

@ -23,8 +23,8 @@ export default class SetupController {
}
private handleMessage = async (message: Api.Message) => {
if (this.isInProgress) {
return true;
if (this.isInProgress || !message.isPrivate) {
return false;
}
if (message.text === '/setup') {

View File

@ -0,0 +1,20 @@
import { CallbackQueryEvent } from 'telegram/events/CallbackQuery';
export default class CallbackQueryHelper {
private readonly queries: Array<() => any> = [];
public registerCallback(cb: () => any) {
const id = this.queries.push(cb) - 1;
const buf = Buffer.alloc(2);
buf.writeUInt16LE(id);
return buf;
}
public onCallbackQuery = async (event: CallbackQueryEvent) => {
const id = event.query.data.readUint16LE();
if (this.queries[id]) {
this.queries[id]();
}
await event.answer();
};
}

View File

@ -4,6 +4,7 @@ import { getLogger, configure } from 'log4js';
import SetupController from './controllers/SetupController';
import { Client as OicqClient } from 'oicq';
import createOicq from './client/oicq';
import ConfigController from './controllers/ConfigController';
(async () => {
configure({
@ -27,7 +28,12 @@ import createOicq from './client/oicq';
({ tgUser, oicq } = await setupController.waitForFinish());
}
else {
config.userBotSession && (tgUser = await Telegram.connect(config.userBotSession));
if (config.userBotSession) {
log.debug('正在登录 TG UserBot');
tgUser = await Telegram.connect(config.userBotSession);
log.debug('TG UserBot 登录完成');
}
log.debug('正在登录 OICQ');
oicq = await createOicq({
uin: config.qqUin,
password: config.qqPassword,
@ -36,5 +42,7 @@ import createOicq from './client/oicq';
onVerifySlider: () => null,
onQrCode: () => null,
});
log.debug('OICQ 登录完成');
}
new ConfigController(tgBot, tgUser, oicq);
})();

5
src/providers/db.ts Normal file
View File

@ -0,0 +1,5 @@
import { PrismaClient } from '@prisma/client';
const db = new PrismaClient();
export default db;

View File

@ -0,0 +1,24 @@
import { Telegram, TelegramChat } from '../client/Telegram';
import { Client as OicqClient } from 'oicq';
import { config } from '../providers/userConfig';
import { Button } from 'telegram/tl/custom/button';
export default class ConfigService {
private owner: TelegramChat;
constructor(private readonly tgBot: Telegram,
private readonly tgUser: Telegram,
private readonly oicq: OicqClient) {
tgBot.getChat(config.owner).then(e => this.owner = e);
}
// 开始添加转发群组流程
public async add() {
const qGroups = Array.from(this.oicq.gl).map(e => e[1]);
await this.owner.createPaginatedInlineSelector('选择 QQ 群组\n然后选择在 TG 中的群组',
qGroups.map(e => [Button.url(
`${e.group_name} (${e.group_id})`,
`https://t.me/${this.tgBot.me.username}?startgroup=${e.group_id}`,
)]));
}
}

6
src/utils/arrays.ts Normal file
View File

@ -0,0 +1,6 @@
export default {
pagination<T>(arr: T[], pageSize: number, currentPage: number) {
const skipNum = currentPage * pageSize;
return (skipNum + pageSize >= arr.length) ? arr.slice(skipNum, arr.length) : arr.slice(skipNum, skipNum + pageSize);
},
};

View File

@ -0,0 +1,37 @@
import { ButtonLike } from 'telegram/define';
import arrays from './arrays';
import { Button } from 'telegram/tl/custom/button';
import { TelegramChat } from '../client/Telegram';
import { Api } from 'telegram';
export default async function createPaginatedInlineSelector(chat: TelegramChat, message: string, choices: ButtonLike[][]) {
const PAGE_SIZE = 8;
let currentPage = 0;
const totalPages = Math.ceil(choices.length / PAGE_SIZE);
let sentMessage: Api.Message;
const getButtons = () => {
const buttons = arrays.pagination(choices, PAGE_SIZE, currentPage);
const paginateButtons: ButtonLike[] = [];
currentPage > 0 && paginateButtons.push(Button.inline('⏪ 上一页', chat.parent.registerCallback(() => {
currentPage = Math.max(0, currentPage - 1);
sentMessage.edit({
text: message + `\n\n第 ${currentPage + 1} 页,共 ${totalPages}`,
buttons: getButtons(),
});
})));
currentPage !== totalPages - 1 && paginateButtons.push(Button.inline('下一页 ⏩', chat.parent.registerCallback(() => {
currentPage = Math.min(totalPages - 1, currentPage + 1);
console.log(currentPage);
sentMessage.edit({
text: message + `\n\n第 ${currentPage + 1} 页,共 ${totalPages}`,
buttons: getButtons(),
});
})));
paginateButtons.length && buttons.push(paginateButtons);
return buttons;
};
sentMessage = await chat.sendMessage({
message: message + `\n\n第 ${currentPage + 1} 页,共 ${totalPages}`,
buttons: getButtons(),
});
}