2022-02-24 10:27:06 +00:00
|
|
|
|
import Telegram from '../client/Telegram';
|
2023-02-23 06:08:53 +00:00
|
|
|
|
import {
|
2023-03-27 06:57:37 +00:00
|
|
|
|
Forwardable,
|
2023-02-23 06:08:53 +00:00
|
|
|
|
Group,
|
|
|
|
|
GroupMessageEvent,
|
|
|
|
|
MessageElem, MessageRet,
|
|
|
|
|
MiraiElem,
|
|
|
|
|
PrivateMessageEvent,
|
|
|
|
|
PttElem,
|
|
|
|
|
Quotable,
|
|
|
|
|
segment,
|
|
|
|
|
Sendable,
|
|
|
|
|
} from 'oicq';
|
2022-02-24 10:27:06 +00:00
|
|
|
|
import { fetchFile, getBigFaceUrl, getImageUrlByMd5 } from '../utils/urls';
|
2022-09-11 13:14:47 +00:00
|
|
|
|
import { ButtonLike, FileLike } from 'telegram/define';
|
2022-03-07 10:14:45 +00:00
|
|
|
|
import { getLogger, Logger } from 'log4js';
|
2022-02-24 10:27:06 +00:00
|
|
|
|
import path from 'path';
|
|
|
|
|
import exts from '../constants/exts';
|
|
|
|
|
import helper from '../helpers/forwardHelper';
|
2022-03-07 08:36:13 +00:00
|
|
|
|
import db from '../models/db';
|
2022-02-24 10:27:06 +00:00
|
|
|
|
import { Button } from 'telegram/tl/custom/button';
|
|
|
|
|
import { SendMessageParams } from 'telegram/client/messages';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
import { Api } from 'telegram';
|
2022-10-12 09:24:32 +00:00
|
|
|
|
import { file as createTempFile, FileResult } from 'tmp-promise';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
import fsP from 'fs/promises';
|
2022-03-01 06:49:57 +00:00
|
|
|
|
import eviltransform from 'eviltransform';
|
2022-03-06 12:26:48 +00:00
|
|
|
|
import silk from '../encoding/silk';
|
2022-03-07 06:14:57 +00:00
|
|
|
|
import axios from 'axios';
|
|
|
|
|
import { md5Hex } from '../utils/hashing';
|
2022-03-07 08:36:13 +00:00
|
|
|
|
import Instance from '../models/Instance';
|
2022-03-07 10:05:14 +00:00
|
|
|
|
import { Pair } from '../models/Pair';
|
2022-03-17 10:15:37 +00:00
|
|
|
|
import OicqClient from '../client/OicqClient';
|
2022-03-21 03:22:57 +00:00
|
|
|
|
import lottie from '../constants/lottie';
|
2022-08-10 05:37:26 +00:00
|
|
|
|
import _ from 'lodash';
|
|
|
|
|
import emoji from '../constants/emoji';
|
2022-10-12 05:07:48 +00:00
|
|
|
|
import convert from '../helpers/convert';
|
2023-02-23 06:08:53 +00:00
|
|
|
|
import { QQMessageSent } from '../types/definitions';
|
2023-03-01 14:16:54 +00:00
|
|
|
|
import ZincSearch from 'zincsearch-node';
|
2023-03-26 12:00:53 +00:00
|
|
|
|
import { speech as AipSpeechClient } from 'baidu-aip-sdk';
|
2023-03-27 06:57:37 +00:00
|
|
|
|
import random from '../utils/random';
|
|
|
|
|
import { escapeXml } from 'oicq/lib/common';
|
2022-02-24 10:27:06 +00:00
|
|
|
|
|
2022-03-13 05:57:45 +00:00
|
|
|
|
const NOT_CHAINABLE_ELEMENTS = ['flash', 'record', 'video', 'location', 'share', 'json', 'xml', 'poke'];
|
|
|
|
|
|
2022-02-24 10:27:06 +00:00
|
|
|
|
// noinspection FallThroughInSwitchStatementJS
|
|
|
|
|
export default class ForwardService {
|
2022-03-07 10:14:45 +00:00
|
|
|
|
private readonly log: Logger;
|
2023-03-01 14:16:54 +00:00
|
|
|
|
private readonly zincSearch: ZincSearch;
|
2023-03-26 12:00:53 +00:00
|
|
|
|
private readonly speechClient: AipSpeechClient;
|
2022-02-24 10:27:06 +00:00
|
|
|
|
|
2022-03-07 08:36:13 +00:00
|
|
|
|
constructor(private readonly instance: Instance,
|
2022-03-17 10:15:37 +00:00
|
|
|
|
private readonly tgBot: Telegram,
|
|
|
|
|
private readonly oicq: OicqClient) {
|
2022-03-07 10:14:45 +00:00
|
|
|
|
this.log = getLogger(`ForwardService - ${instance.id}`);
|
2023-03-01 14:16:54 +00:00
|
|
|
|
if (process.env.ZINC_URL) {
|
|
|
|
|
this.zincSearch = new ZincSearch({
|
|
|
|
|
url: process.env.ZINC_URL,
|
|
|
|
|
user: process.env.ZINC_USERNAME,
|
|
|
|
|
password: process.env.ZINC_PASSWORD,
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-03-26 12:00:53 +00:00
|
|
|
|
if (process.env.BAIDU_APP_ID) {
|
|
|
|
|
this.speechClient = new AipSpeechClient(
|
|
|
|
|
process.env.BAIDU_APP_ID,
|
|
|
|
|
process.env.BAIDU_API_KEY,
|
|
|
|
|
process.env.BAIDU_SECRET_KEY,
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async forwardFromQq(event: PrivateMessageEvent | GroupMessageEvent, pair: Pair) {
|
|
|
|
|
try {
|
2023-02-23 06:08:53 +00:00
|
|
|
|
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 {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 07:38:52 +00:00
|
|
|
|
const tempFiles: FileResult[] = [];
|
2022-10-12 05:50:22 +00:00
|
|
|
|
let message = '', files: FileLike[] = [], buttons: ButtonLike[] = [], replyTo = 0;
|
|
|
|
|
let messageHeader = '', sender = '';
|
2022-02-24 10:27:06 +00:00
|
|
|
|
if (event.message_type === 'group') {
|
|
|
|
|
// 产生头部,这和工作模式没有关系
|
2022-10-12 05:50:22 +00:00
|
|
|
|
sender = event.sender.card || event.sender.nickname;
|
2022-03-02 15:38:16 +00:00
|
|
|
|
if (event.anonymous) {
|
|
|
|
|
sender = `[${sender}]${event.anonymous.name}`;
|
2022-03-02 13:51:59 +00:00
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
messageHeader = `<b>${helper.htmlEscape(sender)}</b>: `;
|
|
|
|
|
}
|
2022-10-12 06:34:17 +00:00
|
|
|
|
const useSticker = (file: FileLike) => {
|
|
|
|
|
files.push(file);
|
|
|
|
|
if (event.message_type === 'group') {
|
|
|
|
|
buttons.push(Button.inline(`${sender}:`));
|
2022-10-13 07:45:30 +00:00
|
|
|
|
messageHeader = '';
|
2022-10-12 06:34:17 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
2023-02-28 07:09:02 +00:00
|
|
|
|
const useForward = async (resId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const messages = await pair.qq.getForwardMsg(resId);
|
|
|
|
|
message = helper.generateForwardBrief(messages);
|
|
|
|
|
const hash = md5Hex(resId);
|
|
|
|
|
buttons.push(Button.url('📃查看', `${process.env.CRV_API}/?hash=${hash}`));
|
|
|
|
|
// 传到 Cloudflare
|
|
|
|
|
axios.post(`${process.env.CRV_API}/add`, {
|
|
|
|
|
auth: process.env.CRV_KEY,
|
|
|
|
|
key: hash,
|
|
|
|
|
data: messages,
|
|
|
|
|
})
|
|
|
|
|
.then(data => this.log.trace('上传消息记录到 Cloudflare', data.data))
|
|
|
|
|
.catch(e => this.log.error('上传消息记录到 Cloudflare 失败', e));
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
message = '[<i>转发多条消息(无法获取)</i>]';
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-02-24 10:27:06 +00:00
|
|
|
|
for (const elem of event.message) {
|
|
|
|
|
let url: string;
|
|
|
|
|
switch (elem.type) {
|
|
|
|
|
case 'text': {
|
2022-04-10 06:10:50 +00:00
|
|
|
|
// 判断微信文章
|
|
|
|
|
const WECHAT_ARTICLE_REGEX = /https?:\/\/mp\.weixin\.qq\.com\/[0-9a-zA-Z\-_+=&?#\/]+/;
|
|
|
|
|
if (WECHAT_ARTICLE_REGEX.test(elem.text)) {
|
|
|
|
|
const instantViewUrl = new URL('https://t.me/iv');
|
|
|
|
|
instantViewUrl.searchParams.set('url', WECHAT_ARTICLE_REGEX.exec(elem.text)[0]);
|
|
|
|
|
instantViewUrl.searchParams.set('rhash', '45756f9b0bb3c6');
|
|
|
|
|
message += `<a href="${instantViewUrl}">\u200e</a>`;
|
|
|
|
|
}
|
|
|
|
|
// 判断 tgs 表情
|
2022-10-12 05:50:22 +00:00
|
|
|
|
let tgs = lottie.getTgsIndex(elem.text);
|
2022-03-21 03:22:57 +00:00
|
|
|
|
if (tgs === -1) {
|
|
|
|
|
message += helper.htmlEscape(elem.text);
|
|
|
|
|
}
|
2022-10-12 05:50:22 +00:00
|
|
|
|
else {
|
2022-10-12 06:34:17 +00:00
|
|
|
|
useSticker(`assets/tgs/tgs${tgs}.tgs`);
|
2022-10-12 05:50:22 +00:00
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'at': {
|
2022-03-17 10:15:37 +00:00
|
|
|
|
if (event.source?.user_id === elem.qq || event.source?.user_id === this.oicq.uin)
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'face':
|
|
|
|
|
case 'sface': {
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message += `[<i>${helper.htmlEscape(elem.text)}</i>]`;
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'bface': {
|
2022-10-12 06:34:17 +00:00
|
|
|
|
useSticker(await convert.webp(elem.file, () => fetchFile(getBigFaceUrl(elem.file))));
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'video':
|
|
|
|
|
// 先获取 URL,要传给下面
|
|
|
|
|
url = await pair.qq.getVideoUrl(elem.fid, elem.md5);
|
|
|
|
|
case 'image':
|
|
|
|
|
if ('url' in elem)
|
|
|
|
|
url = elem.url;
|
|
|
|
|
try {
|
2023-02-22 05:59:13 +00:00
|
|
|
|
if (elem.type === 'image' && elem.asface
|
|
|
|
|
&& !(elem.file as string).toLowerCase().endsWith('.gif')
|
|
|
|
|
// 防止在 TG 中一起发送多个 sticker 失败
|
|
|
|
|
&& event.message.filter(it => it.type === 'image').length === 1
|
|
|
|
|
) {
|
2022-10-12 06:34:17 +00:00
|
|
|
|
useSticker(await convert.webp(elem.file as string, () => fetchFile(elem.url)));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
files.push(await helper.downloadToCustomFile(url, !(message || messageHeader)));
|
|
|
|
|
buttons.push(Button.url(`${emoji.picture()} 查看原图`, url));
|
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('下载媒体失败', e);
|
|
|
|
|
// 下载失败让 Telegram 服务器下载
|
|
|
|
|
files.push(url);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2022-03-04 10:50:11 +00:00
|
|
|
|
case 'flash': {
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message += `[<i>闪照<i>]\n${this.instance.workMode === 'group' ? '每人' : ''}只能查看一次`;
|
2022-03-04 10:50:11 +00:00
|
|
|
|
const dbEntry = await db.flashPhoto.create({
|
|
|
|
|
data: { photoMd5: (elem.file as string).substring(0, 32) },
|
|
|
|
|
});
|
2022-08-10 05:37:26 +00:00
|
|
|
|
buttons.push(Button.url('📸查看', `https://t.me/${this.tgBot.me.username}?start=flash-${dbEntry.id}`));
|
2022-03-04 10:50:11 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
case 'file': {
|
|
|
|
|
const extName = path.extname(elem.name);
|
2022-08-01 09:32:44 +00:00
|
|
|
|
// 50M 以下文件下载转发
|
|
|
|
|
if (elem.size < 1024 * 1024 * 50 || exts.images.includes(extName.toLowerCase())) {
|
2022-02-24 10:27:06 +00:00
|
|
|
|
// 是图片
|
2022-08-01 09:32:44 +00:00
|
|
|
|
let url = await pair.qq.getFileUrl(elem.fid);
|
|
|
|
|
if (url.includes('?fname=')) {
|
|
|
|
|
url = url.split('?fname=')[0];
|
|
|
|
|
// Request path contains unescaped characters
|
|
|
|
|
}
|
|
|
|
|
this.log.info('正在发送媒体,长度', helper.hSize(elem.size));
|
2022-02-24 10:27:06 +00:00
|
|
|
|
try {
|
2022-08-01 09:32:44 +00:00
|
|
|
|
files.push(await helper.downloadToCustomFile(url, !(message || messageHeader), elem.name));
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('下载媒体失败', e);
|
|
|
|
|
// 下载失败让 Telegram 服务器下载
|
|
|
|
|
files.push(url);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-10 05:13:55 +00:00
|
|
|
|
message = `文件: ${helper.htmlEscape(elem.name)}\n` +
|
|
|
|
|
`大小: ${helper.hSize(elem.size)}`;
|
|
|
|
|
const dbEntry = await db.file.create({
|
|
|
|
|
data: { fileId: elem.fid, roomId: pair.qqRoomId, info: message },
|
|
|
|
|
});
|
2022-08-10 05:37:26 +00:00
|
|
|
|
buttons.push(Button.url('📎获取下载地址',
|
|
|
|
|
`https://t.me/${this.tgBot.me.username}?start=file-${dbEntry.id}`));
|
2022-03-02 07:38:52 +00:00
|
|
|
|
break;
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|
|
|
|
|
case 'record': {
|
2022-03-02 07:38:52 +00:00
|
|
|
|
const temp = await createTempFile({ postfix: '.ogg' });
|
|
|
|
|
tempFiles.push(temp);
|
2023-01-08 02:49:12 +00:00
|
|
|
|
url = elem.url;
|
|
|
|
|
if (!url) {
|
|
|
|
|
const refetchMessage = await this.oicq.getMsg(event.message_id);
|
|
|
|
|
url = (refetchMessage.message.find(it => it.type === 'record') as PttElem).url;
|
|
|
|
|
}
|
|
|
|
|
await silk.decode(await fetchFile(url), temp.path);
|
2023-03-26 12:00:53 +00:00
|
|
|
|
if (this.speechClient) {
|
|
|
|
|
const pcmPath = await createTempFile({ postfix: '.pcm' });
|
|
|
|
|
tempFiles.push(pcmPath);
|
|
|
|
|
await silk.conventOggToPcm16000(temp.path, pcmPath.path);
|
|
|
|
|
const pcm = await fsP.readFile(pcmPath.path);
|
|
|
|
|
const recognize = await this.speechClient.recognize(pcm, 'pcm', 16000, {
|
|
|
|
|
dev_pid: 1537,
|
|
|
|
|
cuid: Math.random().toString(),
|
|
|
|
|
});
|
|
|
|
|
if (recognize.err_no) {
|
|
|
|
|
message += '识别失败:' + recognize.err_msg;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
message += recognize.result[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-02 07:38:52 +00:00
|
|
|
|
files.push(temp.path);
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'share': {
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message = helper.htmlEscape(elem.url);
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'json': {
|
2023-02-28 07:09:02 +00:00
|
|
|
|
const result = helper.processJson(elem.data);
|
|
|
|
|
switch (result.type) {
|
|
|
|
|
case 'text':
|
|
|
|
|
message = helper.htmlEscape(result.text);
|
|
|
|
|
break;
|
|
|
|
|
case 'forward':
|
|
|
|
|
await useForward(result.resId);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'xml': {
|
|
|
|
|
const result = helper.processXml(elem.data);
|
|
|
|
|
switch (result.type) {
|
|
|
|
|
case 'text':
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message = helper.htmlEscape(result.text);
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'image':
|
|
|
|
|
try {
|
|
|
|
|
files.push(await helper.downloadToCustomFile(getImageUrlByMd5(result.md5)));
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('下载媒体失败', e);
|
|
|
|
|
// 下载失败让 Telegram 服务器下载
|
|
|
|
|
files.push(getImageUrlByMd5(result.md5));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'forward':
|
2023-02-28 07:09:02 +00:00
|
|
|
|
await useForward(result.resId);
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'rps':
|
|
|
|
|
case 'dice':
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message = `[<i>${elem.type === 'rps' ? '猜拳' : '骰子'}</i>] ${elem.id}`;
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'poke':
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message = `[<i>戳一戳</i>] ${helper.htmlEscape(elem.text)}`;
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
case 'location':
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message = `[<i>位置</i>] ${helper.htmlEscape(elem.name)}\n${helper.htmlEscape(elem.address)}`;
|
2022-02-24 10:27:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message = message.trim();
|
2022-02-24 10:27:06 +00:00
|
|
|
|
message = messageHeader + (message && messageHeader ? '\n' : '') + message;
|
|
|
|
|
|
|
|
|
|
// 处理回复
|
|
|
|
|
if (event.source) {
|
|
|
|
|
try {
|
|
|
|
|
const quote = await db.message.findFirst({
|
|
|
|
|
where: {
|
2022-03-02 13:06:15 +00:00
|
|
|
|
qqRoomId: pair.qqRoomId,
|
2022-02-24 10:27:06 +00:00
|
|
|
|
seq: event.source.seq,
|
2023-02-22 04:41:22 +00:00
|
|
|
|
// rand: event.source.rand,
|
|
|
|
|
qqSenderId: event.source.user_id,
|
2022-03-07 11:41:59 +00:00
|
|
|
|
instanceId: this.instance.id,
|
2022-02-24 10:27:06 +00:00
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (quote) {
|
|
|
|
|
replyTo = quote.tgMsgId;
|
|
|
|
|
}
|
2023-02-22 04:41:22 +00:00
|
|
|
|
else {
|
|
|
|
|
message += '\n\n<i>*回复消息找不到</i>';
|
|
|
|
|
this.log.error('回复消息找不到', {
|
|
|
|
|
qqRoomId: pair.qqRoomId,
|
|
|
|
|
seq: event.source.seq,
|
|
|
|
|
rand: event.source.rand,
|
|
|
|
|
qqSenderId: event.source.user_id,
|
|
|
|
|
instanceId: this.instance.id,
|
|
|
|
|
});
|
2023-02-21 03:56:51 +00:00
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('查找回复消息失败', e);
|
2023-02-22 04:41:22 +00:00
|
|
|
|
message += '\n\n<i>*查找回复消息失败</i>';
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送消息
|
|
|
|
|
const messageToSend: SendMessageParams = {};
|
|
|
|
|
message && (messageToSend.message = message);
|
|
|
|
|
if (files.length === 1) {
|
|
|
|
|
messageToSend.file = files[0];
|
|
|
|
|
}
|
|
|
|
|
else if (files.length) {
|
|
|
|
|
messageToSend.file = files;
|
|
|
|
|
}
|
2022-08-10 05:37:26 +00:00
|
|
|
|
buttons.length && (messageToSend.buttons = _.chunk(buttons, 3));
|
2022-02-24 10:27:06 +00:00
|
|
|
|
replyTo && (messageToSend.replyTo = replyTo);
|
|
|
|
|
|
2022-10-12 05:50:22 +00:00
|
|
|
|
const tgMessage = await pair.tg.sendMessage(messageToSend);
|
2022-03-17 04:42:22 +00:00
|
|
|
|
|
2022-03-17 10:13:14 +00:00
|
|
|
|
if (this.instance.workMode === 'personal' && event.message_type === 'group' && event.atall) {
|
2022-10-12 05:50:22 +00:00
|
|
|
|
await tgMessage.pin({ notify: false });
|
2022-03-17 04:42:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 07:38:52 +00:00
|
|
|
|
tempFiles.forEach(it => it.cleanup());
|
2022-10-12 05:50:22 +00:00
|
|
|
|
return tgMessage;
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
2022-02-26 10:15:40 +00:00
|
|
|
|
this.log.error('从 QQ 到 TG 的消息转发失败', e);
|
2022-03-27 10:34:31 +00:00
|
|
|
|
try {
|
|
|
|
|
this.instance.workMode === 'personal' && await pair.tg.sendMessage('<i>有一条来自 QQ 的消息转发失败</i>');
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
}
|
2022-10-12 05:50:22 +00:00
|
|
|
|
return null;
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-01 14:16:54 +00:00
|
|
|
|
public async forwardFromTelegram(message: Api.Message, pair: Pair): Promise<Array<QQMessageSent>> {
|
2022-02-26 10:15:40 +00:00
|
|
|
|
try {
|
|
|
|
|
const tempFiles: FileResult[] = [];
|
|
|
|
|
const chain: Sendable = [];
|
2022-08-14 05:05:59 +00:00
|
|
|
|
const senderId = Number(message.senderId || message.sender?.id);
|
2022-03-02 15:38:16 +00:00
|
|
|
|
// 这条消息在 tg 中被回复的时候显示的
|
2023-03-27 06:57:37 +00:00
|
|
|
|
let brief = '', isSpoilerPhoto = false;
|
2023-02-23 06:08:53 +00:00
|
|
|
|
const messageHeader = helper.getUserDisplayName(message.sender) +
|
2022-03-10 14:19:22 +00:00
|
|
|
|
(message.forward ? ' 转发自 ' +
|
|
|
|
|
// 要是隐私设置了,应该会有这个,然后下面两个都获取不到
|
2022-03-13 04:53:35 +00:00
|
|
|
|
(message.fwdFrom?.fromName ||
|
2022-03-13 05:14:34 +00:00
|
|
|
|
helper.getUserDisplayName(await message.forward.getChat() || await message.forward.getSender())) :
|
2022-03-10 14:19:22 +00:00
|
|
|
|
'') +
|
2023-02-23 06:08:53 +00:00
|
|
|
|
': \n';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
if (message.photo instanceof Api.Photo ||
|
|
|
|
|
// stickers 和以文件发送的图片都是这个
|
|
|
|
|
message.document?.mimeType?.startsWith('image/')) {
|
2023-03-27 06:57:37 +00:00
|
|
|
|
if ('spoiler' in message.media && message.media.spoiler) {
|
|
|
|
|
isSpoilerPhoto = true;
|
|
|
|
|
const msgList: Forwardable[] = [{
|
|
|
|
|
user_id: this.oicq.uin,
|
|
|
|
|
nickname: messageHeader.substring(0, messageHeader.length - 3),
|
|
|
|
|
message: {
|
|
|
|
|
type: 'image',
|
|
|
|
|
file: await message.downloadMedia({}),
|
|
|
|
|
asface: !!message.sticker,
|
|
|
|
|
},
|
|
|
|
|
}];
|
|
|
|
|
if (message.message) {
|
|
|
|
|
msgList.push({
|
|
|
|
|
user_id: this.oicq.uin,
|
|
|
|
|
nickname: messageHeader.substring(0, messageHeader.length - 3),
|
|
|
|
|
message: message.message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
const fake = await pair.qq.makeForwardMsg(msgList);
|
|
|
|
|
chain.push({
|
|
|
|
|
type: 'xml',
|
|
|
|
|
id: 60,
|
|
|
|
|
data: `<?xml version="1.0" encoding="utf-8"?>` +
|
|
|
|
|
`<msg serviceID="35" templateID="1" action="viewMultiMsg" brief="[Spoiler 图片]"
|
|
|
|
|
m_resid="${fake.resid}" m_fileName="${random.fakeUuid().toUpperCase()}" tSum="${fake.tSum}"
|
|
|
|
|
sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0"><item layout="1"
|
|
|
|
|
advertiser_id="0" aid="0"><title size="34" maxLines="2" lineSpace="12"
|
|
|
|
|
>${escapeXml(messageHeader.substring(0, messageHeader.length - 2))}</title
|
|
|
|
|
><title size="26" color="#777777" maxLines="2" lineSpace="12">Spoiler 图片</title
|
|
|
|
|
>${message.message ? `<title color="#303133" size="26">${escapeXml(message.message)}</title>` : ''
|
|
|
|
|
}<hr hidden="false" style="0" /><summary size="26" color="#777777">请谨慎查看</summary
|
|
|
|
|
></item><source name="Q2TG" icon="" action="" appid="-1" /></msg>`.replaceAll('\n', ''),
|
|
|
|
|
});
|
|
|
|
|
console.log(chain);
|
|
|
|
|
brief += '[Spoiler 图片]';
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
chain.push({
|
|
|
|
|
type: 'image',
|
|
|
|
|
file: await message.downloadMedia({}),
|
|
|
|
|
asface: !!message.sticker,
|
|
|
|
|
});
|
|
|
|
|
brief += '[图片]';
|
|
|
|
|
}
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
else if (message.video || message.videoNote || message.gif) {
|
|
|
|
|
const file = message.video || message.videoNote || message.gif;
|
2023-03-27 10:26:26 +00:00
|
|
|
|
if (file.size.gt(200 * 1024 * 1024)) {
|
|
|
|
|
chain.push('[视频大于 200MB]');
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
2022-09-11 08:29:56 +00:00
|
|
|
|
else if (file.mimeType === 'video/webm' || message.gif) {
|
2022-03-13 06:14:42 +00:00
|
|
|
|
// 把 webm 转换成 gif
|
2022-10-12 05:07:48 +00:00
|
|
|
|
const convertedPath = await convert.webm2gif(message.document.id.toString(16), () => message.downloadMedia({}));
|
2022-03-13 06:14:42 +00:00
|
|
|
|
chain.push({
|
|
|
|
|
type: 'image',
|
|
|
|
|
file: convertedPath,
|
|
|
|
|
asface: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-02-26 10:15:40 +00:00
|
|
|
|
else {
|
|
|
|
|
const temp = await createTempFile();
|
|
|
|
|
tempFiles.push(temp);
|
|
|
|
|
await fsP.writeFile(temp.path, await message.downloadMedia({}));
|
|
|
|
|
chain.push(segment.video(temp.path));
|
|
|
|
|
}
|
2022-03-02 15:38:16 +00:00
|
|
|
|
brief += '[视频]';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
2022-03-02 08:02:08 +00:00
|
|
|
|
else if (message.sticker) {
|
|
|
|
|
// 一定是 tgs
|
2022-10-12 05:07:48 +00:00
|
|
|
|
const gifPath = await convert.tgs2gif(message.sticker.id.toString(16), () => message.downloadMedia({}));
|
2022-03-02 10:29:14 +00:00
|
|
|
|
chain.push({
|
|
|
|
|
type: 'image',
|
|
|
|
|
file: gifPath,
|
|
|
|
|
asface: true,
|
|
|
|
|
});
|
2022-03-02 15:38:16 +00:00
|
|
|
|
brief += '[贴纸]';
|
2022-03-02 08:02:08 +00:00
|
|
|
|
}
|
2022-02-26 10:15:40 +00:00
|
|
|
|
else if (message.voice) {
|
2022-03-02 07:38:52 +00:00
|
|
|
|
const temp = await createTempFile();
|
|
|
|
|
tempFiles.push(temp);
|
|
|
|
|
await fsP.writeFile(temp.path, await message.downloadMedia({}));
|
|
|
|
|
const bufSilk = await silk.encode(temp.path);
|
|
|
|
|
chain.push(segment.record(bufSilk));
|
2023-03-26 12:00:53 +00:00
|
|
|
|
if (this.speechClient) {
|
|
|
|
|
const pcmPath = await createTempFile({ postfix: '.pcm' });
|
|
|
|
|
tempFiles.push(pcmPath);
|
|
|
|
|
await silk.conventOggToPcm16000(temp.path, pcmPath.path);
|
|
|
|
|
const pcm = await fsP.readFile(pcmPath.path);
|
|
|
|
|
const recognize = await this.speechClient.recognize(pcm, 'pcm', 16000, {
|
|
|
|
|
dev_pid: 1537,
|
|
|
|
|
cuid: Math.random().toString(),
|
|
|
|
|
});
|
|
|
|
|
if (recognize.err_no) {
|
|
|
|
|
chain.push('识别失败:' + recognize.err_msg);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
chain.push('[语音] ', recognize.result[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-02 15:38:16 +00:00
|
|
|
|
brief += '[语音]';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
else if (message.poll) {
|
|
|
|
|
const poll = message.poll.poll;
|
|
|
|
|
chain.push(`${poll.multipleChoice ? '多' : '单'}选投票:\n${poll.question}`);
|
|
|
|
|
chain.push(...poll.answers.map(answer => `\n - ${answer.text}`));
|
2022-03-02 15:38:16 +00:00
|
|
|
|
brief += '[投票]';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
else if (message.contact) {
|
|
|
|
|
const contact = message.contact;
|
|
|
|
|
chain.push(`名片:\n` +
|
|
|
|
|
contact.firstName + (contact.lastName ? ' ' + contact.lastName : '') +
|
|
|
|
|
(contact.phoneNumber ? `\n电话:${contact.phoneNumber}` : ''));
|
2022-03-02 15:38:16 +00:00
|
|
|
|
brief += '[名片]';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
2022-03-01 06:49:57 +00:00
|
|
|
|
else if (message.venue && message.venue.geo instanceof Api.GeoPoint) {
|
2022-02-26 10:15:40 +00:00
|
|
|
|
// 地标
|
2022-03-01 06:49:57 +00:00
|
|
|
|
const geo: { lat: number, lng: number } = eviltransform.wgs2gcj(message.venue.geo.lat, message.venue.geo.long);
|
|
|
|
|
chain.push(segment.location(geo.lat, geo.lng, `${message.venue.title} (${message.venue.address})`));
|
2022-03-02 15:38:16 +00:00
|
|
|
|
brief += `[位置:${message.venue.title}]`;
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
2022-03-01 06:49:57 +00:00
|
|
|
|
else if (message.geo instanceof Api.GeoPoint) {
|
2022-02-26 10:15:40 +00:00
|
|
|
|
// 普通的位置,没有名字
|
2022-03-01 06:49:57 +00:00
|
|
|
|
const geo: { lat: number, lng: number } = eviltransform.wgs2gcj(message.geo.lat, message.geo.long);
|
|
|
|
|
chain.push(segment.location(geo.lat, geo.lng, '选中的位置'));
|
2022-03-02 15:38:16 +00:00
|
|
|
|
brief += '[位置]';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
else if (message.media instanceof Api.MessageMediaDocument && message.media.document instanceof Api.Document) {
|
|
|
|
|
const file = message.media.document;
|
|
|
|
|
const fileNameAttribute =
|
|
|
|
|
file.attributes.find(attribute => attribute instanceof Api.DocumentAttributeFilename) as Api.DocumentAttributeFilename;
|
|
|
|
|
chain.push(`文件:${fileNameAttribute ? fileNameAttribute.fileName : ''}\n` +
|
|
|
|
|
`类型:${file.mimeType}\n` +
|
|
|
|
|
`大小:${file.size}`);
|
2022-08-01 09:32:44 +00:00
|
|
|
|
if (file.size.leq(50 * 1024 * 1024)) {
|
2022-03-02 08:02:08 +00:00
|
|
|
|
chain.push('\n文件正在上传中…');
|
2022-08-01 03:14:10 +00:00
|
|
|
|
if (pair.qq instanceof Group) {
|
|
|
|
|
pair.qq.fs.upload(await message.downloadMedia({}), '/',
|
|
|
|
|
fileNameAttribute ? fileNameAttribute.fileName : 'file')
|
|
|
|
|
.catch(err => pair.qq.sendMsg(`上传失败:\n${err.message}`));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
pair.qq.sendFile(await message.downloadMedia({}),
|
|
|
|
|
fileNameAttribute ? fileNameAttribute.fileName : 'file')
|
|
|
|
|
.catch(err => pair.qq.sendMsg(`上传失败:\n${err.message}`));
|
|
|
|
|
}
|
2022-03-02 08:02:08 +00:00
|
|
|
|
}
|
2022-03-02 15:38:16 +00:00
|
|
|
|
brief += '[文件]';
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-03-27 06:57:37 +00:00
|
|
|
|
if (message.message && !isSpoilerPhoto) {
|
2022-12-23 11:01:27 +00:00
|
|
|
|
if (message.entities) {
|
|
|
|
|
const emojiEntities = message.entities.filter(it => it instanceof Api.MessageEntityCustomEmoji) as Api.MessageEntityCustomEmoji[];
|
|
|
|
|
const isMessageAllEmojis = _.sum(emojiEntities.map(it => it.length)) === message.message.length;
|
|
|
|
|
const newChain = [] as (string | MessageElem)[];
|
|
|
|
|
let messageLeft = message.message;
|
|
|
|
|
for (let i = emojiEntities.length - 1; i >= 0; i--) {
|
|
|
|
|
newChain.unshift(messageLeft.substring(emojiEntities[i].offset + emojiEntities[i].length));
|
|
|
|
|
messageLeft = messageLeft.substring(0, emojiEntities[i].offset);
|
|
|
|
|
newChain.unshift({
|
|
|
|
|
type: 'image',
|
|
|
|
|
file: await convert.customEmoji(emojiEntities[i].documentId.toString(16),
|
|
|
|
|
() => this.tgBot.getCustomEmoji(emojiEntities[i].documentId),
|
|
|
|
|
!isMessageAllEmojis),
|
|
|
|
|
asface: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
chain.push(messageLeft, ...newChain);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
chain.push(message.message);
|
|
|
|
|
}
|
2022-03-07 11:46:03 +00:00
|
|
|
|
brief += message.message;
|
|
|
|
|
}
|
2022-02-26 10:15:40 +00:00
|
|
|
|
|
|
|
|
|
// 处理回复
|
|
|
|
|
let source: Quotable;
|
|
|
|
|
if (message.replyToMsgId) {
|
|
|
|
|
try {
|
|
|
|
|
const quote = await db.message.findFirst({
|
|
|
|
|
where: {
|
|
|
|
|
tgChatId: Number(pair.tg.id),
|
|
|
|
|
tgMsgId: message.replyToMsgId,
|
2022-03-07 11:41:59 +00:00
|
|
|
|
instanceId: this.instance.id,
|
2022-02-26 10:15:40 +00:00
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (quote) {
|
|
|
|
|
source = {
|
2022-03-07 11:46:03 +00:00
|
|
|
|
message: quote.brief || ' ',
|
2022-02-26 10:15:40 +00:00
|
|
|
|
seq: quote.seq,
|
2022-12-23 07:10:37 +00:00
|
|
|
|
rand: Number(quote.rand),
|
2022-03-02 13:23:00 +00:00
|
|
|
|
user_id: Number(quote.qqSenderId),
|
2022-02-26 10:15:40 +00:00
|
|
|
|
time: quote.time,
|
|
|
|
|
};
|
|
|
|
|
}
|
2023-02-21 03:56:51 +00:00
|
|
|
|
else {
|
|
|
|
|
source = {
|
|
|
|
|
message: '回复消息找不到',
|
|
|
|
|
seq: 1,
|
|
|
|
|
time: Math.floor(new Date().getTime() / 1000),
|
|
|
|
|
rand: 1,
|
|
|
|
|
user_id: this.oicq.uin,
|
|
|
|
|
};
|
|
|
|
|
}
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('查找回复消息失败', e);
|
2023-02-21 03:56:51 +00:00
|
|
|
|
source = {
|
|
|
|
|
message: '查找回复消息失败',
|
|
|
|
|
seq: 1,
|
|
|
|
|
time: Math.floor(new Date().getTime() / 1000),
|
|
|
|
|
rand: 1,
|
|
|
|
|
user_id: this.oicq.uin,
|
|
|
|
|
};
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-23 06:08:53 +00:00
|
|
|
|
// 防止发送空白消息
|
|
|
|
|
if (chain.length === 0) {
|
2022-04-07 05:32:15 +00:00
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-13 05:57:45 +00:00
|
|
|
|
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));
|
2023-02-23 06:08:53 +00:00
|
|
|
|
|
|
|
|
|
// 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,
|
2023-02-23 06:20:14 +00:00
|
|
|
|
eqq: { type: 'tg', tgUid: senderId, noSplitSender: true, version: 2 },
|
2023-02-23 06:08:53 +00:00
|
|
|
|
q2tgSkip: true,
|
|
|
|
|
}, undefined, 0),
|
|
|
|
|
},
|
2023-02-23 06:52:19 +00:00
|
|
|
|
], source);
|
2023-02-23 06:08:53 +00:00
|
|
|
|
tempFiles.forEach(it => it.cleanup());
|
|
|
|
|
return [{
|
|
|
|
|
...messageSent,
|
|
|
|
|
senderId: pair.instanceMapForTg[senderId].client.uin,
|
|
|
|
|
brief,
|
|
|
|
|
}];
|
2022-08-14 05:05:59 +00:00
|
|
|
|
}
|
2023-02-23 06:08:53 +00:00
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('使用 MapInstance 发送消息失败', e);
|
2022-08-14 05:05:59 +00:00
|
|
|
|
}
|
2023-02-23 06:08:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-03-27 06:57:37 +00:00
|
|
|
|
if (this.instance.workMode === 'group' && !isSpoilerPhoto) {
|
2023-02-23 06:08:53 +00:00
|
|
|
|
chainableElements.unshift(messageHeader);
|
|
|
|
|
}
|
|
|
|
|
const qqMessages = [] as Array<QQMessageSent>;
|
|
|
|
|
if (chainableElements.length) {
|
|
|
|
|
chainableElements.push({
|
|
|
|
|
type: 'mirai',
|
|
|
|
|
data: JSON.stringify({
|
|
|
|
|
id: senderId,
|
2023-02-23 06:20:14 +00:00
|
|
|
|
eqq: { type: 'tg', tgUid: senderId, noSplitSender: this.instance.workMode === 'personal', version: 2 },
|
2023-02-23 06:08:53 +00:00
|
|
|
|
}, undefined, 0),
|
|
|
|
|
});
|
2022-03-13 05:57:45 +00:00
|
|
|
|
qqMessages.push({
|
|
|
|
|
...await pair.qq.sendMsg(chainableElements, source),
|
|
|
|
|
brief,
|
2023-02-23 06:08:53 +00:00
|
|
|
|
senderId: this.oicq.uin,
|
2022-03-13 05:57:45 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
2022-03-13 06:14:42 +00:00
|
|
|
|
if (notChainableElements.length) {
|
2022-04-07 05:32:15 +00:00
|
|
|
|
for (const notChainableElement of notChainableElements) {
|
|
|
|
|
qqMessages.push({
|
|
|
|
|
...await pair.qq.sendMsg(notChainableElement, source),
|
|
|
|
|
brief,
|
2023-02-23 06:08:53 +00:00
|
|
|
|
senderId: this.oicq.uin,
|
2022-04-07 05:32:15 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
2022-03-13 05:57:45 +00:00
|
|
|
|
}
|
2022-02-26 10:15:40 +00:00
|
|
|
|
tempFiles.forEach(it => it.cleanup());
|
2023-03-27 06:57:37 +00:00
|
|
|
|
console.log(qqMessages);
|
2022-03-13 05:57:45 +00:00
|
|
|
|
return qqMessages;
|
2022-02-26 10:15:40 +00:00
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('从 TG 到 QQ 的消息转发失败', e);
|
2022-03-27 10:34:31 +00:00
|
|
|
|
try {
|
|
|
|
|
await message.reply({
|
|
|
|
|
message: `<i>转发失败:${e.message}</i>\n${e}`,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-01 14:16:54 +00:00
|
|
|
|
|
|
|
|
|
public async addToZinc(pairId: number, tgMsgId: number, data: {
|
|
|
|
|
text: string,
|
|
|
|
|
nick: string,
|
|
|
|
|
}) {
|
|
|
|
|
if (!this.zincSearch) return;
|
|
|
|
|
const existsReq = await fetch(process.env.ZINC_URL + `/api/index/q2tg-${pairId}`, {
|
|
|
|
|
method: 'HEAD',
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: 'Basic ' + Buffer.from(process.env.ZINC_USERNAME + ':' + process.env.ZINC_PASSWORD).toString('base64'),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (existsReq.status === 404) {
|
|
|
|
|
await this.zincSearch.indices.create({
|
|
|
|
|
name: `q2tg-${pairId}`,
|
|
|
|
|
mappings: {
|
|
|
|
|
properties: {
|
|
|
|
|
nick: {
|
|
|
|
|
type: 'text',
|
|
|
|
|
index: true,
|
|
|
|
|
store: false,
|
|
|
|
|
aggregatable: false,
|
|
|
|
|
highlightable: true,
|
|
|
|
|
analyzer: 'gse_search',
|
|
|
|
|
search_analyzer: 'gse_standard',
|
|
|
|
|
},
|
|
|
|
|
text: {
|
|
|
|
|
type: 'text',
|
|
|
|
|
index: true,
|
|
|
|
|
store: false,
|
|
|
|
|
aggregatable: false,
|
|
|
|
|
highlightable: true,
|
|
|
|
|
analyzer: 'gse_search',
|
|
|
|
|
search_analyzer: 'gse_standard',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
await this.zincSearch.document.createOrUpdate({
|
|
|
|
|
id: tgMsgId.toString(),
|
|
|
|
|
index: `q2tg-${pairId}`,
|
|
|
|
|
document: data,
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-02-24 10:27:06 +00:00
|
|
|
|
}
|