@karinjs/adapter-qqbot 1.0.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.
@@ -0,0 +1,328 @@
1
+ import { QQBotApi } from './api.js';
2
+ import { markdownTemplate, markdownRaw } from './markdown.js';
3
+ import { common, common as Common, config as Config, } from '../utils/index.js';
4
+ import { logger, listener, segment, KarinMessage, } from 'node-karin';
5
+ /**
6
+ * - QQBot适配器
7
+ */
8
+ export class AdapterQQBot {
9
+ super;
10
+ socket;
11
+ /** 账号配置类型定义 */
12
+ #config;
13
+ account;
14
+ adapter;
15
+ version;
16
+ markdown;
17
+ constructor(config) {
18
+ this.#config = config;
19
+ const { appId, secret, ...restConfig } = this.#config;
20
+ this.markdown = restConfig;
21
+ this.account = { uid: appId, uin: appId, name: '' };
22
+ this.adapter = { id: 'QQ', name: 'QQBot', type: 'internal', sub_type: 'internal', start_time: Date.now(), connect: '', index: 0 };
23
+ this.version = { name: 'QQBot', app_name: 'QQBot', version: Config.package.version };
24
+ this.super = new QQBotApi(this.#config);
25
+ switch (this.markdown.sendMode) {
26
+ /** 原生 */
27
+ case 1: {
28
+ this._sendNsg = async (elements, type, openid, message_id) => await markdownRaw({ bot: this, data: elements, type, openid, message_id });
29
+ return;
30
+ }
31
+ /** 旧图文模板 */
32
+ case 3: {
33
+ this._sendNsg = async (elements, type, openid, message_id) => await markdownTemplate({ bot: this, data: elements, type, openid, message_id });
34
+ return;
35
+ }
36
+ /** 纯文 现已不能申请 */
37
+ case 4: {
38
+ // 后续处理
39
+ return;
40
+ }
41
+ default: {
42
+ logger.warn('后续处理');
43
+ }
44
+ }
45
+ }
46
+ async init() {
47
+ await this.super.getAccessToken();
48
+ await this.super.getWssUrl();
49
+ this.super.createWss();
50
+ this.super.wss.on("GROUP_AT_MESSAGE_CREATE" /* QQBotEventType.GROUP_AT_MESSAGE_CREATE */, (data) => {
51
+ const user_id = data.d.author.id;
52
+ const group_id = data.d.group_id;
53
+ const time = new Date(data.d.timestamp).getTime();
54
+ const message = {
55
+ event: "message" /* EventType.Message */,
56
+ sub_event: "group_message" /* MessageSubType.GroupMessage */,
57
+ raw_event: data,
58
+ event_id: data.id,
59
+ self_id: this.account.uid,
60
+ user_id,
61
+ time,
62
+ message_id: data.d.id,
63
+ message_seq: data.s,
64
+ sender: {
65
+ uid: user_id,
66
+ uin: user_id,
67
+ nick: '',
68
+ role: "unknown" /* Role.Unknown */,
69
+ },
70
+ elements: this.AdapterConvertKarin(data),
71
+ contact: {
72
+ scene: "group" /* Scene.Group */,
73
+ peer: group_id,
74
+ sub_peer: '',
75
+ },
76
+ group_id,
77
+ raw_message: '',
78
+ };
79
+ const e = new KarinMessage(message);
80
+ e.bot = this;
81
+ e.replyCallback = async (elements) => await this._sendNsg(elements, "groups" /* PathType.Groups */, e.contact.peer, e.message_id);
82
+ listener.emit('message', e);
83
+ });
84
+ this.super.wss.on("C2C_MESSAGE_CREATE" /* QQBotEventType.C2C_MESSAGE_CREATE */, (data) => {
85
+ const user_id = data.d.author.user_openid;
86
+ const time = new Date(data.d.timestamp).getTime();
87
+ const message = {
88
+ event: "message" /* EventType.Message */,
89
+ sub_event: "private_message" /* MessageSubType.PrivateMessage */,
90
+ raw_event: data,
91
+ event_id: data.id,
92
+ self_id: this.account.uid,
93
+ user_id,
94
+ time,
95
+ message_id: data.d.id,
96
+ message_seq: data.s,
97
+ sender: {
98
+ uid: user_id,
99
+ uin: user_id,
100
+ nick: '',
101
+ role: "unknown" /* Role.Unknown */,
102
+ },
103
+ elements: this.AdapterConvertKarin(data),
104
+ contact: {
105
+ scene: "friend" /* Scene.Private */,
106
+ peer: user_id,
107
+ sub_peer: '',
108
+ },
109
+ group_id: '',
110
+ raw_message: '',
111
+ };
112
+ const e = new KarinMessage(message);
113
+ e.bot = this;
114
+ e.replyCallback = async (elements) => await this._sendNsg(elements, "users" /* PathType.Friends */, e.contact.peer, e.message_id);
115
+ listener.emit('message', e);
116
+ });
117
+ this.super.wss.on('start', () => {
118
+ this.account.name = this.super.nick;
119
+ this.logger('info', `建立连接成功: ${this.account.name}`);
120
+ /** 注册bot */
121
+ const index = listener.addBot({ type: this.adapter.type, bot: this });
122
+ if (index)
123
+ this.adapter.index = index;
124
+ });
125
+ /** 等待建立连接 注册Bot */
126
+ }
127
+ async _sendNsg(elements, type, openid, message_id) {
128
+ return await this.KarinConvertAdapter(elements, type, openid, message_id);
129
+ }
130
+ get self_id() {
131
+ return this.account.uid;
132
+ }
133
+ logger(level, ...args) {
134
+ this.super.logger(level, ...args);
135
+ }
136
+ async GetVersion() {
137
+ const data = this.version;
138
+ delete data.name;
139
+ return data;
140
+ }
141
+ /**
142
+ * QQBot转karin
143
+ * @return karin格式消息
144
+ * */
145
+ AdapterConvertKarin(event) {
146
+ const elements = [];
147
+ const data = event.d;
148
+ /** 检查是否存在图片 */
149
+ for (const v of data.attachments || []) {
150
+ elements.push(segment.image(v.url, {
151
+ name: v.filename,
152
+ // size: v.size,
153
+ height: v.height,
154
+ width: v.width,
155
+ file_type: 'original',
156
+ }));
157
+ }
158
+ const regex = /<faceType=\d+,faceId="\d+",ext="[^"]+">|[^<]+/g;
159
+ const result = data.content.match(regex) || [data.content];
160
+ result.forEach(v => {
161
+ if (v.startsWith('<faceType=')) {
162
+ const Match = v.match(/faceId="(\d+)"/);
163
+ elements.push(segment.face(Number(Match[1])));
164
+ }
165
+ else {
166
+ elements.push(segment.text(v));
167
+ }
168
+ });
169
+ return elements;
170
+ }
171
+ /**
172
+ * karin转qqbot
173
+ * @param data karin格式消息
174
+ * */
175
+ async KarinConvertAdapter(data, type, openid, message_id) {
176
+ let seq = 0;
177
+ /** 待发送列表 */
178
+ const send_list = [];
179
+ /** 采用并发上传 */
180
+ await Promise.all(data.map(async (i, index) => {
181
+ const results = [];
182
+ switch (i.type) {
183
+ case 'text': {
184
+ const qr = await Common.getQQBotText(i.text);
185
+ results.push({ index, options: this.super.buildText(qr.text, message_id, ++seq) });
186
+ if (qr.data) {
187
+ const { file_info } = await this.super.uploadMedia(openid, type, qr.data.base64, 1 /* FileType.Image */);
188
+ results.push({ index, options: this.super.buildMedia(file_info, message_id, ++seq) });
189
+ }
190
+ break;
191
+ }
192
+ case 'reply':
193
+ // 新增一个字段来进行传参进行被动回复事件更合适
194
+ // elements.push({ type: 'reply', data: { id: i.message_id } })
195
+ break;
196
+ case 'record': {
197
+ const file = await common.voiceToSilk(i.file);
198
+ /** 上传 */
199
+ const { file_info } = await this.super.uploadMedia(openid, type, file, 3 /* FileType.Record */);
200
+ /** 构建发送参数 */
201
+ results.push({ index, options: this.super.buildMedia(file_info, message_id, ++seq) });
202
+ break;
203
+ }
204
+ case 'image':
205
+ case 'video':
206
+ case 'file': {
207
+ const map = {
208
+ image: 1 /* FileType.Image */,
209
+ video: 2 /* FileType.Video */,
210
+ file: 4 /* FileType.File */,
211
+ };
212
+ /** 上传 */
213
+ const { file_info } = await this.super.uploadMedia(openid, type, i.file, map[i.type]);
214
+ /** 构建发送参数 */
215
+ results.push({ index, options: this.super.buildMedia(file_info, message_id, ++seq) });
216
+ break;
217
+ }
218
+ // 不支持的消息类型
219
+ default:
220
+ break;
221
+ }
222
+ return results;
223
+ })).then((allResults) => {
224
+ allResults.flat().sort((a, b) => a.index - b.index).forEach(result => {
225
+ send_list.push(result.options);
226
+ });
227
+ });
228
+ const result = {
229
+ message_id: '',
230
+ message_time: 0,
231
+ raw_data: [],
232
+ };
233
+ /** 并发发送 */
234
+ await Promise.all(send_list.map(async (v, index) => {
235
+ index !== 0 && await new Promise(resolve => setTimeout(resolve, index * 100));
236
+ const res = await this.super.sendMessage(openid, type, v);
237
+ result.raw_data.push(res);
238
+ }));
239
+ result.message_id = result.raw_data[0].id;
240
+ result.message_time = result.raw_data[0].time;
241
+ return result;
242
+ }
243
+ async SendMessage(_contact, elements) {
244
+ const text = [];
245
+ for (const v of elements) {
246
+ switch (v.type) {
247
+ case 'at':
248
+ text.push(`@${v.uid}`);
249
+ break;
250
+ case 'face':
251
+ text.push(`[表情:${v.id}]`);
252
+ break;
253
+ case 'text':
254
+ text.push(v.text);
255
+ break;
256
+ case 'image':
257
+ // text.push(await this.#MsgToFile(v.type, v.file))
258
+ break;
259
+ default:
260
+ text.push(`[未知消息类型:${JSON.stringify(v)}]`);
261
+ }
262
+ }
263
+ this.logger('info', `${logger.green('Send private input: ')}${text.join('')}`);
264
+ return { message_id: 'input' };
265
+ }
266
+ getAvatarUrl() {
267
+ return 'https://p.qlogo.cn/gh/967068507/967068507/0';
268
+ }
269
+ getGroupAvatar() {
270
+ return 'https://p.qlogo.cn/gh/967068507/967068507/0';
271
+ }
272
+ async GetCurrentAccount() {
273
+ return { account_uid: 'input', account_uin: 'input', account_name: 'input' };
274
+ }
275
+ async GetEssenceMessageList() { throw new Error('Method not implemented.'); }
276
+ async DownloadForwardMessage() { throw new Error('Method not implemented.'); }
277
+ async SetEssenceMessage() { throw new Error('Method not implemented.'); }
278
+ async DeleteEssenceMessage() { throw new Error('Method not implemented.'); }
279
+ async SetFriendApplyResult() { throw new Error('Method not implemented.'); }
280
+ async SetGroupApplyResult() { throw new Error('Method not implemented.'); }
281
+ async SetInvitedJoinGroupResult() { throw new Error('Method not implemented.'); }
282
+ async ReactMessageWithEmoji() { throw new Error('Method not implemented.'); }
283
+ async UploadPrivateFile() { throw new Error('Method not implemented.'); }
284
+ async UploadGroupFile() { throw new Error('Method not implemented.'); }
285
+ async UploadForwardMessage() { throw new Error('Method not implemented.'); }
286
+ async sendForwardMessage() { throw new Error('Method not implemented.'); }
287
+ async SendMessageByResId() { throw new Error('Method not implemented.'); }
288
+ async RecallMessage() { throw new Error('Method not implemented.'); }
289
+ async GetMessage() { throw new Error('Method not implemented.'); }
290
+ async GetHistoryMessage() { throw new Error('Method not implemented.'); }
291
+ async VoteUser() { throw new Error('Method not implemented.'); }
292
+ async KickMember() { throw new Error('Method not implemented.'); }
293
+ async BanMember() { throw new Error('Method not implemented.'); }
294
+ async SetGroupWholeBan() { throw new Error('Method not implemented.'); }
295
+ async SetGroupAdmin() { throw new Error('Method not implemented.'); }
296
+ async ModifyMemberCard() { throw new Error('Method not implemented.'); }
297
+ async ModifyGroupName() { throw new Error('Method not implemented.'); }
298
+ async LeaveGroup() { throw new Error('Method not implemented.'); }
299
+ async SetGroupUniqueTitle() { throw new Error('Method not implemented.'); }
300
+ async GetStrangerProfileCard() { throw new Error('Method not implemented.'); }
301
+ async GetFriendList() { throw new Error('Method not implemented.'); }
302
+ async GetGroupInfo() { throw new Error('Method not implemented.'); }
303
+ async GetGroupList() { throw new Error('Method not implemented.'); }
304
+ async GetGroupMemberInfo() { throw new Error('Method not implemented.'); }
305
+ async GetGroupMemberList() { throw new Error('Method not implemented.'); }
306
+ async GetGroupHonor() { throw new Error('Method not implemented.'); }
307
+ async DownloadFile() { throw new Error('Method not implemented.'); }
308
+ async CreateFolder() { throw new Error('Method not implemented.'); }
309
+ async RenameFolder() { throw new Error('Method not implemented.'); }
310
+ async DeleteFolde() { throw new Error('Method not implemented.'); }
311
+ async DeleteFolder() { throw new Error('Method not implemented.'); }
312
+ async UploadFile() { throw new Error('Method not implemented.'); }
313
+ async DeleteFile() { throw new Error('Method not implemented.'); }
314
+ async GetFileSystemInfo() { throw new Error('Method not implemented.'); }
315
+ async GetFileList() { throw new Error('Method not implemented.'); }
316
+ async ModifyGroupRemark() { throw new Error('Method not implemented.'); }
317
+ async GetRemainCountAtAll() { throw new Error('Method not implemented.'); }
318
+ async GetProhibitedUserList() { throw new Error('Method not implemented.'); }
319
+ async PokeMember() { throw new Error('Method not implemented.'); }
320
+ async SetMessageReaded() { throw new Error('Method not implemented.'); }
321
+ }
322
+ const list = Object.keys(Config.Config.accounts).filter(v => v !== 'default');
323
+ for (const v of list) {
324
+ const config = Config.getBotConfig(v);
325
+ if (!config)
326
+ continue;
327
+ new AdapterQQBot(config).init();
328
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 计算事件名称数组对应的 intents 的值
3
+ * @param events 事件名称数组
4
+ * @returns intents 的值
5
+ */
6
+ export declare function Intents(events: string[]): number;
@@ -0,0 +1,57 @@
1
+ const eventToIntentMap = {
2
+ GUILD_CREATE: 1 << 0,
3
+ GUILD_UPDATE: 1 << 0,
4
+ GUILD_DELETE: 1 << 0,
5
+ CHANNEL_CREATE: 1 << 0,
6
+ CHANNEL_UPDATE: 1 << 0,
7
+ CHANNEL_DELETE: 1 << 0,
8
+ GUILD_MEMBER_ADD: 1 << 1,
9
+ GUILD_MEMBER_UPDATE: 1 << 1,
10
+ GUILD_MEMBER_REMOVE: 1 << 1,
11
+ MESSAGE_CREATE: 1 << 9,
12
+ MESSAGE_DELETE: 1 << 9,
13
+ MESSAGE_REACTION_ADD: 1 << 10,
14
+ MESSAGE_REACTION_REMOVE: 1 << 10,
15
+ DIRECT_MESSAGE_CREATE: 1 << 12,
16
+ DIRECT_MESSAGE_DELETE: 1 << 12,
17
+ C2C_MESSAGE_CREATE: 1 << 25,
18
+ FRIEND_ADD: 1 << 25,
19
+ FRIEND_DEL: 1 << 25,
20
+ C2C_MSG_REJECT: 1 << 25,
21
+ C2C_MSG_RECEIVE: 1 << 25,
22
+ GROUP_AT_MESSAGE_CREATE: 1 << 25,
23
+ GROUP_ADD_ROBOT: 1 << 25,
24
+ GROUP_DEL_ROBOT: 1 << 25,
25
+ GROUP_MSG_REJECT: 1 << 25,
26
+ GROUP_MSG_RECEIVE: 1 << 25,
27
+ INTERACTION_CREATE: 1 << 26,
28
+ MESSAGE_AUDIT_PASS: 1 << 27,
29
+ MESSAGE_AUDIT_REJECT: 1 << 27,
30
+ FORUM_THREAD_CREATE: 1 << 28,
31
+ FORUM_THREAD_UPDATE: 1 << 28,
32
+ FORUM_THREAD_DELETE: 1 << 28,
33
+ FORUM_POST_CREATE: 1 << 28,
34
+ FORUM_POST_DELETE: 1 << 28,
35
+ FORUM_REPLY_CREATE: 1 << 28,
36
+ FORUM_REPLY_DELETE: 1 << 28,
37
+ FORUM_PUBLISH_AUDIT_RESULT: 1 << 28,
38
+ AUDIO_START: 1 << 29,
39
+ AUDIO_FINISH: 1 << 29,
40
+ AUDIO_ON_MIC: 1 << 29,
41
+ AUDIO_OFF_MIC: 1 << 29,
42
+ AT_MESSAGE_CREATE: 1 << 30,
43
+ PUBLIC_MESSAGE_DELETE: 1 << 30,
44
+ };
45
+ /**
46
+ * 计算事件名称数组对应的 intents 的值
47
+ * @param events 事件名称数组
48
+ * @returns intents 的值
49
+ */
50
+ export function Intents(events) {
51
+ return events.reduce((acc, event) => {
52
+ const intent = eventToIntentMap[event];
53
+ if (intent !== undefined)
54
+ acc |= intent;
55
+ return acc;
56
+ }, 0);
57
+ }
@@ -0,0 +1,23 @@
1
+ import { AdapterQQBot } from './index.js';
2
+ import { PathType } from '../types/index.js';
3
+ import { KarinElement, ReplyReturn } from 'node-karin';
4
+ /**
5
+ * 图文模板
6
+ */
7
+ export declare function markdownTemplate(Opt: {
8
+ bot: AdapterQQBot;
9
+ data: Array<KarinElement>;
10
+ type: PathType;
11
+ openid: string;
12
+ message_id?: string;
13
+ }): Promise<ReplyReturn>;
14
+ /**
15
+ * 原生markdown
16
+ */
17
+ export declare function markdownRaw(Opt: {
18
+ bot: AdapterQQBot;
19
+ data: Array<KarinElement>;
20
+ type: PathType;
21
+ openid: string;
22
+ message_id?: string;
23
+ }): Promise<ReplyReturn>;
@@ -0,0 +1,251 @@
1
+ import path from 'path';
2
+ import { common, common as Common } from '../utils/common.js';
3
+ import { handler, common as karinCommon, } from 'node-karin';
4
+ /**
5
+ * 图文模板
6
+ */
7
+ export async function markdownTemplate(Opt) {
8
+ const { bot, data, type, openid, message_id } = Opt;
9
+ /** 先对消息进行分类 */
10
+ const text = [];
11
+ const image = [];
12
+ /** 待发送列表 */
13
+ const send_list = [];
14
+ let seq = 0;
15
+ let keyboard;
16
+ const params = [];
17
+ const markdown = [];
18
+ const list = [];
19
+ const buttons = [];
20
+ const imgs = [];
21
+ const result = {
22
+ message_id: '',
23
+ message_time: 0,
24
+ raw_data: [],
25
+ };
26
+ const textFn = async () => {
27
+ text.sort((a, b) => a.index - b.index);
28
+ const data = Common.getQQBotButton(text.map(v => v.data.text).join(''));
29
+ /** 去除at全体 */
30
+ params.push({ key: bot.markdown.oldTemplate.textStartKey, values: [data.text.replace(/everyone/g, '').replace(/\n/g, '\r')] });
31
+ /** 按钮 */
32
+ buttons.push(...data.data);
33
+ };
34
+ const imageFn = async (i) => {
35
+ const { url, width, height } = await handler.call('qqbot.files', { file: i.data.file, type: 'image', name: '0.png' });
36
+ imgs.push({ index: i.index, url, width, height });
37
+ };
38
+ const mediaFn = async (file, type, name, FileType) => {
39
+ const { url } = await handler.call('qqbot.files', { file, type, name });
40
+ const { file_info } = await bot.super.uploadMedia(openid, type, url, FileType);
41
+ const opt = bot.super.buildMedia(file_info, message_id, seq);
42
+ const res = await bot.super.sendMessage(openid, type, opt);
43
+ result.raw_data.push(res);
44
+ };
45
+ await Promise.all(data.map(async (data, index) => {
46
+ switch (data.type) {
47
+ case 'text':
48
+ text.push({ index, data });
49
+ break;
50
+ case 'image':
51
+ image.push({ index, data });
52
+ break;
53
+ case 'record': {
54
+ const file = await common.voiceToSilk(data.file);
55
+ list.push(mediaFn(file, type, '0.silk', 3 /* FileType.Record */));
56
+ break;
57
+ }
58
+ case 'video': {
59
+ list.push(mediaFn(data.file, type, '0.mp4', 2 /* FileType.Video */));
60
+ break;
61
+ }
62
+ case 'file': {
63
+ /** 判断是不是一个本地路径 */
64
+ let ext = Date.now() + '';
65
+ if (path.isAbsolute(data.file))
66
+ ext = Date.now() + path.extname(data.file);
67
+ list.push(mediaFn(data.file, type, ext, 4 /* FileType.File */));
68
+ break;
69
+ }
70
+ case 'button': {
71
+ buttons.push(data);
72
+ break;
73
+ }
74
+ case 'markdown': {
75
+ text.push({ index, data: { type: 'text', text: data.content } });
76
+ break;
77
+ }
78
+ case 'markdown_tpl': {
79
+ markdown.push({ index, custom_template_id: data.custom_template_id, params: data.params });
80
+ break;
81
+ }
82
+ default:
83
+ break;
84
+ }
85
+ }));
86
+ if (text.length || image.length) {
87
+ /** 处理文本 */
88
+ if (text.length)
89
+ list.push(textFn());
90
+ /** 处理图片 */
91
+ image.map(i => list.push(imageFn(i)));
92
+ await Promise.all(list);
93
+ }
94
+ else {
95
+ await Promise.all(list);
96
+ }
97
+ if (buttons.length) {
98
+ const rows = [];
99
+ buttons.forEach(v => {
100
+ const button = karinCommon.buttonToQQBot(v);
101
+ rows.push(...button);
102
+ });
103
+ keyboard = { content: { rows: [...rows] } };
104
+ }
105
+ if (markdown.length) {
106
+ markdown.sort((a, b) => a.index - b.index);
107
+ await Promise.all(markdown.map(async (v, index) => {
108
+ const opt = bot.super.buildTplMarkdown(v.custom_template_id, v.params, keyboard, message_id, ++seq);
109
+ send_list.push({ index: v.index, opt });
110
+ }));
111
+ }
112
+ if (imgs.length) {
113
+ imgs.sort((a, b) => a.index - b.index);
114
+ /** 取第一个 */
115
+ const { url, width, height } = imgs[0];
116
+ params.push({ key: bot.markdown.oldTemplate.imgDescKey, values: [`img #${width}px #${height}px`] });
117
+ params.push({ key: bot.markdown.oldTemplate.imgUrlKey, values: [url] });
118
+ /** 去除第一个 */
119
+ imgs.shift();
120
+ send_list.push({ index: -1, opt: bot.super.buildTplMarkdown(bot.markdown.templateId, params, keyboard, message_id, ++seq) });
121
+ /** 剩下的构建为单独的md */
122
+ await Promise.all(imgs.map(async (img) => {
123
+ const { url, width, height } = img;
124
+ const params = [];
125
+ params.push({ key: bot.markdown.oldTemplate.imgDescKey, values: [`img #${width}px #${height}px`] });
126
+ params.push({ key: bot.markdown.oldTemplate.imgUrlKey, values: [url] });
127
+ send_list.push({ index: img.index, opt: bot.super.buildTplMarkdown(bot.markdown.templateId, params, keyboard, message_id, ++seq) });
128
+ }));
129
+ }
130
+ else {
131
+ params.length && send_list.push({ index: -1, opt: bot.super.buildTplMarkdown(bot.markdown.templateId, params, keyboard, message_id, ++seq) });
132
+ }
133
+ /** 并发发送 */
134
+ await Promise.all(send_list.map(async (v, index) => {
135
+ index !== 0 && await new Promise(resolve => setTimeout(resolve, index * 100));
136
+ const res = await bot.super.sendMessage(openid, type, v.opt);
137
+ result.raw_data.push(res);
138
+ }));
139
+ result.message_id = result.raw_data[0].id;
140
+ result.message_time = result.raw_data[0].time;
141
+ return result;
142
+ }
143
+ /**
144
+ * 原生markdown
145
+ */
146
+ export async function markdownRaw(Opt) {
147
+ const { bot, data, type, openid, message_id } = Opt;
148
+ /** 待发送列表 */
149
+ const send_list = [];
150
+ let seq = 0;
151
+ const list = [];
152
+ const result = {
153
+ message_id: '',
154
+ message_time: 0,
155
+ raw_data: [],
156
+ };
157
+ let keyboard;
158
+ const buttons = [];
159
+ const message = [];
160
+ const markdown_tpl = [];
161
+ const imageFn = async (index, data) => {
162
+ const { url, width, height } = await handler.call('qqbot.files', { file: data.file, type: 'image', name: '0.png' });
163
+ message.push({ index, data: `![img #${width}px #${height}px](${url})` });
164
+ };
165
+ /** 富媒体消息直接发送 无需和正常消息走 */
166
+ const mediaFn = async (file, type, name, FileType) => {
167
+ const { url } = await handler.call('qqbot.files', { file, type, name });
168
+ const { file_info } = await bot.super.uploadMedia(openid, type, url, FileType);
169
+ const opt = bot.super.buildMedia(file_info, message_id, seq);
170
+ const res = await bot.super.sendMessage(openid, type, opt);
171
+ result.raw_data.push(res);
172
+ };
173
+ await Promise.all(data.map(async (data, index) => {
174
+ switch (data.type) {
175
+ case 'text': {
176
+ /** 先提取掉url 随后放出按钮中 */
177
+ const res = Common.getQQBotButton(data.text);
178
+ buttons.push(...res.data);
179
+ message.push({ index, data: res.text.replace(/everyone/g, '').replace(/\n/g, '\r') });
180
+ break;
181
+ }
182
+ case 'image':
183
+ imageFn(index, data);
184
+ break;
185
+ case 'record': {
186
+ const file = await common.voiceToSilk(data.file);
187
+ list.push(mediaFn(file, type, '0.silk', 3 /* FileType.Record */));
188
+ break;
189
+ }
190
+ case 'video': {
191
+ list.push(mediaFn(data.file, type, '0.mp4', 2 /* FileType.Video */));
192
+ break;
193
+ }
194
+ case 'file': {
195
+ /** 判断是不是一个本地路径 */
196
+ let ext = Date.now() + '';
197
+ if (path.isAbsolute(data.file))
198
+ ext = Date.now() + path.extname(data.file);
199
+ list.push(mediaFn(data.file, type, ext, 4 /* FileType.File */));
200
+ break;
201
+ }
202
+ case 'button': {
203
+ buttons.push(data);
204
+ break;
205
+ }
206
+ case 'keyboard': {
207
+ buttons.push(data);
208
+ break;
209
+ }
210
+ case 'markdown': {
211
+ message.push({ index, data: data.content });
212
+ break;
213
+ }
214
+ // 原生下发模板多少有点问题...
215
+ case 'markdown_tpl': {
216
+ markdown_tpl.push({ index, data });
217
+ break;
218
+ }
219
+ default:
220
+ break;
221
+ }
222
+ }));
223
+ await Promise.all(list);
224
+ if (buttons.length) {
225
+ const rows = [];
226
+ buttons.forEach(v => {
227
+ const button = karinCommon.buttonToQQBot(v);
228
+ rows.push(...button);
229
+ });
230
+ keyboard = { content: { rows: [...rows] } };
231
+ }
232
+ if (markdown_tpl.length) {
233
+ await Promise.all(markdown_tpl.map(async (v, index) => {
234
+ const opt = bot.super.buildTplMarkdown(v.data.custom_template_id, v.data.params, keyboard, message_id, ++seq);
235
+ send_list.push({ index: v.index, opt });
236
+ }));
237
+ }
238
+ if (message.length) {
239
+ message.sort((a, b) => a.index - b.index);
240
+ send_list.push({ index: -1, opt: bot.super.buildRawMarkdown(message.map(v => v.data).join(''), keyboard, message_id, ++seq) });
241
+ }
242
+ /** 并发发送 */
243
+ await Promise.all(send_list.map(async (v, index) => {
244
+ index !== 0 && await new Promise(resolve => setTimeout(resolve, index * 100));
245
+ const res = await bot.super.sendMessage(openid, type, v.opt);
246
+ result.raw_data.push(res);
247
+ }));
248
+ result.message_id = result.raw_data[0].id;
249
+ result.message_time = result.raw_data[0].time;
250
+ return result;
251
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import './core/index.js';
2
+ import Jimp from 'jimp';
3
+ export default Jimp;
package/lib/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import './core/index.js';
2
+ import { logger, common } from 'node-karin';
3
+ import { basename, dirPath } from './utils/dir.js';
4
+ import Jimp from 'jimp';
5
+ const pkg = common.readJson(dirPath + '/package.json');
6
+ logger.info(`${logger.violet(`[插件:${pkg.version}]`)} ${logger.green(basename)} 初始化完成~`);
7
+ export default Jimp;