@karinjs/adapter-qqbot 1.2.9 → 1.3.1
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/README.md +869 -84
- package/dist/chunk-PYOUSZ6B.js +1501 -0
- package/dist/index.d.ts +499 -0
- package/dist/index.js +100 -0
- package/dist/web.config.d.ts +25 -0
- package/dist/web.config.js +224 -0
- package/package.json +50 -25
- 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/index.d.ts +0 -3
- package/lib/index.js +0 -7
- package/lib/types/event.d.ts +0 -232
- package/lib/types/event.js +0 -1
- 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/media.js +0 -1
- package/lib/types/message.d.ts +0 -85
- package/lib/types/message.js +0 -1
- package/lib/utils/common.d.ts +0 -66
- package/lib/utils/common.js +0 -210
- package/lib/utils/config.d.ts +0 -134
- package/lib/utils/config.js +0 -176
- package/lib/utils/dir.d.ts +0 -5
- package/lib/utils/dir.js +0 -10
- package/lib/utils/index.d.ts +0 -3
- package/lib/utils/index.js +0 -3
package/lib/types/message.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { ArkType, KeyboardType, MarkdownType } from 'node-karin';
|
|
2
|
-
import { MediaType } from './media.js';
|
|
3
|
-
/**
|
|
4
|
-
* 请求路径类型
|
|
5
|
-
*/
|
|
6
|
-
export declare const enum PathType {
|
|
7
|
-
Groups = "groups",
|
|
8
|
-
Friends = "users"
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* 消息类型
|
|
12
|
-
*/
|
|
13
|
-
export declare const enum MessageType {
|
|
14
|
-
/**
|
|
15
|
-
* 文本消息
|
|
16
|
-
*/
|
|
17
|
-
Text = 0,
|
|
18
|
-
/**
|
|
19
|
-
* markdown消息
|
|
20
|
-
*/
|
|
21
|
-
Markdown = 2,
|
|
22
|
-
/**
|
|
23
|
-
* ark消息
|
|
24
|
-
*/
|
|
25
|
-
Ark = 3,
|
|
26
|
-
/**
|
|
27
|
-
* embed消息
|
|
28
|
-
*/
|
|
29
|
-
Embed = 4,
|
|
30
|
-
/**
|
|
31
|
-
* 富媒体消息
|
|
32
|
-
*/
|
|
33
|
-
Media = 7
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* v2接口枚举
|
|
37
|
-
*/
|
|
38
|
-
export declare const enum V2ApiType {
|
|
39
|
-
/**
|
|
40
|
-
* 发送消息
|
|
41
|
-
*/
|
|
42
|
-
Message = "messages",
|
|
43
|
-
/**
|
|
44
|
-
* 上传富媒体文件
|
|
45
|
-
*/
|
|
46
|
-
Files = "files"
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* 被动消息ID类型
|
|
50
|
-
*/
|
|
51
|
-
export declare const enum IdType {
|
|
52
|
-
MsgID = "msg_id",
|
|
53
|
-
EventID = "event_id"
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* 被动消息Seq
|
|
57
|
-
*/
|
|
58
|
-
export declare const enum SeqType {
|
|
59
|
-
MsgSeq = "msg_seq"
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* 发送消息请求参数
|
|
63
|
-
*/
|
|
64
|
-
export interface SendMessageOptions {
|
|
65
|
-
/** 文本内容 没有传空格 */
|
|
66
|
-
content: string;
|
|
67
|
-
/** 消息类型 */
|
|
68
|
-
msg_type: MessageType;
|
|
69
|
-
/** markdown消息 */
|
|
70
|
-
markdown?: MarkdownType<boolean>;
|
|
71
|
-
/** keyboard按钮消息 */
|
|
72
|
-
keyboard?: KeyboardType<boolean>;
|
|
73
|
-
/** ark消息 */
|
|
74
|
-
ark?: ArkType;
|
|
75
|
-
/** 富媒体消息 */
|
|
76
|
-
media?: MediaType;
|
|
77
|
-
/** 【暂未支持】消息引用 */
|
|
78
|
-
message_reference?: object;
|
|
79
|
-
/** 平台方事件ID,可以用于被动消息发送 */
|
|
80
|
-
[IdType.EventID]?: string;
|
|
81
|
-
/** 平台方消息ID,可以用于被动消息发送 */
|
|
82
|
-
[IdType.MsgID]?: string;
|
|
83
|
-
/** 回复消息的序号 相同的 msg_id + msg_seq 重复发送会失败 */
|
|
84
|
-
[SeqType.MsgSeq]?: number;
|
|
85
|
-
}
|
package/lib/types/message.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/lib/utils/common.d.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { ButtonElement } from 'node-karin';
|
|
2
|
-
/**
|
|
3
|
-
* 二维码返回参数
|
|
4
|
-
*/
|
|
5
|
-
export interface QRRes {
|
|
6
|
-
/** 二维码base64 */
|
|
7
|
-
base64: string;
|
|
8
|
-
/** 宽度 */
|
|
9
|
-
width: number;
|
|
10
|
-
/** 高度 */
|
|
11
|
-
height: number;
|
|
12
|
-
}
|
|
13
|
-
declare class Common {
|
|
14
|
-
/** ffmpeg */
|
|
15
|
-
ffmpeg: string;
|
|
16
|
-
constructor();
|
|
17
|
-
random(min: number, max: number): number;
|
|
18
|
-
/**
|
|
19
|
-
* 语音转silk
|
|
20
|
-
* @param voice - 语音文件
|
|
21
|
-
* @returns silk buffer
|
|
22
|
-
*/
|
|
23
|
-
voiceToSilk(voice: string): Promise<string>;
|
|
24
|
-
/**
|
|
25
|
-
* 初始化ffmpeg
|
|
26
|
-
*/
|
|
27
|
-
initFfmpeg(): Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* 处理URL
|
|
30
|
-
* @param url - URL
|
|
31
|
-
* @param exclude - 排除的URL
|
|
32
|
-
*/
|
|
33
|
-
getUrls(url: string, exclude?: never[]): string[];
|
|
34
|
-
/**
|
|
35
|
-
* 传入URL数组,转为二维码base64
|
|
36
|
-
* @param urls - URL数组
|
|
37
|
-
*/
|
|
38
|
-
getQrCode(urls: string[]): Promise<QRRes>;
|
|
39
|
-
/**
|
|
40
|
-
* 传入文本 返回处理后的文本、图片
|
|
41
|
-
* @param text - 文本
|
|
42
|
-
*/
|
|
43
|
-
getQQBotText(text: string): Promise<{
|
|
44
|
-
/** 处理后的文本 */
|
|
45
|
-
text: string;
|
|
46
|
-
/** 二维码数据 */
|
|
47
|
-
data?: QRRes;
|
|
48
|
-
}>;
|
|
49
|
-
/**
|
|
50
|
-
* 传入文本 处理文本 返回的按钮为多行按钮
|
|
51
|
-
* @param text - 文本
|
|
52
|
-
*/
|
|
53
|
-
getQQBotButton(text: string): {
|
|
54
|
-
text: string;
|
|
55
|
-
data: ButtonElement[];
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* 传入文本 将文本一些特殊字符转义去除 用于处理markdown
|
|
59
|
-
* @param text - 文本
|
|
60
|
-
* @param isC2C - 是否是C2C
|
|
61
|
-
* @returns 处理后的文本
|
|
62
|
-
*/
|
|
63
|
-
formatText(text: string, isC2C?: boolean): string;
|
|
64
|
-
}
|
|
65
|
-
export declare const common: Common;
|
|
66
|
-
export {};
|
package/lib/utils/common.js
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import Jimp from 'jimp';
|
|
3
|
-
import qrcode from 'qrcode';
|
|
4
|
-
import { basename } from './dir.js';
|
|
5
|
-
import { encode, isSilk } from 'silk-wasm';
|
|
6
|
-
import lodash from 'node-karin/lodash';
|
|
7
|
-
import GetUrls from '@karinjs/geturls';
|
|
8
|
-
import { config, execs, logger, common as karinCommon, segment } from 'node-karin';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
class Common {
|
|
11
|
-
/** ffmpeg */
|
|
12
|
-
ffmpeg;
|
|
13
|
-
constructor() {
|
|
14
|
-
this.ffmpeg = '';
|
|
15
|
-
this.initFfmpeg();
|
|
16
|
-
}
|
|
17
|
-
random(min, max) {
|
|
18
|
-
return lodash.random(min, max);
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* 语音转silk
|
|
22
|
-
* @param voice - 语音文件
|
|
23
|
-
* @returns silk buffer
|
|
24
|
-
*/
|
|
25
|
-
async voiceToSilk(voice) {
|
|
26
|
-
if (!this.ffmpeg)
|
|
27
|
-
throw TypeError('ffmpeg未安装,请安装 ffmpeg 或者 配置ffmpeg_path');
|
|
28
|
-
const time = Date.now();
|
|
29
|
-
const dir = `${process.cwd()}/temp/${basename}/${Date.now()}`;
|
|
30
|
-
let voicePath = dir;
|
|
31
|
-
/** 传入的是路径 则直接使用 */
|
|
32
|
-
if (fs.existsSync(voice)) {
|
|
33
|
-
const buffer = fs.readFileSync(voice);
|
|
34
|
-
if (isSilk(buffer))
|
|
35
|
-
return `base64://${Buffer.from(buffer).toString('base64')}`;
|
|
36
|
-
voicePath = path.resolve(voice);
|
|
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;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* 传入URL数组,转为二维码base64
|
|
118
|
-
* @param urls - URL数组
|
|
119
|
-
*/
|
|
120
|
-
async getQrCode(urls) {
|
|
121
|
-
const list = await Promise.all(urls.map(url => qrcode.toDataURL(url)));
|
|
122
|
-
/** 转为jimp图像对象 */
|
|
123
|
-
const Jimps = await Promise.all(list.map(url => {
|
|
124
|
-
return Jimp.read(Buffer.from(url.split(',')[1], 'base64'));
|
|
125
|
-
}));
|
|
126
|
-
/** 获取最长的宽高 */
|
|
127
|
-
const QRwidth = lodash.max(Jimps.map(j => j.bitmap.width)) || Jimps[0].bitmap.width;
|
|
128
|
-
const Qrheight = lodash.sum(Jimps.map(j => j.bitmap.height));
|
|
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
|
-
};
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* 传入文本 返回处理后的文本、图片
|
|
166
|
-
* @param text - 文本
|
|
167
|
-
*/
|
|
168
|
-
async getQQBotText(text) {
|
|
169
|
-
const urls = this.getUrls(text);
|
|
170
|
-
const data = urls.length ? (await this.getQrCode(urls)) : undefined;
|
|
171
|
-
/** 使用for循环来替换字符串 保证一致性 */
|
|
172
|
-
for (const url of urls) {
|
|
173
|
-
text = text.replace(url, '[请扫码查看]');
|
|
174
|
-
}
|
|
175
|
-
return {
|
|
176
|
-
text,
|
|
177
|
-
data,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* 传入文本 处理文本 返回的按钮为多行按钮
|
|
182
|
-
* @param text - 文本
|
|
183
|
-
*/
|
|
184
|
-
getQQBotButton(text) {
|
|
185
|
-
const urls = this.getUrls(text);
|
|
186
|
-
const data = [];
|
|
187
|
-
/** 使用for循环来替换字符串 保证一致性 */
|
|
188
|
-
for (const url of urls) {
|
|
189
|
-
text = text.replace(url, '[请点击按钮查看]');
|
|
190
|
-
data.push(segment.button({ text: url, link: url }));
|
|
191
|
-
}
|
|
192
|
-
return {
|
|
193
|
-
text,
|
|
194
|
-
data,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* 传入文本 将文本一些特殊字符转义去除 用于处理markdown
|
|
199
|
-
* @param text - 文本
|
|
200
|
-
* @param isC2C - 是否是C2C
|
|
201
|
-
* @returns 处理后的文本
|
|
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();
|
package/lib/utils/config.d.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
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
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* 配置类型定义
|
|
69
|
-
*/
|
|
70
|
-
export interface ConfigType {
|
|
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
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* 配置文件
|
|
132
|
-
*/
|
|
133
|
-
export declare const config: Cfg;
|
|
134
|
-
export {};
|
package/lib/utils/config.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import Yaml from 'yaml';
|
|
3
|
-
import chokidar from 'node-karin/chokidar';
|
|
4
|
-
import { dirPath, basename } from '../utils/index.js';
|
|
5
|
-
import { logger, YamlEditor } from 'node-karin';
|
|
6
|
-
class Cfg {
|
|
7
|
-
/** 配置文件跟路径 */
|
|
8
|
-
dir;
|
|
9
|
-
/** 默认配置文件根路径 */
|
|
10
|
-
defdir;
|
|
11
|
-
/** 缓存 不经常用的不建议缓存 */
|
|
12
|
-
change;
|
|
13
|
-
/** 监听文件 */
|
|
14
|
-
watcher;
|
|
15
|
-
constructor() {
|
|
16
|
-
this.dir = `./config/plugin/${basename}`;
|
|
17
|
-
this.defdir = `${dirPath}/config`;
|
|
18
|
-
this.change = new Map();
|
|
19
|
-
this.watcher = new Map();
|
|
20
|
-
this.initCfg();
|
|
21
|
-
}
|
|
22
|
-
/** 初始化配置 */
|
|
23
|
-
async initCfg() {
|
|
24
|
-
/** 读取默认配置的所有yaml */
|
|
25
|
-
const files = fs.readdirSync(this.defdir).filter(file => file.endsWith('.yaml'));
|
|
26
|
-
for (const file of files) {
|
|
27
|
-
const dirPath = `${this.dir}/${file}`;
|
|
28
|
-
const defPath = `${this.defdir}/${file}`;
|
|
29
|
-
if (!fs.existsSync(dirPath))
|
|
30
|
-
fs.copyFileSync(defPath, dirPath);
|
|
31
|
-
}
|
|
32
|
-
// this.updateYaml(`${this.dir}/config.yaml`, [
|
|
33
|
-
// { key: 'a', val: 'value', comment: '实例配置1', type: true },
|
|
34
|
-
// { key: 'b.c', val: 'Value', comment: '实例配置2', type: true },
|
|
35
|
-
// ])
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* 更新yaml文件
|
|
39
|
-
* @param filePath - 文件路径
|
|
40
|
-
* @param settings - 设置项
|
|
41
|
-
*/
|
|
42
|
-
updateYaml(filePath, settings) {
|
|
43
|
-
let yaml = new YamlEditor(filePath);
|
|
44
|
-
/** 先添加内容 */
|
|
45
|
-
settings.forEach(({ key, val }) => {
|
|
46
|
-
try {
|
|
47
|
-
if (!yaml.has(key))
|
|
48
|
-
yaml.set(key, val);
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
logger.error(`[common] 更新yaml文件时出错:${error.stack || error.message || error}`);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
/** 先保存 */
|
|
55
|
-
yaml.save();
|
|
56
|
-
/** 重新解析 再次写入注释 直接写入注释会报错 写入的不是node节点模式 */
|
|
57
|
-
yaml = new YamlEditor(filePath);
|
|
58
|
-
settings.forEach(({ key, comment, type }) => {
|
|
59
|
-
try {
|
|
60
|
-
yaml.comment(key, comment, type);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
logger.error(`[common] 更新yaml文件时出错:${error.stack || error.message || error}`);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
yaml.save();
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* 基本配置
|
|
70
|
-
*/
|
|
71
|
-
get Config() {
|
|
72
|
-
const key = 'change.config';
|
|
73
|
-
const res = this.change.get(key);
|
|
74
|
-
/** 取缓存 */
|
|
75
|
-
if (res)
|
|
76
|
-
return res;
|
|
77
|
-
/** 取配置 */
|
|
78
|
-
const config = this.getYaml('config', 'config', true);
|
|
79
|
-
const defSet = this.getYaml('defSet', 'config', false);
|
|
80
|
-
const data = {
|
|
81
|
-
...defSet,
|
|
82
|
-
...config,
|
|
83
|
-
default: { ...defSet?.default, ...config?.default },
|
|
84
|
-
};
|
|
85
|
-
/** 缓存 */
|
|
86
|
-
this.change.set(key, data);
|
|
87
|
-
return data;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* 获取指定Bot配置
|
|
91
|
-
* @param {string} appID - Bot的AppID
|
|
92
|
-
*/
|
|
93
|
-
getBotConfig(appID) {
|
|
94
|
-
if (!appID)
|
|
95
|
-
throw new Error('appID不能为空');
|
|
96
|
-
const key = `change.config.${appID}`;
|
|
97
|
-
const res = this.change.get(key);
|
|
98
|
-
/** 取缓存 */
|
|
99
|
-
if (res)
|
|
100
|
-
return res;
|
|
101
|
-
/** 所有配置 */
|
|
102
|
-
const allCfg = this.Config;
|
|
103
|
-
/** 取不到 */
|
|
104
|
-
if (!allCfg?.accounts?.[appID])
|
|
105
|
-
return null;
|
|
106
|
-
/** 单个账号配置 */
|
|
107
|
-
const botCfg = allCfg.accounts[appID];
|
|
108
|
-
/** 全局默认配置 */
|
|
109
|
-
const defCfg = allCfg.default;
|
|
110
|
-
/** 对regex进行处理 */
|
|
111
|
-
const regex = Array.isArray(botCfg.regex) ? botCfg.regex : defCfg.regex;
|
|
112
|
-
/** 对表达式进行new RegExp处理 */
|
|
113
|
-
const regexData = regex.map(({ reg, rep }) => {
|
|
114
|
-
return {
|
|
115
|
-
reg: reg instanceof RegExp ? reg : new RegExp(reg),
|
|
116
|
-
rep,
|
|
117
|
-
};
|
|
118
|
-
});
|
|
119
|
-
const data = {
|
|
120
|
-
...defCfg,
|
|
121
|
-
...botCfg,
|
|
122
|
-
sendMode: Number(botCfg.sendMode) || defCfg.sendMode,
|
|
123
|
-
regex: regexData,
|
|
124
|
-
};
|
|
125
|
-
/** 缓存 */
|
|
126
|
-
this.change.set(key, data);
|
|
127
|
-
return data;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* package
|
|
131
|
-
*/
|
|
132
|
-
get package() {
|
|
133
|
-
const data = fs.readFileSync(dirPath + '/package.json', 'utf8');
|
|
134
|
-
const pkg = JSON.parse(data);
|
|
135
|
-
return pkg;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* 获取配置yaml
|
|
139
|
-
*/
|
|
140
|
-
getYaml(type, name, isWatch = false) {
|
|
141
|
-
/** 文件路径 */
|
|
142
|
-
const file = type === 'defSet' ? `${this.defdir}/${name}.yaml` : `${this.dir}/${name}.yaml`;
|
|
143
|
-
/** 读取文件 */
|
|
144
|
-
const data = Yaml.parse(fs.readFileSync(file, 'utf8'));
|
|
145
|
-
/** 监听文件 */
|
|
146
|
-
if (isWatch)
|
|
147
|
-
this.watch(type, name, file);
|
|
148
|
-
return data;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* 监听配置文件
|
|
152
|
-
* @param {'defSet'|'config'} type 类型
|
|
153
|
-
* @param {string} name 文件名称 不带后缀
|
|
154
|
-
* @param {string} file 文件路径
|
|
155
|
-
*/
|
|
156
|
-
async watch(type, name, file) {
|
|
157
|
-
const key = `change.${name}`;
|
|
158
|
-
/** 已经监听过了 */
|
|
159
|
-
const res = this.change.get(key);
|
|
160
|
-
if (res)
|
|
161
|
-
return true;
|
|
162
|
-
/** 监听文件 */
|
|
163
|
-
const watcher = chokidar.watch(file);
|
|
164
|
-
/** 监听文件变化 */
|
|
165
|
-
watcher.on('change', () => {
|
|
166
|
-
this.change.delete(key);
|
|
167
|
-
logger.mark(`[修改配置文件][${type}][${name}]`);
|
|
168
|
-
});
|
|
169
|
-
/** 缓存 防止重复监听 */
|
|
170
|
-
this.watcher.set(key, watcher);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* 配置文件
|
|
175
|
-
*/
|
|
176
|
-
export const config = new Cfg();
|
package/lib/utils/dir.d.ts
DELETED
package/lib/utils/dir.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
/** 当前文件的绝对路径 */
|
|
5
|
-
const filePath = fileURLToPath(import.meta.url).replace(/\\/g, '/');
|
|
6
|
-
/** 插件包绝对路径 */
|
|
7
|
-
const dirPath = path.resolve(filePath, '../../../');
|
|
8
|
-
/** 插件包的名称 */
|
|
9
|
-
const basename = JSON.parse(fs.readFileSync(`${dirPath}/package.json`, 'utf-8')).name;
|
|
10
|
-
export { dirPath, basename };
|
package/lib/utils/index.d.ts
DELETED
package/lib/utils/index.js
DELETED