Q2TG/src/services/ConfigService.ts

326 lines
12 KiB
TypeScript
Raw Normal View History

2022-02-23 09:11:04 +00:00
import Telegram from '../client/Telegram';
2023-06-30 11:26:45 +00:00
import { Friend, FriendInfo, Group, GroupInfo } from 'icqq';
2022-02-20 08:25:30 +00:00
import { Button } from 'telegram/tl/custom/button';
2022-03-07 10:14:45 +00:00
import { getLogger, Logger } from 'log4js';
2022-02-23 09:11:04 +00:00
import { getAvatar } from '../utils/urls';
import { CustomFile } from 'telegram/client/uploads';
import db from '../models/db';
2022-02-22 08:24:43 +00:00
import { Api, utils } from 'telegram';
2022-02-23 09:11:04 +00:00
import OicqClient from '../client/OicqClient';
2022-03-02 11:40:51 +00:00
import { md5 } from '../utils/hashing';
2022-02-23 09:11:04 +00:00
import TelegramChat from '../client/TelegramChat';
import Instance from '../models/Instance';
import getAboutText from '../utils/getAboutText';
import random from '../utils/random';
2022-02-20 08:25:30 +00:00
const DEFAULT_FILTER_ID = 114; // 514
2022-02-20 08:25:30 +00:00
export default class ConfigService {
private owner: Promise<TelegramChat>;
2022-03-07 10:14:45 +00:00
private readonly log: Logger;
2022-02-20 08:25:30 +00:00
constructor(private readonly instance: Instance,
private readonly tgBot: Telegram,
2022-02-20 08:25:30 +00:00
private readonly oicq: OicqClient) {
this.log = getLogger(`ConfigService - ${instance.id}`);
this.owner = tgBot.getChat(this.instance.owner);
2022-02-20 08:25:30 +00:00
}
2022-02-21 06:30:02 +00:00
private getAssociateLink(roomId: number) {
return `https://t.me/${this.tgBot.me.username}?startgroup=${roomId}`;
}
// region 打开添加关联的菜单
2022-02-20 08:25:30 +00:00
// 开始添加转发群组流程
public async addGroup() {
const qGroups = Array.from(this.oicq.gl).map(e => e[1])
.filter(it => !this.instance.forwardPairs.find(-it.group_id));
const buttons = qGroups.map(e =>
this.instance.workMode === 'personal' ?
[Button.inline(
`${e.group_name} (${e.group_id})`,
this.tgBot.registerCallback(() => this.onSelectChatPersonal(e)),
)] :
[Button.url(
`${e.group_name} (${e.group_id})`,
this.getAssociateLink(-e.group_id),
)]);
await (await this.owner).createPaginatedInlineSelector(
'选择 QQ 群组' + (this.instance.workMode === 'group' ? '\n然后选择在 TG 中的群组' : ''), buttons);
}
// 只可能是 personal 运行模式
public async addFriend() {
const classes = Array.from(this.oicq.classes);
const friends = Array.from(this.oicq.fl).map(e => e[1]);
classes.sort((a, b) => {
if (a[1] < b[1]) {
return -1;
}
else if (a[1] == b[1]) {
return 0;
}
else {
return 1;
}
});
await (await this.owner).createPaginatedInlineSelector('选择分组', classes.map(e => [
Button.inline(e[1], this.tgBot.registerCallback(
() => this.openFriendSelection(friends.filter(f => f.class_id === e[0]), e[1]),
)),
]));
}
private async openFriendSelection(clazz: FriendInfo[], name: string) {
clazz = clazz.filter(them => !this.instance.forwardPairs.find(them.user_id));
await (await this.owner).createPaginatedInlineSelector(`选择 QQ 好友\n分组${name}`, clazz.map(e => [
Button.inline(`${e.remark || e.nickname} (${e.user_id})`, this.tgBot.registerCallback(
() => this.onSelectChatPersonal(e),
)),
]));
2022-02-20 08:25:30 +00:00
}
private async onSelectChatPersonal(info: FriendInfo | GroupInfo) {
const roomId = 'user_id' in info ? info.user_id : -info.group_id;
const name = 'user_id' in info ? info.remark || info.nickname : info.group_name;
const entity = this.oicq.getChat(roomId);
const avatar = await getAvatar(roomId);
const message = await (await this.owner).sendMessage({
2022-03-16 14:51:44 +00:00
message: await getAboutText(entity, true),
buttons: [
[Button.inline('自动创建群组', this.tgBot.registerCallback(
async () => {
2022-03-11 07:52:41 +00:00
await message.delete({ revoke: true });
this.createGroupAndLink(roomId, name);
}))],
[Button.url('手动选择现有群组', this.getAssociateLink(roomId))],
],
file: new CustomFile('avatar.png', avatar.length, '', avatar),
});
}
public async addExact(gin: number) {
const group = this.oicq.gl.get(gin);
let avatar: Buffer;
try {
2022-02-23 09:11:04 +00:00
avatar = await getAvatar(-group.group_id);
}
catch (e) {
avatar = null;
this.log.error(`加载 ${group.group_name} (${gin}) 的头像失败`, e);
}
const message = `${group.group_name}\n${group.group_id}\n${group.member_count} 名成员`;
await (await this.owner).sendMessage({
message,
file: avatar ? new CustomFile('avatar.png', avatar.length, '', avatar) : undefined,
2022-02-21 06:30:02 +00:00
buttons: Button.url('关联 Telegram 群组', this.getAssociateLink(-group.group_id)),
});
}
2022-02-21 06:30:02 +00:00
// endregion
/**
*
* @param room
* @param title
* @param status false
* @param chat
*/
public async createGroupAndLink(room: number | Friend | Group, title?: string, status: boolean | Api.Message = true, chat?: TelegramChat) {
this.log.info(`创建群组并关联:${room}`);
if (typeof room === 'number') {
room = this.oicq.getChat(room);
}
2022-02-23 09:11:04 +00:00
if (!title) {
// TS 这边不太智能
if (room instanceof Friend) {
title = room.remark || room.nickname;
2022-02-23 09:11:04 +00:00
}
else {
title = room.name;
2022-02-23 09:11:04 +00:00
}
}
let isFinish = false;
try {
let errorMessage = '';
2022-02-23 09:11:04 +00:00
// 状态信息
if (status === true) {
const avatar = await getAvatar(room);
const statusReceiver = chat ? await this.tgBot.getChat(chat.id) : await this.owner;
2022-03-11 07:52:41 +00:00
status = await statusReceiver.sendMessage({
message: '正在创建 Telegram 群…',
file: new CustomFile('avatar.png', avatar.length, '', avatar),
});
}
else if (status instanceof Api.Message) {
await status.edit({ text: '正在创建 Telegram 群…', buttons: Button.clear() });
}
2022-02-23 09:11:04 +00:00
2023-01-19 06:29:40 +00:00
/* if (!chat) {
// 创建群聊,拿到的是 user 的 chat
chat = await this.tgUser.createChat(title, await getAboutText(room, false));
// 添加机器人
status && await status.edit({ text: '正在添加机器人…' });
await chat.inviteMember(this.tgBot.me.id);
} */
// 设置管理员
status && await status.edit({ text: '正在设置管理员…' });
await chat.setAdmin(this.tgBot.me.username);
// 添加到 Filter
/* try {
status && await status.edit({ text: '正在将群添加到文件夹…' });
const dialogFilters = await this.tgUser.getDialogFilters() as Api.DialogFilter[];
const filter = dialogFilters.find(e => e.id === DEFAULT_FILTER_ID);
if (filter) {
filter.includePeers.push(utils.getInputPeer(chat));
await this.tgUser.updateDialogFilter({
id: DEFAULT_FILTER_ID,
filter,
});
}
}
catch (e) {
errorMessage += `\n添加到文件夹失败${e.message}`;
} */
// 关闭【添加成员】快捷条
try {
status && await status.edit({ text: '正在关闭【添加成员】快捷条…' });
await chat.hidePeerSettingsBar();
}
catch (e) {
errorMessage += `\n关闭【添加成员】快捷条失败${e.message}`;
2022-03-01 13:37:01 +00:00
}
2022-02-23 09:11:04 +00:00
// 关联写入数据库
const chatForBot = await this.tgBot.getChat(chat.id);
status && await status.edit({ text: '正在写数据库…' });
const dbPair = await this.instance.forwardPairs.add(room, chatForBot, chat);
2022-02-23 09:11:04 +00:00
isFinish = true;
// 更新头像
try {
status && await status.edit({ text: '正在更新头像…' });
const avatar = await getAvatar(room);
const avatarHash = md5(avatar);
await chatForBot.setProfilePhoto(avatar);
await db.avatarCache.create({
data: { forwardPairId: dbPair.id, hash: avatarHash },
});
}
catch (e) {
errorMessage += `\n更新头像失败${e.message}`;
}
2022-02-23 09:11:04 +00:00
// 完成
if (status) {
await status.edit({ text: '正在获取链接…' });
2022-08-01 02:39:49 +00:00
const { link } = await chat.getInviteLink() as Api.ChatInviteExported;
await status.edit({
text: '创建完成!' + (errorMessage ? '但发生以下错误' + errorMessage : ''),
buttons: Button.url('打开', link),
});
}
2022-02-23 09:11:04 +00:00
}
catch (e) {
this.log.error('创建群组并关联失败', e);
await (await this.owner).sendMessage(`创建群组并关联${isFinish ? '成功了但没完全成功' : '失败'}\n<code>${e}</code>`);
2022-02-23 09:11:04 +00:00
}
}
public async promptNewQqChat(chat: Group | Friend) {
const message = await (await this.owner).sendMessage({
message: '你' +
(chat instanceof Group ? '加入了一个新的群' : '增加了一' + random.pick('位', '个', '只', '头') + '好友') +
'\n' +
await getAboutText(chat, true) + '\n' +
'要创建关联群吗',
buttons: Button.inline('创建', this.tgBot.registerCallback(async () => {
await message.delete({ revoke: true });
this.createGroupAndLink(chat, chat instanceof Group ? chat.name : chat.remark || chat.nickname);
})),
});
return message;
}
2023-01-19 06:29:40 +00:00
public async createLinkGroup(qqRoomId: number, tgChatId: number) {
if (this.instance.workMode === 'group') {
try {
const qGroup = this.oicq.getChat(qqRoomId) as Group;
const tgChat = await this.tgBot.getChat(tgChatId);
const tgUserChat = await this.tgUser.getChat(tgChatId);
await this.instance.forwardPairs.add(qGroup, tgChat, tgUserChat);
2023-01-19 06:29:40 +00:00
await tgChat.sendMessage(`QQ群${qGroup.name} (<code>${qGroup.group_id}</code>)已与 ` +
`Telegram 群 ${(tgChat.entity as Api.Channel).title} (<code>${tgChatId}</code>)关联`);
if (!(tgChat.entity instanceof Api.Channel)) {
// TODO 添加一个转换为超级群组的方法链接
await tgChat.sendMessage({
message: '请注意,这个群不是超级群组。一些功能,比如说同步撤回,可能会工作不正常。建议将此群组转换为超级群组',
linkPreview: false,
});
}
}
catch (e) {
this.log.error(e);
await (await this.owner).sendMessage(`错误:<code>${e}</code>`);
}
}
else {
await (await this.owner).sendMessage(`跳过`);
/* const chat = await this.tgUser.getChat(tgChatId);
await this.createGroupAndLink(qqRoomId, undefined, true, chat); */
}
}
// 创建 QQ 群组的文件夹
/* public async setupFilter() {
const result = await this.tgUser.getDialogFilters() as Api.DialogFilter[];
let filter = result.find(e => e.id === DEFAULT_FILTER_ID);
if (!filter) {
this.log.info('创建 TG 文件夹');
// 要自己计算新的 id随意 id 也是可以的
// https://github.com/morethanwords/tweb/blob/7d646bc9a87d943426d831f30b69d61b743f51e0/src/lib/storages/filters.ts#L251
// 创建
filter = new Api.DialogFilter({
id: DEFAULT_FILTER_ID,
title: 'QQ',
pinnedPeers: [
(await this.tgUser.getChat(this.tgBot.me.username)).inputPeer,
],
includePeers: [],
excludePeers: [],
emoticon: '🐧',
});
let errorText = '设置文件夹失败';
try {
const isSuccess = await this.tgUser.updateDialogFilter({
id: DEFAULT_FILTER_ID,
filter,
});
if (!isSuccess) {
this.log.error(errorText);
await (await this.owner).sendMessage(errorText);
}
}
catch (e) {
this.log.error(errorText, e);
await (await this.owner).sendMessage(errorText + `\n<code>${e}</code>`);
}
}
} */
/* public async migrateAllChats() {
const dbPairs = await db.forwardPair.findMany();
for (const forwardPair of dbPairs) {
const chatForUser = await this.tgUser.getChat(Number(forwardPair.tgChatId));
if (chatForUser.entity instanceof Api.Chat) {
this.log.info('升级群组 ', chatForUser.id);
await chatForUser.migrate();
}
}
} */
}