@karinjs/adapter-qqbot 1.2.9 → 1.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/comment/config.json +3 -0
- package/config/config/config.yaml +53 -0
- package/lib/cli/pr.d.ts +2 -0
- package/lib/cli/pr.js +73 -0
- package/lib/core/adapter/adapter.d.ts +91 -0
- package/lib/core/adapter/adapter.js +144 -0
- package/lib/core/adapter/handler.d.ts +49 -0
- package/lib/core/adapter/handler.js +140 -0
- package/lib/core/adapter/markdown.d.ts +38 -0
- package/lib/core/adapter/markdown.js +220 -0
- package/lib/core/adapter/normal.d.ts +30 -0
- package/lib/core/adapter/normal.js +263 -0
- package/lib/core/api/index.d.ts +167 -0
- package/lib/core/api/index.js +180 -0
- package/lib/core/api/sign.d.ts +22 -0
- package/lib/core/api/sign.js +40 -0
- package/lib/core/api/types.d.ts +236 -0
- package/lib/core/event/conver.d.ts +10 -0
- package/lib/core/event/conver.js +57 -0
- package/lib/core/event/index.d.ts +9 -0
- package/lib/core/event/index.js +98 -0
- package/lib/core/event/message.d.ts +27 -0
- package/lib/core/event/message.js +95 -0
- package/lib/core/event/notice.d.ts +50 -0
- package/lib/core/event/notice.js +133 -0
- package/lib/{types/event.d.ts → core/event/types.d.ts} +255 -14
- package/lib/core/internal/axios.d.ts +25 -0
- package/lib/core/internal/axios.js +46 -0
- package/lib/core/internal/sign.d.ts +22 -0
- package/lib/core/internal/sign.js +40 -0
- package/lib/core/webhook/c2c.d.ts +2 -0
- package/lib/core/webhook/c2c.js +14 -0
- package/lib/core/webhook/http.d.ts +2 -0
- package/lib/core/webhook/http.js +35 -0
- package/lib/core/webhook/index.d.ts +17 -0
- package/lib/core/webhook/index.js +87 -0
- package/lib/core/webhook/ws.d.ts +2 -0
- package/lib/core/webhook/ws.js +79 -0
- package/lib/index.d.ts +1 -3
- package/lib/index.js +5 -7
- package/lib/root.d.ts +5 -0
- package/lib/root.js +9 -0
- package/lib/types/config.d.ts +53 -0
- package/lib/utils/common.d.ts +75 -61
- package/lib/utils/common.js +190 -202
- package/lib/utils/config.d.ts +5 -130
- package/lib/utils/config.js +50 -172
- package/lib/utils/dir.d.ts +1 -5
- package/lib/utils/dir.js +1 -10
- package/lib/utils/index.d.ts +4 -3
- package/lib/utils/index.js +4 -3
- package/package.json +41 -23
- package/config/config.yaml +0 -69
- package/lib/core/api.d.ts +0 -180
- package/lib/core/api.js +0 -454
- package/lib/core/index.d.ts +0 -90
- package/lib/core/index.js +0 -349
- package/lib/core/intents.d.ts +0 -6
- package/lib/core/intents.js +0 -57
- package/lib/core/markdown.d.ts +0 -23
- package/lib/core/markdown.js +0 -265
- package/lib/types/index.d.ts +0 -3
- package/lib/types/index.js +0 -3
- package/lib/types/media.d.ts +0 -100
- package/lib/types/message.d.ts +0 -85
- /package/lib/{types/event.js → core/api/types.js} +0 -0
- /package/lib/{types/media.js → core/event/types.js} +0 -0
- /package/lib/types/{message.js → config.js} +0 -0
package/lib/utils/common.js
CHANGED
|
@@ -1,210 +1,198 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import Jimp from 'jimp';
|
|
1
|
+
import fs from 'node:fs';
|
|
3
2
|
import qrcode from 'qrcode';
|
|
4
|
-
import
|
|
5
|
-
import { encode, isSilk } from 'silk-wasm';
|
|
3
|
+
import size from 'image-size';
|
|
6
4
|
import lodash from 'node-karin/lodash';
|
|
5
|
+
import EventEmitter from 'node:events';
|
|
7
6
|
import GetUrls from '@karinjs/geturls';
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
else {
|
|
39
|
-
/** 保存到本地 */
|
|
40
|
-
const buffer = await karinCommon.buffer(voice);
|
|
41
|
-
if (isSilk(buffer))
|
|
42
|
-
return `base64://${Buffer.from(buffer).toString('base64')}`;
|
|
43
|
-
fs.writeFileSync(dir, buffer);
|
|
44
|
-
}
|
|
45
|
-
const pcmPath = `${dir}.pcm`;
|
|
46
|
-
const silkPath = `${dir}.silk`;
|
|
47
|
-
const command = `${this.ffmpeg} -i ${voicePath} -f s16le -ar 48000 -ac 1 ${pcmPath}`;
|
|
48
|
-
await execs(command);
|
|
49
|
-
logger.info('pcm文件转换成功 开始转码为silk文件');
|
|
50
|
-
const buffer = await fs.promises.readFile(pcmPath);
|
|
51
|
-
const silk = await encode(buffer, 48000);
|
|
52
|
-
logger.info(`silk文件转码成功 音频时长: ${logger.yellow(silk.duration + 'ms')} 转码耗时: ${logger.yellow(Date.now() - time + 'ms')}`);
|
|
53
|
-
/** dev模式下生成silk文件并保留 */
|
|
54
|
-
if (process.env.karin_app_mode === 'dev') {
|
|
55
|
-
await fs.promises.writeFile(silkPath, Buffer.from(silk.data));
|
|
56
|
-
logger.info(`silk文件已生成: ${logger.yellow(silkPath)}`);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
/** 非dev删除文件 */
|
|
60
|
-
fs.promises.unlink(voicePath);
|
|
61
|
-
fs.promises.unlink(pcmPath);
|
|
62
|
-
}
|
|
63
|
-
return `base64://${Buffer.from(silk.data).toString('base64')}`;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* 初始化ffmpeg
|
|
67
|
-
*/
|
|
68
|
-
async initFfmpeg() {
|
|
69
|
-
try {
|
|
70
|
-
/** 环境变量 */
|
|
71
|
-
await execs('ffmpeg -version');
|
|
72
|
-
this.ffmpeg = 'ffmpeg';
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
/** 环境变量没有读配置 */
|
|
76
|
-
const ffmpeg_path = config.Config.ffmpeg_path;
|
|
77
|
-
if (!ffmpeg_path)
|
|
78
|
-
return logger.error(logger.red('ffmpeg未安装,请安装 ffmpeg 或者 配置ffmpeg_path'));
|
|
79
|
-
/** 检查文件是否存在 */
|
|
80
|
-
if (!fs.existsSync(ffmpeg_path))
|
|
81
|
-
return logger.error(logger.red('ffmpeg_path配置错误,请检查文件是否存在'));
|
|
82
|
-
this.ffmpeg = `"${ffmpeg_path}"`;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* 处理URL
|
|
87
|
-
* @param url - URL
|
|
88
|
-
* @param exclude - 排除的URL
|
|
89
|
-
*/
|
|
90
|
-
getUrls(url, exclude = []) {
|
|
91
|
-
/** 中文不符合url规范 */
|
|
92
|
-
url = url.replace(/[\u4e00-\u9fa5]/g, '|');
|
|
93
|
-
const urls = GetUrls.getUrls(url, {
|
|
94
|
-
exclude,
|
|
95
|
-
/** 去除 WWW */
|
|
96
|
-
stripWWW: false,
|
|
97
|
-
/** 规范化协议 */
|
|
98
|
-
normalizeProtocol: false,
|
|
99
|
-
/** 移除查询参数 */
|
|
100
|
-
removeQueryParameters: false,
|
|
101
|
-
/** 移除唯一斜杠 */
|
|
102
|
-
removeSingleSlash: false,
|
|
103
|
-
/** 查询参数排序 */
|
|
104
|
-
sortQueryParameters: false,
|
|
105
|
-
/** 去除认证信息 */
|
|
106
|
-
stripAuthentication: false,
|
|
107
|
-
/** 去除文本片段 */
|
|
108
|
-
stripTextFragment: false,
|
|
109
|
-
/** 移除末尾斜杠 */
|
|
110
|
-
removeTrailingSlash: false,
|
|
111
|
-
/** 不进行标准处理url */
|
|
112
|
-
normalize: false,
|
|
113
|
-
});
|
|
114
|
-
return urls;
|
|
7
|
+
import { encode, isSilk } from 'silk-wasm';
|
|
8
|
+
import { common, ffmpeg, logger, segment, tempPath } from 'node-karin';
|
|
9
|
+
/**
|
|
10
|
+
* 事件总线
|
|
11
|
+
*/
|
|
12
|
+
export const event = new EventEmitter();
|
|
13
|
+
/**
|
|
14
|
+
* 睡眠函数
|
|
15
|
+
* @param ms - 毫秒
|
|
16
|
+
*/
|
|
17
|
+
export const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
18
|
+
/**
|
|
19
|
+
* 生成随机数
|
|
20
|
+
* @param min - 最小值
|
|
21
|
+
* @param max - 最大值
|
|
22
|
+
*/
|
|
23
|
+
export const random = (min, max) => lodash.random(min, max);
|
|
24
|
+
/**
|
|
25
|
+
* 调整express中间件和路由的顺序,将路由提升到中间件前面
|
|
26
|
+
* @param stack - express实例的stack
|
|
27
|
+
* @param path - 路由名称
|
|
28
|
+
* @param method - 请求方法
|
|
29
|
+
* @param middlewareName - 中间件名称
|
|
30
|
+
*/
|
|
31
|
+
export const expressStack = (stack, path, method, middlewareName) => {
|
|
32
|
+
const routeIndex = stack.findIndex((layer) => layer.route?.path === path && layer.route?.methods?.[method]);
|
|
33
|
+
const middlewareIndex = stack.findIndex((layer) => layer.name === middlewareName);
|
|
34
|
+
if (routeIndex !== -1 && middlewareIndex !== -1 && routeIndex > middlewareIndex) {
|
|
35
|
+
const [route] = stack.splice(routeIndex, 1);
|
|
36
|
+
stack.splice(middlewareIndex, 0, route);
|
|
115
37
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
/** 空白区域高度 */
|
|
130
|
-
const padding = 0;
|
|
131
|
-
/** 二维码图像加上文字 */
|
|
132
|
-
const qrImagesWithText = await Promise.all(Jimps.map(async (qrImage) => {
|
|
133
|
-
/** 增加空白区域 */
|
|
134
|
-
const newImage = new Jimp(QRwidth, Qrheight + padding, 0xffffffff);
|
|
135
|
-
/** 将二维码图像放在空白区域下方 */
|
|
136
|
-
newImage.composite(qrImage, 0, padding);
|
|
137
|
-
return newImage;
|
|
138
|
-
}));
|
|
139
|
-
/** 动态计算每行二维码的数量 */
|
|
140
|
-
const qrPerRow = Math.ceil(Math.sqrt(urls.length));
|
|
141
|
-
/** 计算总行数和合并后图像的总宽度和总高度 */
|
|
142
|
-
const totalRows = Math.ceil(qrImagesWithText.length / qrPerRow);
|
|
143
|
-
const combinedWidth = QRwidth * qrPerRow;
|
|
144
|
-
const combinedHeight = (Qrheight + padding) * totalRows;
|
|
145
|
-
/** 创建合并后的图像对象 */
|
|
146
|
-
const combinedQRImage = new Jimp(combinedWidth, combinedHeight, 0xffffffff);
|
|
147
|
-
/** 将二维码图像合并到合并后的图像对象中 */
|
|
148
|
-
qrImagesWithText.forEach((qrImage, index) => {
|
|
149
|
-
const x = (index % qrPerRow) * QRwidth;
|
|
150
|
-
const y = Math.floor(index / qrPerRow) * (Qrheight + padding);
|
|
151
|
-
combinedQRImage.composite(qrImage, x, y);
|
|
152
|
-
});
|
|
153
|
-
/** 转为base64 */
|
|
154
|
-
const base64 = await combinedQRImage.getBase64Async(Jimp.MIME_PNG);
|
|
155
|
-
/** 宽高 */
|
|
156
|
-
const width = combinedQRImage.bitmap.width;
|
|
157
|
-
const height = combinedQRImage.bitmap.height;
|
|
158
|
-
return {
|
|
159
|
-
base64,
|
|
160
|
-
width,
|
|
161
|
-
height,
|
|
162
|
-
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* 调整express路由的顺序 使其在`expressInit`之后
|
|
41
|
+
* @param stack - express实例的stack
|
|
42
|
+
* @param path - 路由名称
|
|
43
|
+
* @param method - 请求方法
|
|
44
|
+
*/
|
|
45
|
+
export const expressAfterInit = (stack, path, method) => {
|
|
46
|
+
const routeIndex = stack.findIndex((layer) => layer.route?.path === path && layer.route?.methods?.[method]);
|
|
47
|
+
const initIndex = stack.findIndex((layer) => layer.name === 'expressInit');
|
|
48
|
+
if (routeIndex !== -1 && initIndex !== -1 && (initIndex + 1) < routeIndex) {
|
|
49
|
+
const [route] = stack.splice(routeIndex, 1);
|
|
50
|
+
stack.splice(initIndex + 1, 0, route);
|
|
163
51
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* 虚假事件
|
|
55
|
+
* @param log 日志
|
|
56
|
+
*/
|
|
57
|
+
export const fakeEvent = (log) => {
|
|
58
|
+
logger.fatal(`${logger.red('[QQBot][虚假事件]')} ${log.replace(/\n/g, '\\n')}`);
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* 处理url
|
|
62
|
+
* @param text - 文本
|
|
63
|
+
* @param exclude - 排除的URL
|
|
64
|
+
* @returns url列表
|
|
65
|
+
*/
|
|
66
|
+
export const handleUrl = (text, exclude = []) => {
|
|
67
|
+
/** 中文不符合url规范 */
|
|
68
|
+
text = text.replace(/[\u4e00-\u9fa5]/g, '|');
|
|
69
|
+
const urls = GetUrls.getUrls(text, {
|
|
70
|
+
exclude,
|
|
71
|
+
/** 去除 WWW */
|
|
72
|
+
stripWWW: false,
|
|
73
|
+
/** 规范化协议 */
|
|
74
|
+
normalizeProtocol: false,
|
|
75
|
+
/** 移除查询参数 */
|
|
76
|
+
removeQueryParameters: false,
|
|
77
|
+
/** 移除唯一斜杠 */
|
|
78
|
+
removeSingleSlash: false,
|
|
79
|
+
/** 查询参数排序 */
|
|
80
|
+
sortQueryParameters: false,
|
|
81
|
+
/** 去除认证信息 */
|
|
82
|
+
stripAuthentication: false,
|
|
83
|
+
/** 去除文本片段 */
|
|
84
|
+
stripTextFragment: false,
|
|
85
|
+
/** 移除末尾斜杠 */
|
|
86
|
+
removeTrailingSlash: false,
|
|
87
|
+
/** 不进行标准处理url */
|
|
88
|
+
normalize: false,
|
|
89
|
+
});
|
|
90
|
+
return urls;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* 生成二维码
|
|
94
|
+
* @param urls - url列表
|
|
95
|
+
* @returns 返回带`base64://`前缀的二维码列表
|
|
96
|
+
*/
|
|
97
|
+
export const qrs = async (urls) => {
|
|
98
|
+
const list = await Promise.all(urls.map((url) => qrcode.toDataURL(url)));
|
|
99
|
+
return list.map((item) => `base64://${item.split(',')[1]}`);
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* 语音文件转silk
|
|
103
|
+
* @param file 文件路径 base64 buffer
|
|
104
|
+
* @returns silk文件buffer
|
|
105
|
+
*/
|
|
106
|
+
export const silkEncode = async (file) => {
|
|
107
|
+
const buffer = await common.buffer(file);
|
|
108
|
+
if (isSilk(buffer))
|
|
109
|
+
return buffer;
|
|
110
|
+
const start = Date.now();
|
|
111
|
+
const root = `${tempPath}/recordToSilk`;
|
|
112
|
+
const temp = `${root}/${start}`;
|
|
113
|
+
const src = `${temp}/${start}`;
|
|
114
|
+
const pcm = `${temp}/${start}.pcm`;
|
|
115
|
+
// const silk = `${temp}/${start}.silk`
|
|
116
|
+
fs.mkdirSync(temp, { recursive: true });
|
|
117
|
+
fs.writeFileSync(src, buffer);
|
|
118
|
+
const { status, error } = await ffmpeg(` -i ${src} -f s16le -ar 48000 -ac 1 ${pcm}`);
|
|
119
|
+
if (!status)
|
|
120
|
+
throw error;
|
|
121
|
+
const pamBuffer = await fs.promises.readFile(pcm);
|
|
122
|
+
const { data, duration } = await encode(pamBuffer, 48000);
|
|
123
|
+
setTimeout(() => {
|
|
124
|
+
// TODO: 缓存silk文件 提升性能
|
|
125
|
+
const ms = logger.yellow(duration + 'ms');
|
|
126
|
+
const end = logger.yellow((Date.now() - start) + 'ms');
|
|
127
|
+
logger.info(`[QQBot] silk文件转码成功 音频时长: ${ms} 转码耗时: ${end}`);
|
|
128
|
+
fs.rmSync(temp, { recursive: true, force: true });
|
|
129
|
+
}, 100);
|
|
130
|
+
return Buffer.from(data);
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* 处理文本 提取其中的url转为按钮
|
|
134
|
+
* @param text - 文本
|
|
135
|
+
* @param isC2C - 是否为C2C消息
|
|
136
|
+
*/
|
|
137
|
+
export const textToButton = (text, isC2C = false) => {
|
|
138
|
+
text = text.replace(/@everyone/g, 'everyone').replace(/\n/g, '\r');
|
|
139
|
+
if (isC2C)
|
|
140
|
+
text = text.replace(/<qqbot-at-user id=".+" \/>/gm, '').replace(/<@.+>/gm, '');
|
|
141
|
+
const urls = handleUrl(text);
|
|
142
|
+
if (urls.length === 0) {
|
|
143
|
+
return { text, buttons: [] };
|
|
179
144
|
}
|
|
145
|
+
const buttons = [];
|
|
146
|
+
urls.forEach((url, index) => {
|
|
147
|
+
text = text.replace(new RegExp(url, 'g'), `[请点击 按钮${index} 查看]`);
|
|
148
|
+
buttons.push(segment.button({ text: `${index}. ${url}`, link: url }));
|
|
149
|
+
});
|
|
150
|
+
// 最多只有5个长度
|
|
151
|
+
return { text, buttons: buttons.length > 5 ? buttons.slice(0, 5) : buttons };
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* 传入一个图片链接,获取图片的宽高
|
|
155
|
+
* @param url - 图片链接
|
|
156
|
+
* @returns 图片的宽高
|
|
157
|
+
*/
|
|
158
|
+
export const getImageSize = async (url) => {
|
|
159
|
+
const buffer = await common.buffer(url);
|
|
160
|
+
const { width = 100, height = 100 } = size(buffer);
|
|
161
|
+
return { width, height };
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* 处理模板文本,对不能直接发送的 Markdown 语法进行转义
|
|
165
|
+
* @param text - 文本
|
|
166
|
+
* @returns 转义后的文本
|
|
167
|
+
*/
|
|
168
|
+
export const escapeMarkdown = (text) => {
|
|
180
169
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```md
|
|
172
|
+
* # 标题
|
|
173
|
+
* **加粗**
|
|
174
|
+
* __下划线加粗__
|
|
175
|
+
* _斜体_
|
|
176
|
+
* *星号斜体*
|
|
177
|
+
* ***加粗斜体***
|
|
178
|
+
* ~~删除线~~
|
|
179
|
+
* ```
|
|
183
180
|
*/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
*/
|
|
203
|
-
formatText(text, isC2C = false) {
|
|
204
|
-
text = text.replace(/everyone/g, '').replace(/\n/g, '\r');
|
|
205
|
-
if (isC2C)
|
|
206
|
-
text = text.replace(/<qqbot-at-user id=".+" \/>/gm, '').replace(/<@.+>/gm, '');
|
|
207
|
-
return text;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
export const common = new Common();
|
|
181
|
+
// 转义标题(仅行首的 #)
|
|
182
|
+
text = text.replace(/^(\s*)#(?!#)/gm, '$1\r#');
|
|
183
|
+
// 转义加粗斜体(排除已经转义的情况)
|
|
184
|
+
text = text.replace(/(?<!\\)\*\*\*(.+?)(?<!\\)\*\*\*/g, '\\*\\*\\*$1\\*\\*\\*');
|
|
185
|
+
// 转义加粗(星号加粗,排除已转义)
|
|
186
|
+
text = text.replace(/(?<!\\)\*\*(.+?)(?<!\\)\*\*/g, '\\*\\*$1\\*\\*');
|
|
187
|
+
// 转义下划线加粗(排除已转义)
|
|
188
|
+
text = text.replace(/(?<!\\)__(.+?)(?<!\\)__/g, '\\__\\__$1\\__\\__');
|
|
189
|
+
// 转义斜体(星号斜体,排除已转义)
|
|
190
|
+
text = text.replace(/(?<!\\)\*(.+?)(?<!\\)\*/g, '\\*$1\\*');
|
|
191
|
+
// 转义斜体(下划线斜体,排除已转义)
|
|
192
|
+
text = text.replace(/(?<!\\)_(.+?)(?<!\\)_/g, '\\_$1\\_');
|
|
193
|
+
// 转义删除线(排除已转义)
|
|
194
|
+
text = text.replace(/(?<!\\)~~(.+?)(?<!\\)~~/g, '\\~\\~$1\\~\\~');
|
|
195
|
+
// 转义其他特殊字符 这里先不管。
|
|
196
|
+
// text = text.replace(/(?<!\\)([\\`*_{}\[\]()#+\-.!])/g, '\\$1')
|
|
197
|
+
return text;
|
|
198
|
+
};
|
package/lib/utils/config.d.ts
CHANGED
|
@@ -1,134 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
* - 0: 直接发送
|
|
3
|
-
* - 1: 原生 Markdown
|
|
4
|
-
* - 3: 旧图文模板 Markdown
|
|
5
|
-
* - 4: 纯文模板 Markdown
|
|
6
|
-
* - 5: 自定义处理
|
|
7
|
-
*/
|
|
8
|
-
export type SendMode = 0 | 1 | 3 | 4 | 5;
|
|
9
|
-
/**
|
|
10
|
-
* 全局配置类型定义
|
|
11
|
-
*/
|
|
12
|
-
export interface GlobalCfgType {
|
|
13
|
-
/** 获取调用凭证的 API 地址 */
|
|
14
|
-
accessTokenApi: string;
|
|
15
|
-
/** 沙盒环境 API 地址 */
|
|
16
|
-
sandBoxApi: string;
|
|
17
|
-
/** 正式环境 API 地址 */
|
|
18
|
-
qqBotApi: string;
|
|
19
|
-
/** 是否开启沙盒环境 */
|
|
20
|
-
sandBox: boolean;
|
|
21
|
-
/** 机器人发送模式
|
|
22
|
-
* - 0: 直接发送
|
|
23
|
-
* - 1: 原生 Markdown
|
|
24
|
-
* - 3: 旧图文模板 Markdown
|
|
25
|
-
* - 4: 纯文模板 Markdown
|
|
26
|
-
* - 5: 自定义处理
|
|
27
|
-
*/
|
|
28
|
-
sendMode: SendMode;
|
|
29
|
-
/** 是否开启频道消息 */
|
|
30
|
-
guild: boolean;
|
|
31
|
-
/** 频道机器人模式 0-公域机器人 1-私域机器人 */
|
|
32
|
-
guildMode: 0 | 1;
|
|
33
|
-
/** 机器人 Markdown 模板 ID,仅在 sendMode 为 3 或 4 时有效 */
|
|
34
|
-
templateId: string;
|
|
35
|
-
/** 机器人旧图文模板 Markdown 配置 */
|
|
36
|
-
oldTemplate: {
|
|
37
|
-
/** 开头文字的键 */
|
|
38
|
-
textStartKey: string;
|
|
39
|
-
/** 图片描述的键 */
|
|
40
|
-
imgDescKey: string;
|
|
41
|
-
/** 图片地址的键 */
|
|
42
|
-
imgUrlKey: string;
|
|
43
|
-
/** 结尾文字的键 */
|
|
44
|
-
textEndKey: string;
|
|
45
|
-
};
|
|
46
|
-
/** 机器人纯文模板 Markdown 配置,10个文本键 */
|
|
47
|
-
textTemplate: string[];
|
|
48
|
-
/** 文本中的 URL 转二维码白名单,配置后将不转换这些 URL 为二维码 */
|
|
49
|
-
exclude: string[];
|
|
50
|
-
/** 接受到消息后对文本进行表达式处理,优先级高于全局配置 */
|
|
51
|
-
regex: Array<{
|
|
52
|
-
/** 匹配的正则表达式 */
|
|
53
|
-
reg: RegExp;
|
|
54
|
-
/** 替换为的内容 */
|
|
55
|
-
rep: string;
|
|
56
|
-
}>;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* 账号配置类型定义
|
|
60
|
-
*/
|
|
61
|
-
export interface AccountCfgType extends GlobalCfgType {
|
|
62
|
-
/** 机器人 AppID */
|
|
63
|
-
appId: string;
|
|
64
|
-
/** 机器人 AppSecret */
|
|
65
|
-
secret: string;
|
|
66
|
-
}
|
|
1
|
+
import type { Config } from '@/types/config';
|
|
67
2
|
/**
|
|
68
|
-
*
|
|
3
|
+
* @description package.json
|
|
69
4
|
*/
|
|
70
|
-
export
|
|
71
|
-
/**
|
|
72
|
-
* 全局默认配置
|
|
73
|
-
*/
|
|
74
|
-
default: GlobalCfgType;
|
|
75
|
-
/** 账号配置列表 */
|
|
76
|
-
accounts: Record<string, AccountCfgType>;
|
|
77
|
-
}
|
|
78
|
-
declare class Cfg {
|
|
79
|
-
/** 配置文件跟路径 */
|
|
80
|
-
dir: string;
|
|
81
|
-
/** 默认配置文件根路径 */
|
|
82
|
-
defdir: string;
|
|
83
|
-
/** 缓存 不经常用的不建议缓存 */
|
|
84
|
-
change: Map<string, any>;
|
|
85
|
-
/** 监听文件 */
|
|
86
|
-
watcher: Map<string, any>;
|
|
87
|
-
constructor();
|
|
88
|
-
/** 初始化配置 */
|
|
89
|
-
initCfg(): Promise<void>;
|
|
90
|
-
/**
|
|
91
|
-
* 更新yaml文件
|
|
92
|
-
* @param filePath - 文件路径
|
|
93
|
-
* @param settings - 设置项
|
|
94
|
-
*/
|
|
95
|
-
updateYaml(filePath: string, settings: {
|
|
96
|
-
/** 键路径 */
|
|
97
|
-
key: string;
|
|
98
|
-
/** 要写入的 */
|
|
99
|
-
val: any;
|
|
100
|
-
/** 需要写入的注释 */
|
|
101
|
-
comment: string;
|
|
102
|
-
/** 是否添加到顶部 否则添加到同一行 */
|
|
103
|
-
type: boolean;
|
|
104
|
-
}[]): void;
|
|
105
|
-
/**
|
|
106
|
-
* 基本配置
|
|
107
|
-
*/
|
|
108
|
-
get Config(): ConfigType;
|
|
109
|
-
/**
|
|
110
|
-
* 获取指定Bot配置
|
|
111
|
-
* @param {string} appID - Bot的AppID
|
|
112
|
-
*/
|
|
113
|
-
getBotConfig(appID: string): AccountCfgType | null;
|
|
114
|
-
/**
|
|
115
|
-
* package
|
|
116
|
-
*/
|
|
117
|
-
get package(): any;
|
|
118
|
-
/**
|
|
119
|
-
* 获取配置yaml
|
|
120
|
-
*/
|
|
121
|
-
getYaml(type: 'defSet' | 'config', name: string, isWatch?: boolean): any;
|
|
122
|
-
/**
|
|
123
|
-
* 监听配置文件
|
|
124
|
-
* @param {'defSet'|'config'} type 类型
|
|
125
|
-
* @param {string} name 文件名称 不带后缀
|
|
126
|
-
* @param {string} file 文件路径
|
|
127
|
-
*/
|
|
128
|
-
watch(type: 'defSet' | 'config', name: string, file: string): Promise<true | undefined>;
|
|
129
|
-
}
|
|
5
|
+
export declare const pkg: () => any;
|
|
130
6
|
/**
|
|
131
|
-
* 配置文件
|
|
7
|
+
* @description 配置文件
|
|
132
8
|
*/
|
|
133
|
-
export declare const config:
|
|
134
|
-
export {};
|
|
9
|
+
export declare const config: () => Config;
|