mirror of https://github.com/Nofated095/Q2TG.git
feat: MapInstance 功能,在 tg 群里面发消息,如果 tg 账号在同一进程中有一个个人模式的实例,在 QQ 群里消息会用发送者个人模式实例对应的 QQ 发出去
This commit is contained in:
parent
59204eb89a
commit
ee57624df3
|
@ -92,7 +92,7 @@ export default class ForwardController {
|
|||
await db.message.create({
|
||||
data: {
|
||||
qqRoomId: pair.qqRoomId,
|
||||
qqSenderId: this.oicq.uin,
|
||||
qqSenderId: qqMessageSent.senderId,
|
||||
time: qqMessageSent.time,
|
||||
brief: qqMessageSent.brief,
|
||||
seq: qqMessageSent.seq,
|
||||
|
|
13
src/index.ts
13
src/index.ts
|
@ -16,12 +16,21 @@ import db from './models/db';
|
|||
log.error('UnhandledException: ', error);
|
||||
});
|
||||
const instanceEntries = await db.instance.findMany();
|
||||
|
||||
const instances = [] as Instance[];
|
||||
if (!instanceEntries.length) {
|
||||
await Instance.start(0);
|
||||
instances.push(await Instance.start(0));
|
||||
}
|
||||
else {
|
||||
for (const instanceEntry of instanceEntries) {
|
||||
await Instance.start(instanceEntry.id);
|
||||
instances.push(await Instance.start(instanceEntry.id));
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(async () => {
|
||||
log.info('开始加载 MapInstance')
|
||||
for (const instance of instances.filter(it => it.workMode === 'group')) {
|
||||
await instance.forwardPairs.initMapInstance(instances.filter(it => it.workMode === 'personal'));
|
||||
}
|
||||
}, 15 * 1000);
|
||||
})();
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Entity } from 'telegram/define';
|
|||
import { BigInteger } from 'big-integer';
|
||||
import { Pair } from './Pair';
|
||||
import { getLogger, Logger } from 'log4js';
|
||||
import Instance from './Instance';
|
||||
|
||||
export default class ForwardPairs {
|
||||
private pairs: Pair[] = [];
|
||||
|
@ -75,4 +76,21 @@ export default class ForwardPairs {
|
|||
return this.pairs.find(e => e.tg.id.eq(target.id));
|
||||
}
|
||||
}
|
||||
|
||||
public async initMapInstance(instances: Instance[]) {
|
||||
for (const forwardPair of this.pairs) {
|
||||
for (const instance of instances) {
|
||||
const instanceTgUserId = instance.userMe.id.toString();
|
||||
if (forwardPair.instanceMapForTg[instanceTgUserId]) continue;
|
||||
try {
|
||||
const group = instance.oicq.getChat(forwardPair.qqRoomId) as Group;
|
||||
if (!group) continue;
|
||||
forwardPair.instanceMapForTg[instanceTgUserId] = group;
|
||||
this.log.info('MapInstance', { group: forwardPair.qqRoomId, tg: instanceTgUserId, qq: instance.qqUin });
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,9 @@ export default class Instance {
|
|||
|
||||
private readonly log: Logger;
|
||||
|
||||
private tgBot: Telegram;
|
||||
private tgUser: Telegram;
|
||||
private oicq: OicqClient;
|
||||
public tgBot: Telegram;
|
||||
public tgUser: Telegram;
|
||||
public oicq: OicqClient;
|
||||
|
||||
private _ownerChat: TelegramChat;
|
||||
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import { getLogger } from "log4js";
|
||||
import { Friend, Group } from "oicq";
|
||||
import TelegramChat from "../client/TelegramChat";
|
||||
import getAboutText from "../utils/getAboutText";
|
||||
import { md5 } from "../utils/hashing";
|
||||
import { getAvatar } from "../utils/urls";
|
||||
import db from "./db";
|
||||
import { getLogger } from 'log4js';
|
||||
import { Friend, Group } from 'oicq';
|
||||
import TelegramChat from '../client/TelegramChat';
|
||||
import getAboutText from '../utils/getAboutText';
|
||||
import { md5 } from '../utils/hashing';
|
||||
import { getAvatar } from '../utils/urls';
|
||||
import db from './db';
|
||||
|
||||
const log = getLogger("ForwardPair");
|
||||
const log = getLogger('ForwardPair');
|
||||
|
||||
export class Pair {
|
||||
// 群成员的 tg 账号对应它对应的 QQ 账号获取到的 Group 对象
|
||||
// 只有群组模式有效
|
||||
public readonly instanceMapForTg = {} as { [tgUserId: string]: Group };
|
||||
|
||||
constructor(
|
||||
public readonly qq: Friend | Group,
|
||||
private _tg: TelegramChat,
|
||||
|
@ -17,8 +21,9 @@ export class Pair {
|
|||
private _poke: boolean,
|
||||
private _enable: boolean,
|
||||
private _disableQ2TG: boolean,
|
||||
private _disableTG2Q: boolean
|
||||
) {}
|
||||
private _disableTG2Q: boolean,
|
||||
) {
|
||||
}
|
||||
|
||||
// 更新 TG 群组的头像和简介
|
||||
public async updateInfo() {
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import Telegram from '../client/Telegram';
|
||||
import { Group, GroupMessageEvent, MessageElem, PrivateMessageEvent, PttElem, Quotable, segment, Sendable } from 'oicq';
|
||||
import {
|
||||
Group,
|
||||
GroupMessageEvent,
|
||||
MessageElem, MessageRet,
|
||||
MiraiElem,
|
||||
PrivateMessageEvent,
|
||||
PttElem,
|
||||
Quotable,
|
||||
segment,
|
||||
Sendable,
|
||||
} from 'oicq';
|
||||
import { fetchFile, getBigFaceUrl, getImageUrlByMd5 } from '../utils/urls';
|
||||
import { ButtonLike, FileLike } from 'telegram/define';
|
||||
import { getLogger, Logger } from 'log4js';
|
||||
|
@ -23,7 +33,7 @@ import lottie from '../constants/lottie';
|
|||
import _ from 'lodash';
|
||||
import emoji from '../constants/emoji';
|
||||
import convert from '../helpers/convert';
|
||||
import { CustomFile } from 'telegram/client/uploads';
|
||||
import { QQMessageSent } from '../types/definitions';
|
||||
|
||||
const NOT_CHAINABLE_ELEMENTS = ['flash', 'record', 'video', 'location', 'share', 'json', 'xml', 'poke'];
|
||||
|
||||
|
@ -39,6 +49,16 @@ export default class ForwardService {
|
|||
|
||||
public async forwardFromQq(event: PrivateMessageEvent | GroupMessageEvent, pair: Pair) {
|
||||
try {
|
||||
const messageMirai = event.message.find(it => it.type === 'mirai') as MiraiElem;
|
||||
if (messageMirai) {
|
||||
try {
|
||||
const miraiData = JSON.parse(messageMirai.data);
|
||||
if (miraiData.q2tgSkip) return;
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
const tempFiles: FileResult[] = [];
|
||||
let message = '', files: FileLike[] = [], buttons: ButtonLike[] = [], replyTo = 0;
|
||||
let messageHeader = '', sender = '';
|
||||
|
@ -295,20 +315,20 @@ export default class ForwardService {
|
|||
}
|
||||
}
|
||||
|
||||
async forwardFromTelegram(message: Api.Message, pair: Pair) {
|
||||
async forwardFromTelegram(message: Api.Message, pair: Pair): Promise<Array<QQMessageSent>> {
|
||||
try {
|
||||
const tempFiles: FileResult[] = [];
|
||||
const chain: Sendable = [];
|
||||
const senderId = Number(message.senderId || message.sender?.id);
|
||||
// 这条消息在 tg 中被回复的时候显示的
|
||||
let brief = '';
|
||||
this.instance.workMode === 'group' && chain.push(helper.getUserDisplayName(message.sender) +
|
||||
const messageHeader = helper.getUserDisplayName(message.sender) +
|
||||
(message.forward ? ' 转发自 ' +
|
||||
// 要是隐私设置了,应该会有这个,然后下面两个都获取不到
|
||||
(message.fwdFrom?.fromName ||
|
||||
helper.getUserDisplayName(await message.forward.getChat() || await message.forward.getSender())) :
|
||||
'') +
|
||||
': \n');
|
||||
': \n';
|
||||
if (message.photo instanceof Api.Photo ||
|
||||
// stickers 和以文件发送的图片都是这个
|
||||
message.document?.mimeType?.startsWith('image/')) {
|
||||
|
@ -474,24 +494,60 @@ export default class ForwardService {
|
|||
}
|
||||
}
|
||||
|
||||
// 防止发送空白消息,也就是除了发送者啥都没有的消息
|
||||
if (this.instance.workMode === 'group' && chain.length === 1) {
|
||||
// 防止发送空白消息
|
||||
if (chain.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const notChainableElements = chain.filter(element => typeof element === 'object' && NOT_CHAINABLE_ELEMENTS.includes(element.type));
|
||||
const chainableElements = chain.filter(element => typeof element !== 'object' || !NOT_CHAINABLE_ELEMENTS.includes(element.type));
|
||||
const qqMessages = [];
|
||||
if (chainableElements.length) {
|
||||
if (this.instance.workMode === 'group') {
|
||||
chainableElements.push({
|
||||
type: 'mirai',
|
||||
data: JSON.stringify({ id: senderId }, undefined, 0),
|
||||
});
|
||||
|
||||
// MapInstance
|
||||
if (!notChainableElements.length // notChainableElements 无法附加 mirai 信息,要防止被来回转发
|
||||
&& chainableElements.length
|
||||
&& this.instance.workMode
|
||||
&& pair.instanceMapForTg[senderId]
|
||||
) {
|
||||
try {
|
||||
const messageSent = await pair.instanceMapForTg[senderId].sendMsg([
|
||||
...chainableElements,
|
||||
{
|
||||
type: 'mirai',
|
||||
data: JSON.stringify({
|
||||
id: senderId,
|
||||
eqq: { type: 'tg', tgUid: senderId, noSplitSender: true },
|
||||
q2tgSkip: true,
|
||||
}, undefined, 0),
|
||||
},
|
||||
]);
|
||||
tempFiles.forEach(it => it.cleanup());
|
||||
return [{
|
||||
...messageSent,
|
||||
senderId: pair.instanceMapForTg[senderId].client.uin,
|
||||
brief,
|
||||
}];
|
||||
}
|
||||
catch (e) {
|
||||
this.log.error('使用 MapInstance 发送消息失败', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.instance.workMode === 'group') {
|
||||
chainableElements.unshift(messageHeader);
|
||||
}
|
||||
const qqMessages = [] as Array<QQMessageSent>;
|
||||
if (chainableElements.length) {
|
||||
chainableElements.push({
|
||||
type: 'mirai',
|
||||
data: JSON.stringify({
|
||||
id: senderId,
|
||||
eqq: { type: 'tg', tgUid: senderId, noSplitSender: this.instance.workMode === 'personal' },
|
||||
}, undefined, 0),
|
||||
});
|
||||
qqMessages.push({
|
||||
...await pair.qq.sendMsg(chainableElements, source),
|
||||
brief,
|
||||
senderId: this.oicq.uin,
|
||||
});
|
||||
}
|
||||
if (notChainableElements.length) {
|
||||
|
@ -499,6 +555,7 @@ export default class ForwardService {
|
|||
qqMessages.push({
|
||||
...await pair.qq.sendMsg(notChainableElement, source),
|
||||
brief,
|
||||
senderId: this.oicq.uin,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
import { MessageRet } from 'oicq';
|
||||
|
||||
export type WorkMode = 'group' | 'personal';
|
||||
export type QQMessageSent = MessageRet & { senderId: number, brief: string };
|
||||
|
|
Loading…
Reference in New Issue