node-karin 0.10.5 → 0.10.7
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/lib/adapter/input/index.js +2 -2
- package/lib/adapter/onebot/11/convert.d.ts +2 -1
- package/lib/adapter/onebot/11/convert.js +18 -18
- package/lib/adapter/onebot/11/index.js +1 -1
- package/lib/core/listener/listener.d.ts +1 -2
- package/lib/core/listener/listener.js +1 -1
- package/lib/event/handler/message.js +6 -6
- package/lib/types/element/element.d.ts +71 -81
- package/lib/types/element/qqbot.d.ts +123 -0
- package/lib/types/element/qqbot.js +1 -0
- package/lib/types/event/message.d.ts +5 -0
- package/lib/types/event/message.js +2 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/type/global.d.ts +23 -0
- package/lib/types/type/global.js +1 -0
- package/lib/utils/common/common.d.ts +8 -1
- package/lib/utils/common/common.js +52 -15
- package/lib/utils/core/segment.d.ts +10 -10
- package/lib/utils/core/segment.js +20 -10
- package/lib/utils/tools/exec.js +1 -3
- package/package.json +1 -1
|
@@ -112,7 +112,7 @@ export class AdapterInput {
|
|
|
112
112
|
}
|
|
113
113
|
const buffer = await common.buffer(file);
|
|
114
114
|
// 生成文件名 根据type生成不同的文件后缀
|
|
115
|
-
const name = `${Date.now()}.${type === 'image' ? 'jpg' : type === '
|
|
115
|
+
const name = `${Date.now()}.${type === 'image' ? 'jpg' : type === 'record' ? 'mp3' : 'file'}`;
|
|
116
116
|
fs.writeFileSync(`./temp/input/${name}`, buffer);
|
|
117
117
|
return `[${type === 'image' ? '图片' : '语音'}: http://${ip}:${config.Server.http.port}/api/input?name=${name}&token=${this.token} ]`;
|
|
118
118
|
}
|
|
@@ -135,7 +135,7 @@ export class AdapterInput {
|
|
|
135
135
|
text.push(v.text);
|
|
136
136
|
break;
|
|
137
137
|
case 'image':
|
|
138
|
-
case '
|
|
138
|
+
case 'record':
|
|
139
139
|
text.push(await this.#MsgToFile(v.type, v.file));
|
|
140
140
|
break;
|
|
141
141
|
default:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AdapterOneBot11 } from './index.js';
|
|
1
2
|
import { KarinElement, OB11Segment } from '../../../types/index.js';
|
|
2
3
|
/**
|
|
3
4
|
* onebot11转karin
|
|
@@ -8,4 +9,4 @@ export declare function AdapterConvertKarin(data: Array<OB11Segment>): Array<Kar
|
|
|
8
9
|
* karin转onebot11
|
|
9
10
|
* @param data karin格式消息
|
|
10
11
|
*/
|
|
11
|
-
export declare function KarinConvertAdapter(data: Array<KarinElement
|
|
12
|
+
export declare function KarinConvertAdapter(data: Array<KarinElement>, bot: AdapterOneBot11): Array<OB11Segment>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
1
2
|
import { segment } from '../../../utils/index.js';
|
|
2
3
|
/**
|
|
3
4
|
* onebot11转karin
|
|
@@ -53,11 +54,22 @@ export function AdapterConvertKarin(data) {
|
|
|
53
54
|
}
|
|
54
55
|
return elements;
|
|
55
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* 处理非本地ws的文件
|
|
59
|
+
* @param file 文件路径
|
|
60
|
+
*/
|
|
61
|
+
function fileToBase64(file, bot) {
|
|
62
|
+
if (!bot || !file.startsWith('file://') || !bot.adapter.connect)
|
|
63
|
+
return file;
|
|
64
|
+
const list = ['127.0.0.1', 'localhost'];
|
|
65
|
+
const url = new URL(bot.adapter.connect);
|
|
66
|
+
return list.includes(url.hostname) ? `base64://${fs.readFileSync(file).toString('base64')}` : file;
|
|
67
|
+
}
|
|
56
68
|
/**
|
|
57
69
|
* karin转onebot11
|
|
58
70
|
* @param data karin格式消息
|
|
59
71
|
*/
|
|
60
|
-
export function KarinConvertAdapter(data) {
|
|
72
|
+
export function KarinConvertAdapter(data, bot) {
|
|
61
73
|
const elements = [];
|
|
62
74
|
for (const i of data) {
|
|
63
75
|
const type = i.type;
|
|
@@ -77,7 +89,7 @@ export function KarinConvertAdapter(data) {
|
|
|
77
89
|
case "image" /* OB11SegmentType.Image */:
|
|
78
90
|
case "video" /* OB11SegmentType.Video */:
|
|
79
91
|
case "file" /* OB11SegmentType.File */: {
|
|
80
|
-
elements.push({ type, data: { file: i.file } });
|
|
92
|
+
elements.push({ type, data: { file: fileToBase64(i.file, bot) } });
|
|
81
93
|
break;
|
|
82
94
|
}
|
|
83
95
|
case "xml" /* OB11SegmentType.Xml */: {
|
|
@@ -93,7 +105,7 @@ export function KarinConvertAdapter(data) {
|
|
|
93
105
|
break;
|
|
94
106
|
}
|
|
95
107
|
case "record" /* OB11SegmentType.Record */: {
|
|
96
|
-
elements.push({ type, data: { file: i.file, magic: i.magic || false } });
|
|
108
|
+
elements.push({ type, data: { file: fileToBase64(i.file, bot), magic: i.magic || false } });
|
|
97
109
|
break;
|
|
98
110
|
}
|
|
99
111
|
case "music" /* OB11SegmentType.Music */: {
|
|
@@ -106,21 +118,6 @@ export function KarinConvertAdapter(data) {
|
|
|
106
118
|
}
|
|
107
119
|
break;
|
|
108
120
|
}
|
|
109
|
-
case 'button': {
|
|
110
|
-
elements.push({ type: 'button', data: i.data });
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
case 'markdown': {
|
|
114
|
-
const { type, ...data } = i;
|
|
115
|
-
elements.push({ type, data: { ...data } });
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
case 'rows': {
|
|
119
|
-
for (const val of i.rows) {
|
|
120
|
-
elements.push({ type: 'button', data: val.data });
|
|
121
|
-
}
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
121
|
case "poke" /* OB11SegmentType.Poke */: {
|
|
125
122
|
elements.push({ type, data: { type: i.poke_type, id: i.id } });
|
|
126
123
|
break;
|
|
@@ -157,6 +154,9 @@ export function KarinConvertAdapter(data) {
|
|
|
157
154
|
elements.push({ type: 'weather', data: { city: i.city, type: i.type } });
|
|
158
155
|
break;
|
|
159
156
|
}
|
|
157
|
+
case 'button':
|
|
158
|
+
case 'markdown':
|
|
159
|
+
case 'keyboard':
|
|
160
160
|
default: {
|
|
161
161
|
elements.push(i);
|
|
162
162
|
break;
|
|
@@ -3,7 +3,7 @@ import { KarinAdapter, Contact, KarinElement } from '../../types/index.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* 监听器管理
|
|
5
5
|
*/
|
|
6
|
-
declare class Listeners extends EventEmitter {
|
|
6
|
+
export declare class Listeners extends EventEmitter {
|
|
7
7
|
#private;
|
|
8
8
|
/**
|
|
9
9
|
* Bot索引
|
|
@@ -103,4 +103,3 @@ declare class Listeners extends EventEmitter {
|
|
|
103
103
|
}
|
|
104
104
|
export declare const listener: Listeners;
|
|
105
105
|
export declare const Bot: Listeners;
|
|
106
|
-
export {};
|
|
@@ -108,8 +108,11 @@ export class MessageHandler extends EventBaseHandler {
|
|
|
108
108
|
logs.push(`[face:${val.id}]`);
|
|
109
109
|
break;
|
|
110
110
|
case 'video':
|
|
111
|
+
logs.push(`[video:${val.file}]`);
|
|
112
|
+
break;
|
|
111
113
|
case 'record':
|
|
112
|
-
|
|
114
|
+
this.e.record = val.file;
|
|
115
|
+
logs.push(`[record:${val.file}]`);
|
|
113
116
|
break;
|
|
114
117
|
case 'image':
|
|
115
118
|
this.e.image.push(val.file);
|
|
@@ -186,11 +189,8 @@ export class MessageHandler extends EventBaseHandler {
|
|
|
186
189
|
logs.push(`[markdown_tpl:${JSON.stringify(content)}]`);
|
|
187
190
|
break;
|
|
188
191
|
}
|
|
189
|
-
case '
|
|
190
|
-
|
|
191
|
-
for (const v of val.rows)
|
|
192
|
-
rows.push(JSON.stringify(v.data));
|
|
193
|
-
logs.push(`[rows:${JSON.stringify(rows)}]`);
|
|
192
|
+
case 'keyboard': {
|
|
193
|
+
logs.push(`[rows:${JSON.stringify(val.rows)}]`);
|
|
194
194
|
break;
|
|
195
195
|
}
|
|
196
196
|
case 'button':
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ElementType = 'text' | 'at' | 'face' | 'bubble_face' | 'reply' | 'image' | '
|
|
1
|
+
export type ElementType = 'text' | 'at' | 'face' | 'bubble_face' | 'reply' | 'image' | 'video' | 'basketball' | 'dice' | 'rps' | 'poke' | 'music' | 'weather' | 'location' | 'share' | 'gift' | 'market_face' | 'forward' | 'contact' | 'json' | 'xml' | 'file' | 'markdown' | 'markdown_tpl' | 'keyboard' | 'node' | 'rows' | 'record' | 'long_msg';
|
|
2
2
|
export interface Element {
|
|
3
3
|
/**
|
|
4
4
|
* - 元素类型
|
|
@@ -109,29 +109,6 @@ export interface ImageElement extends Element {
|
|
|
109
109
|
*/
|
|
110
110
|
file_type: 'show' | 'flash' | 'original';
|
|
111
111
|
}
|
|
112
|
-
/**
|
|
113
|
-
* - 语音元素
|
|
114
|
-
* @deprecated 即将废弃 请使用segment.record
|
|
115
|
-
*/
|
|
116
|
-
export interface VoiceElement extends Element {
|
|
117
|
-
type: 'voice';
|
|
118
|
-
/**
|
|
119
|
-
* - 语音文件url、路径或者base64
|
|
120
|
-
*/
|
|
121
|
-
file: string;
|
|
122
|
-
/**
|
|
123
|
-
* - 是否为魔法语音
|
|
124
|
-
*/
|
|
125
|
-
magic: boolean;
|
|
126
|
-
/**
|
|
127
|
-
* - 语音md5
|
|
128
|
-
*/
|
|
129
|
-
md5?: string;
|
|
130
|
-
/**
|
|
131
|
-
* - 语音名称
|
|
132
|
-
*/
|
|
133
|
-
name?: string;
|
|
134
|
-
}
|
|
135
112
|
/**
|
|
136
113
|
* - 语音元素
|
|
137
114
|
*/
|
|
@@ -489,68 +466,81 @@ export interface MarkdownElement extends Element {
|
|
|
489
466
|
content: string;
|
|
490
467
|
}
|
|
491
468
|
/**
|
|
492
|
-
*
|
|
469
|
+
* 单个按钮结构 这是karin的按钮结构 与qqbot的不同
|
|
470
|
+
*/
|
|
471
|
+
export interface Button {
|
|
472
|
+
/**
|
|
473
|
+
* - 按钮显示文本
|
|
474
|
+
*/
|
|
475
|
+
text: string;
|
|
476
|
+
/**
|
|
477
|
+
* - 按钮类型 不建议使用 此为预留字段
|
|
478
|
+
*/
|
|
479
|
+
type?: number;
|
|
480
|
+
/**
|
|
481
|
+
* - 是否为回调按钮
|
|
482
|
+
* @default false
|
|
483
|
+
*/
|
|
484
|
+
callback?: boolean;
|
|
485
|
+
/**
|
|
486
|
+
* - 跳转按钮
|
|
487
|
+
*/
|
|
488
|
+
link?: string;
|
|
489
|
+
/**
|
|
490
|
+
* - 操作相关的数据
|
|
491
|
+
*/
|
|
492
|
+
data?: string;
|
|
493
|
+
/**
|
|
494
|
+
* - 按钮点击后显示的文字,不传为text
|
|
495
|
+
*/
|
|
496
|
+
show?: string;
|
|
497
|
+
/**
|
|
498
|
+
* 按钮样式
|
|
499
|
+
* - 0-灰色线框
|
|
500
|
+
* - 1-蓝色线框
|
|
501
|
+
* - 2-特殊样式按钮
|
|
502
|
+
* - 3-红色文字
|
|
503
|
+
* - 4-白色填充
|
|
504
|
+
*/
|
|
505
|
+
style?: number;
|
|
506
|
+
/**
|
|
507
|
+
* - 点击按钮后直接自动发送 data
|
|
508
|
+
*/
|
|
509
|
+
enter?: boolean;
|
|
510
|
+
/**
|
|
511
|
+
* - 指令是否带引用回复本消息
|
|
512
|
+
*/
|
|
513
|
+
reply?: boolean;
|
|
514
|
+
/**
|
|
515
|
+
* - 是否仅群管理员可操作
|
|
516
|
+
*/
|
|
517
|
+
admin?: boolean;
|
|
518
|
+
/**
|
|
519
|
+
* - 有权限点击的用户UID列表 群聊、私聊
|
|
520
|
+
*/
|
|
521
|
+
list?: string[];
|
|
522
|
+
/**
|
|
523
|
+
* - 有权限点击的用户UID列表 频道
|
|
524
|
+
*/
|
|
525
|
+
role?: string[];
|
|
526
|
+
/**
|
|
527
|
+
* - 客户端不支持本 action 的时候,弹出的 toast 文案
|
|
528
|
+
*/
|
|
529
|
+
tips?: string;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* - 按钮 构建单行多个按钮
|
|
493
533
|
*/
|
|
494
534
|
export interface ButtonElement {
|
|
495
535
|
type: 'button';
|
|
496
|
-
data:
|
|
497
|
-
/**
|
|
498
|
-
* - 按钮显示文本
|
|
499
|
-
*/
|
|
500
|
-
text: string;
|
|
501
|
-
/**
|
|
502
|
-
* - 是否为回调按钮
|
|
503
|
-
* @default false
|
|
504
|
-
*/
|
|
505
|
-
callback?: boolean;
|
|
506
|
-
/**
|
|
507
|
-
* - 跳转按钮
|
|
508
|
-
*/
|
|
509
|
-
link?: string;
|
|
510
|
-
/**
|
|
511
|
-
* - 操作相关的数据
|
|
512
|
-
*/
|
|
513
|
-
data?: string;
|
|
514
|
-
/**
|
|
515
|
-
* - 按钮点击后显示的文字,不传为text
|
|
516
|
-
*/
|
|
517
|
-
show?: string;
|
|
518
|
-
/**
|
|
519
|
-
* - 按钮样式:0 灰色线框,1 蓝色线框
|
|
520
|
-
*/
|
|
521
|
-
style?: number;
|
|
522
|
-
/**
|
|
523
|
-
* - 点击按钮后直接自动发送 data
|
|
524
|
-
*/
|
|
525
|
-
enter?: boolean;
|
|
526
|
-
/**
|
|
527
|
-
* - 指令是否带引用回复本消息
|
|
528
|
-
*/
|
|
529
|
-
reply?: boolean;
|
|
530
|
-
/**
|
|
531
|
-
* - 是否仅群管理员可操作
|
|
532
|
-
*/
|
|
533
|
-
admin?: boolean;
|
|
534
|
-
/**
|
|
535
|
-
* - 有权限点击的用户UID列表 群聊、私聊
|
|
536
|
-
*/
|
|
537
|
-
list?: string[];
|
|
538
|
-
/**
|
|
539
|
-
* - 有权限点击的用户UID列表 频道
|
|
540
|
-
*/
|
|
541
|
-
role?: string[];
|
|
542
|
-
/**
|
|
543
|
-
* - 客户端不支持本 action 的时候,弹出的 toast 文案
|
|
544
|
-
*/
|
|
545
|
-
tips?: string;
|
|
546
|
-
};
|
|
536
|
+
data: Array<Button>;
|
|
547
537
|
}
|
|
548
538
|
/**
|
|
549
|
-
* - 按钮组
|
|
539
|
+
* - 按钮组 构建多行多个按钮
|
|
550
540
|
*/
|
|
551
|
-
export interface
|
|
552
|
-
type: '
|
|
553
|
-
rows: Array<
|
|
541
|
+
export interface KeyBoardElement extends Element {
|
|
542
|
+
type: 'keyboard';
|
|
543
|
+
rows: Array<Array<Button>>;
|
|
554
544
|
}
|
|
555
545
|
/**
|
|
556
546
|
* - 长消息元素
|
|
@@ -562,7 +552,7 @@ export interface LongMsgElement extends Element {
|
|
|
562
552
|
*/
|
|
563
553
|
id: string;
|
|
564
554
|
}
|
|
565
|
-
export type KarinElement = TextElement | AtElement | FaceElement | BubbleFaceElement | ReplyElement | ImageElement |
|
|
555
|
+
export type KarinElement = TextElement | AtElement | FaceElement | BubbleFaceElement | ReplyElement | ImageElement | VideoElement | BasketballElement | DiceElement | RpsElement | PokeElement | MusicElement | WeatherElement | LocationElement | ShareElement | GiftElement | MarketFaceElement | ForwardElement | ContactElement | JsonElement | XmlElement | FileElement | ButtonElement | KeyBoardElement | RecordElement | LongMsgElement | TplMarkdownElement | RawMarkdownElement;
|
|
566
556
|
/**
|
|
567
557
|
* - 构建自定义转发节点 此元素仅可通过专用接口发送 不支持混合发送
|
|
568
558
|
*/
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 原生Markdown消息结构
|
|
3
|
+
*/
|
|
4
|
+
export interface RawMarkdownType {
|
|
5
|
+
/** markdown文本 */
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 模板Markdown消息结构
|
|
10
|
+
*/
|
|
11
|
+
export interface TplMarkdownType {
|
|
12
|
+
/**
|
|
13
|
+
* - 模板ID
|
|
14
|
+
*/
|
|
15
|
+
custom_template_id: string;
|
|
16
|
+
/**
|
|
17
|
+
* - 模板参数
|
|
18
|
+
*/
|
|
19
|
+
params: Array<{
|
|
20
|
+
/**
|
|
21
|
+
* - 模板参数键名称
|
|
22
|
+
*/
|
|
23
|
+
key: string;
|
|
24
|
+
/**
|
|
25
|
+
* - 模板参数值
|
|
26
|
+
*/
|
|
27
|
+
values: Array<string>;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Markdown消息结构
|
|
32
|
+
* @param T 是否为原生结构
|
|
33
|
+
*/
|
|
34
|
+
export type MarkdownType<T extends boolean> = T extends true ? RawMarkdownType : TplMarkdownType;
|
|
35
|
+
/**
|
|
36
|
+
* 原生keyboard消息结构
|
|
37
|
+
*/
|
|
38
|
+
export interface RawKeyboardType {
|
|
39
|
+
content: {
|
|
40
|
+
rows: Array<{
|
|
41
|
+
buttons: Array<ButtonType>;
|
|
42
|
+
}>;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 模板keyboard消息结构
|
|
47
|
+
*/
|
|
48
|
+
export interface TplKeyboardType {
|
|
49
|
+
/** 模板ID */
|
|
50
|
+
id: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 按钮消息结构
|
|
54
|
+
*/
|
|
55
|
+
export interface ButtonType {
|
|
56
|
+
/** 按钮ID:在一个keyboard消息内设置唯一 */
|
|
57
|
+
id: string;
|
|
58
|
+
/** 按钮上的文字 */
|
|
59
|
+
render_data: {
|
|
60
|
+
/** 按钮上的文字 */
|
|
61
|
+
label: string;
|
|
62
|
+
/** 点击后按钮的上文字 */
|
|
63
|
+
visited_label: string;
|
|
64
|
+
/**
|
|
65
|
+
* 按钮样式
|
|
66
|
+
* - 0-灰色线框
|
|
67
|
+
* - 1-蓝色线框
|
|
68
|
+
* - 2-特殊样式按钮
|
|
69
|
+
* - 3-红色文字
|
|
70
|
+
* - 4-白色填充
|
|
71
|
+
*/
|
|
72
|
+
style: number;
|
|
73
|
+
};
|
|
74
|
+
/** 操作相关的数据 */
|
|
75
|
+
action: {
|
|
76
|
+
/** 设置 0 跳转按钮:http 或 小程序 客户端识别 scheme,设置 1 回调按钮:回调后台接口, data 传给后台,设置 2 指令按钮:自动在输入框插入 @bot data */
|
|
77
|
+
type: number;
|
|
78
|
+
/** 权限设置 */
|
|
79
|
+
permission: {
|
|
80
|
+
/** 0 指定用户可操作,1 仅管理者可操作,2 所有人可操作,3 指定身份组可操作(仅频道可用) */
|
|
81
|
+
type: number;
|
|
82
|
+
/** 有权限的用户 id 的列表 */
|
|
83
|
+
specify_user_ids?: string[];
|
|
84
|
+
/** 有权限的身份组 id 的列表(仅频道可用) */
|
|
85
|
+
specify_role_ids?: string[];
|
|
86
|
+
};
|
|
87
|
+
/** 操作相关的数据 */
|
|
88
|
+
data: string;
|
|
89
|
+
/** 指令按钮可用,指令是否带引用回复本消息,默认 false */
|
|
90
|
+
reply?: boolean;
|
|
91
|
+
/** 指令按钮可用,点击按钮后直接自动发送 data,默认 false */
|
|
92
|
+
enter?: boolean;
|
|
93
|
+
/** 本字段仅在指令按钮下有效,设置后后会忽略 action.enter 配置。
|
|
94
|
+
设置为 1 时 ,点击按钮自动唤起启手Q选图器,其他值暂无效果。
|
|
95
|
+
(仅支持手机端版本 8983+ 的单聊场景,桌面端不支持) */
|
|
96
|
+
anchor?: number;
|
|
97
|
+
/** 【已弃用】可操作点击的次数,默认不限 */
|
|
98
|
+
click_limit?: number;
|
|
99
|
+
/** 【已弃用】指令按钮可用,弹出子频道选择器,默认 false */
|
|
100
|
+
at_bot_show_channel_list?: boolean;
|
|
101
|
+
/** 客户端不支持本action的时候,弹出的toast文案 */
|
|
102
|
+
unsupport_tips: string;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Keyboard消息结构
|
|
107
|
+
* @param T 是否为原生结构
|
|
108
|
+
*/
|
|
109
|
+
export type KeyboardType<T extends boolean> = T extends true ? RawKeyboardType : TplKeyboardType;
|
|
110
|
+
/**
|
|
111
|
+
* Ark消息结构
|
|
112
|
+
*/
|
|
113
|
+
export interface ArkType {
|
|
114
|
+
/** 模版 id,管理端可获得或内邀申请获得 */
|
|
115
|
+
template_id: number;
|
|
116
|
+
/** {key: xxx, value: xxx},模版内变量与填充值的kv映射 */
|
|
117
|
+
kv: Array<{
|
|
118
|
+
obj_kv: Array<{
|
|
119
|
+
key: string;
|
|
120
|
+
value: string;
|
|
121
|
+
}>;
|
|
122
|
+
}>;
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -34,6 +34,10 @@ export interface KarinMessageType extends KarinEventType {
|
|
|
34
34
|
* - 消息图片
|
|
35
35
|
*/
|
|
36
36
|
image: string[];
|
|
37
|
+
/**
|
|
38
|
+
* - 语音url
|
|
39
|
+
*/
|
|
40
|
+
record: string;
|
|
37
41
|
file: any;
|
|
38
42
|
/**
|
|
39
43
|
* 是否atBot
|
|
@@ -88,6 +92,7 @@ export declare class KarinMessage implements KarinMessageType {
|
|
|
88
92
|
replyCallback: KarinMessageType['replyCallback'];
|
|
89
93
|
msg: KarinMessageType['msg'];
|
|
90
94
|
image: KarinMessageType['image'];
|
|
95
|
+
record: KarinMessageType['record'];
|
|
91
96
|
file: KarinMessageType['file'];
|
|
92
97
|
atBot: KarinMessageType['atBot'];
|
|
93
98
|
atAll: KarinMessageType['atAll'];
|
|
@@ -30,6 +30,7 @@ export class KarinMessage {
|
|
|
30
30
|
replyCallback;
|
|
31
31
|
msg;
|
|
32
32
|
image;
|
|
33
|
+
record;
|
|
33
34
|
file;
|
|
34
35
|
atBot;
|
|
35
36
|
atAll;
|
|
@@ -63,6 +64,7 @@ export class KarinMessage {
|
|
|
63
64
|
this.msg = '';
|
|
64
65
|
this.image = [];
|
|
65
66
|
this.file = {};
|
|
67
|
+
this.record = '';
|
|
66
68
|
this.atBot = false;
|
|
67
69
|
this.atAll = false;
|
|
68
70
|
this.at = [];
|
package/lib/types/index.d.ts
CHANGED
package/lib/types/index.js
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Logger as LoggerType } from 'log4js';
|
|
2
|
+
import { Logger } from '../../types/index.js';
|
|
3
|
+
import { Lang, Mode, Runner } from '../../cli/karin.js';
|
|
4
|
+
declare global {
|
|
5
|
+
namespace NodeJS {
|
|
6
|
+
interface ProcessEnv {
|
|
7
|
+
/** 重启次数 */
|
|
8
|
+
karin_app_start_count: string;
|
|
9
|
+
/** 是否为监听模式 yes=true no=false */
|
|
10
|
+
karin_app_watch: string;
|
|
11
|
+
/** 运行模式 "dev" | "prod" */
|
|
12
|
+
karin_app_mode: `${Mode}`;
|
|
13
|
+
/** 语言环境 "js" | "ts" */
|
|
14
|
+
karin_app_lang: `${Lang}`;
|
|
15
|
+
/** 运行器 "node" | "tsx" | "pm2" */
|
|
16
|
+
karin_app_runner: `${Runner}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
declare global {
|
|
21
|
+
var logger: LoggerType & Logger;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import { AxiosRequestConfig } from 'axios';
|
|
3
3
|
import { Readable } from 'stream';
|
|
4
|
-
import { dirName, fileName, KarinElement, NodeElement } from '../../types/index.js';
|
|
4
|
+
import { ButtonElement, ButtonType, dirName, fileName, KarinElement, NodeElement, KeyBoardElement } from '../../types/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* 常用方法
|
|
7
7
|
*/
|
|
@@ -174,6 +174,13 @@ declare class Common {
|
|
|
174
174
|
/** 需要写入的注释 */
|
|
175
175
|
comment: string;
|
|
176
176
|
}[]): void;
|
|
177
|
+
/**
|
|
178
|
+
* karin按钮转换为QQBot官方按钮 返回官方按钮结构
|
|
179
|
+
* @param button - 按钮
|
|
180
|
+
*/
|
|
181
|
+
buttonToQQBot(button: ButtonElement | KeyBoardElement): Array<{
|
|
182
|
+
buttons: Array<ButtonType>;
|
|
183
|
+
}>;
|
|
177
184
|
}
|
|
178
185
|
/**
|
|
179
186
|
* 常用方法
|
|
@@ -324,18 +324,10 @@ class Common {
|
|
|
324
324
|
* @param elements - 消息内容
|
|
325
325
|
*/
|
|
326
326
|
makeMessage(elements) {
|
|
327
|
-
/** 将msg格式化为数组 */
|
|
328
327
|
if (!Array.isArray(elements))
|
|
329
328
|
elements = [elements];
|
|
330
329
|
const message = [];
|
|
331
|
-
elements.forEach(v => {
|
|
332
|
-
if (typeof v === 'string') {
|
|
333
|
-
message.push(segment.text(v));
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
message.push(v);
|
|
337
|
-
}
|
|
338
|
-
});
|
|
330
|
+
elements.forEach(v => { typeof v === 'string' ? message.push(segment.text(v)) : message.push(v); });
|
|
339
331
|
return message;
|
|
340
332
|
}
|
|
341
333
|
/**
|
|
@@ -468,7 +460,6 @@ class Common {
|
|
|
468
460
|
break;
|
|
469
461
|
case 'video':
|
|
470
462
|
case 'image':
|
|
471
|
-
case 'voice':
|
|
472
463
|
case 'record':
|
|
473
464
|
case 'file': {
|
|
474
465
|
let file;
|
|
@@ -534,11 +525,8 @@ class Common {
|
|
|
534
525
|
logs.push(`[markdown_tpl:${JSON.stringify(content)}]`);
|
|
535
526
|
break;
|
|
536
527
|
}
|
|
537
|
-
case '
|
|
538
|
-
|
|
539
|
-
for (const v of val.rows)
|
|
540
|
-
rows.push(JSON.stringify(v.data));
|
|
541
|
-
logs.push(`[rows:${JSON.stringify(rows)}]`);
|
|
528
|
+
case 'keyboard': {
|
|
529
|
+
logs.push(`[rows:${JSON.stringify(val.rows)}]`);
|
|
542
530
|
break;
|
|
543
531
|
}
|
|
544
532
|
case 'button':
|
|
@@ -584,6 +572,55 @@ class Common {
|
|
|
584
572
|
});
|
|
585
573
|
yaml.save();
|
|
586
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* karin按钮转换为QQBot官方按钮 返回官方按钮结构
|
|
577
|
+
* @param button - 按钮
|
|
578
|
+
*/
|
|
579
|
+
buttonToQQBot(button) {
|
|
580
|
+
let id = 0;
|
|
581
|
+
const rows = [];
|
|
582
|
+
/** 格式化为多行 */
|
|
583
|
+
const list = [];
|
|
584
|
+
button.type === 'button' ? list.push(button.data) : list.push(...button.rows);
|
|
585
|
+
for (const row of list) {
|
|
586
|
+
const buttons = [];
|
|
587
|
+
for (const i of row) {
|
|
588
|
+
const type = i.link ? 0 : (i.callback ? 1 : i.type ?? 2);
|
|
589
|
+
const data = {
|
|
590
|
+
id: String(id),
|
|
591
|
+
render_data: {
|
|
592
|
+
label: i.text,
|
|
593
|
+
style: i.style ?? 0,
|
|
594
|
+
visited_label: i.show || i.text,
|
|
595
|
+
},
|
|
596
|
+
action: {
|
|
597
|
+
type,
|
|
598
|
+
data: i.data || i.link || i.text,
|
|
599
|
+
unsupport_tips: i.tips || '.',
|
|
600
|
+
permission: { type: 2 },
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
if (i.enter)
|
|
604
|
+
data.action.enter = true;
|
|
605
|
+
if (i.reply)
|
|
606
|
+
data.action.reply = true;
|
|
607
|
+
if (i.admin)
|
|
608
|
+
data.action.permission.type = 1;
|
|
609
|
+
if (i.list) {
|
|
610
|
+
data.action.permission.type = 0;
|
|
611
|
+
data.action.permission.specify_user_ids = i.list;
|
|
612
|
+
}
|
|
613
|
+
else if (i.role) {
|
|
614
|
+
data.action.permission.type = 3;
|
|
615
|
+
data.action.permission.specify_role_ids = i.role;
|
|
616
|
+
}
|
|
617
|
+
buttons.push(data);
|
|
618
|
+
id++;
|
|
619
|
+
}
|
|
620
|
+
rows.push({ buttons });
|
|
621
|
+
}
|
|
622
|
+
return rows;
|
|
623
|
+
}
|
|
587
624
|
}
|
|
588
625
|
/**
|
|
589
626
|
* 常用方法
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextElement, AtElement, ImageElement, FaceElement, BubbleFaceElement, ReplyElement, VideoElement, BasketballElement, DiceElement, RpsElement, PokeElement, MusicElement, WeatherElement, LocationElement, ShareElement, GiftElement, MarketFaceElement, ForwardElement, ContactElement, JsonElement, XmlElement, FileElement, ButtonElement, CustomMusicElemen, TplMarkdownElement, RawMarkdownElement, NodeElement, KarinElement, LongMsgElement, RecordElement } from '../../types/index.js';
|
|
1
|
+
import { TextElement, AtElement, ImageElement, FaceElement, BubbleFaceElement, ReplyElement, VideoElement, BasketballElement, DiceElement, RpsElement, PokeElement, MusicElement, WeatherElement, LocationElement, ShareElement, GiftElement, MarketFaceElement, ForwardElement, ContactElement, JsonElement, XmlElement, FileElement, ButtonElement, CustomMusicElemen, TplMarkdownElement, RawMarkdownElement, NodeElement, KarinElement, LongMsgElement, RecordElement, KeyBoardElement, Button } from '../../types/index.js';
|
|
2
2
|
export declare const segment: {
|
|
3
3
|
/**
|
|
4
4
|
* 纯文本
|
|
@@ -70,12 +70,10 @@ export declare const segment: {
|
|
|
70
70
|
}): ImageElement;
|
|
71
71
|
/**
|
|
72
72
|
* 语音
|
|
73
|
-
* @description 即将废弃,请使用voice
|
|
74
73
|
* @param file - 语音URL或路径、Base64
|
|
75
74
|
* @param magic - 是否魔法语音,默认为 false
|
|
76
75
|
* @param md5 - 语音md5
|
|
77
76
|
* @param name - 语音名称
|
|
78
|
-
* @returns {VoiceElement} 语音元素
|
|
79
77
|
*/
|
|
80
78
|
record(file: string, magic?: boolean, md5?: string, name?: string): RecordElement;
|
|
81
79
|
/**
|
|
@@ -84,8 +82,6 @@ export declare const segment: {
|
|
|
84
82
|
* @param magic - 是否魔法语音,默认为 false
|
|
85
83
|
* @param md5 - 语音md5
|
|
86
84
|
* @param name - 语音名称
|
|
87
|
-
* @returns {VoiceElement} 语音元素
|
|
88
|
-
* @deprecated 即将废弃 请使用segment.record
|
|
89
85
|
*/
|
|
90
86
|
voice(file: string, magic?: boolean, md5?: string, name?: string): RecordElement;
|
|
91
87
|
/**
|
|
@@ -260,15 +256,19 @@ export declare const segment: {
|
|
|
260
256
|
*/
|
|
261
257
|
markdown_tpl(custom_template_id: TplMarkdownElement["custom_template_id"], params: TplMarkdownElement["params"]): TplMarkdownElement;
|
|
262
258
|
/**
|
|
263
|
-
* 按钮
|
|
259
|
+
* 按钮 构建单行单个(obj)、多个按钮(obj[])
|
|
264
260
|
* @param data - 按钮数据
|
|
265
261
|
* @returns {ButtonElement} 按钮元素
|
|
266
262
|
*/
|
|
267
|
-
button(data:
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
rows: Array<ButtonElement>;
|
|
263
|
+
button(data: Button | Array<Button>): {
|
|
264
|
+
type: ButtonElement["type"];
|
|
265
|
+
data: Array<Button>;
|
|
271
266
|
};
|
|
267
|
+
/**
|
|
268
|
+
* 多维按钮
|
|
269
|
+
* @param data - 按钮数据
|
|
270
|
+
*/
|
|
271
|
+
keyboard(data: Array<Button> | Array<Array<Button>>): KeyBoardElement;
|
|
272
272
|
/**
|
|
273
273
|
* 转发自定义节点
|
|
274
274
|
* @param user_id - 用户ID
|
|
@@ -90,12 +90,10 @@ export const segment = new (class Segment {
|
|
|
90
90
|
}
|
|
91
91
|
/**
|
|
92
92
|
* 语音
|
|
93
|
-
* @description 即将废弃,请使用voice
|
|
94
93
|
* @param file - 语音URL或路径、Base64
|
|
95
94
|
* @param magic - 是否魔法语音,默认为 false
|
|
96
95
|
* @param md5 - 语音md5
|
|
97
96
|
* @param name - 语音名称
|
|
98
|
-
* @returns {VoiceElement} 语音元素
|
|
99
97
|
*/
|
|
100
98
|
record(file, magic = false, md5 = '', name = '') {
|
|
101
99
|
return {
|
|
@@ -112,8 +110,6 @@ export const segment = new (class Segment {
|
|
|
112
110
|
* @param magic - 是否魔法语音,默认为 false
|
|
113
111
|
* @param md5 - 语音md5
|
|
114
112
|
* @param name - 语音名称
|
|
115
|
-
* @returns {VoiceElement} 语音元素
|
|
116
|
-
* @deprecated 即将废弃 请使用segment.record
|
|
117
113
|
*/
|
|
118
114
|
voice(file, magic = false, md5 = '', name = '') {
|
|
119
115
|
return {
|
|
@@ -404,25 +400,39 @@ export const segment = new (class Segment {
|
|
|
404
400
|
};
|
|
405
401
|
}
|
|
406
402
|
/**
|
|
407
|
-
* 按钮
|
|
403
|
+
* 按钮 构建单行单个(obj)、多个按钮(obj[])
|
|
408
404
|
* @param data - 按钮数据
|
|
409
405
|
* @returns {ButtonElement} 按钮元素
|
|
410
406
|
*/
|
|
411
407
|
button(data) {
|
|
412
408
|
return {
|
|
413
409
|
type: 'button',
|
|
414
|
-
data,
|
|
410
|
+
data: Array.isArray(data) ? data : [data],
|
|
415
411
|
};
|
|
416
412
|
}
|
|
417
|
-
|
|
413
|
+
/**
|
|
414
|
+
* 多维按钮
|
|
415
|
+
* @param data - 按钮数据
|
|
416
|
+
*/
|
|
417
|
+
keyboard(data) {
|
|
418
|
+
/** 每一个元素为一行按钮 每一行按钮存在多个 */
|
|
418
419
|
const rows = [];
|
|
419
420
|
if (!Array.isArray(data))
|
|
420
421
|
data = [data];
|
|
421
422
|
for (const i of data) {
|
|
422
|
-
|
|
423
|
-
|
|
423
|
+
/** 如果还是数组 说明是单行多个按钮 */
|
|
424
|
+
if (Array.isArray(i)) {
|
|
425
|
+
const button = [];
|
|
426
|
+
for (const v of i)
|
|
427
|
+
button.push(v);
|
|
428
|
+
rows.push(button);
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
/** 单行 单个按钮 */
|
|
432
|
+
rows.push([i]);
|
|
433
|
+
}
|
|
424
434
|
}
|
|
425
|
-
return { type: '
|
|
435
|
+
return { type: 'keyboard', rows };
|
|
426
436
|
}
|
|
427
437
|
/**
|
|
428
438
|
* 转发自定义节点
|
package/lib/utils/tools/exec.js
CHANGED
|
@@ -42,11 +42,9 @@ export const exec = (cmd, log = true, options = { cwd: process.cwd(), encoding:
|
|
|
42
42
|
*/
|
|
43
43
|
export const execs = (cmd, options = { cwd: process.cwd(), encoding: 'utf-8' }) => {
|
|
44
44
|
return new Promise((resolve, reject) => {
|
|
45
|
-
execCmd(cmd, options, (error, stdout
|
|
45
|
+
execCmd(cmd, options, (error, stdout) => {
|
|
46
46
|
if (error)
|
|
47
47
|
return reject(error);
|
|
48
|
-
if (stderr)
|
|
49
|
-
console.error(stderr);
|
|
50
48
|
resolve(stdout.trim());
|
|
51
49
|
});
|
|
52
50
|
});
|