@zhin.js/core 1.0.0 → 1.0.2
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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +295 -74
- package/lib/adapter.d.ts +39 -0
- package/lib/adapter.d.ts.map +1 -0
- package/{dist → lib}/adapter.js +20 -2
- package/lib/adapter.js.map +1 -0
- package/lib/app.d.ts +115 -0
- package/lib/app.d.ts.map +1 -0
- package/{dist → lib}/app.js +148 -78
- package/lib/app.js.map +1 -0
- package/lib/bot.d.ts +31 -0
- package/lib/bot.d.ts.map +1 -0
- package/lib/command.d.ts +32 -0
- package/lib/command.d.ts.map +1 -0
- package/lib/command.js +46 -0
- package/lib/command.js.map +1 -0
- package/lib/component.d.ts +27 -0
- package/lib/component.d.ts.map +1 -0
- package/lib/component.js +469 -0
- package/lib/component.js.map +1 -0
- package/{dist → lib}/config.d.ts.map +1 -1
- package/{dist → lib}/config.js +6 -9
- package/lib/config.js.map +1 -0
- package/lib/cron.d.ts +81 -0
- package/lib/cron.d.ts.map +1 -0
- package/lib/cron.js +159 -0
- package/lib/cron.js.map +1 -0
- package/lib/errors.d.ts +165 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +306 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +17 -0
- package/lib/index.js.map +1 -0
- package/lib/jsx-runtime.d.ts +12 -0
- package/lib/jsx-runtime.d.ts.map +1 -0
- package/lib/jsx-runtime.js +11 -0
- package/lib/jsx-runtime.js.map +1 -0
- package/lib/jsx.d.ts +32 -0
- package/lib/jsx.d.ts.map +1 -0
- package/lib/jsx.js +57 -0
- package/lib/jsx.js.map +1 -0
- package/lib/message.d.ts +47 -0
- package/lib/message.d.ts.map +1 -0
- package/lib/message.js +11 -0
- package/lib/message.js.map +1 -0
- package/lib/plugin.d.ts +50 -0
- package/lib/plugin.d.ts.map +1 -0
- package/lib/plugin.js +170 -0
- package/lib/plugin.js.map +1 -0
- package/lib/prompt.d.ts +116 -0
- package/lib/prompt.d.ts.map +1 -0
- package/lib/prompt.js +240 -0
- package/lib/prompt.js.map +1 -0
- package/lib/schema.d.ts +83 -0
- package/lib/schema.d.ts.map +1 -0
- package/lib/schema.js +245 -0
- package/lib/schema.js.map +1 -0
- package/{dist → lib}/types-generator.d.ts.map +1 -1
- package/{dist → lib}/types-generator.js +6 -3
- package/lib/types-generator.js.map +1 -0
- package/lib/types.d.ts +121 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/utils.d.ts +52 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +340 -0
- package/lib/utils.js.map +1 -0
- package/package.json +23 -9
- package/src/adapter.ts +25 -9
- package/src/app.ts +363 -258
- package/src/bot.ts +29 -8
- package/src/command.ts +50 -0
- package/src/component.ts +561 -0
- package/src/config.ts +9 -12
- package/src/cron.ts +176 -0
- package/src/errors.ts +365 -0
- package/src/index.ts +16 -13
- package/src/jsx-runtime.ts +12 -0
- package/src/jsx.d.ts +52 -0
- package/src/jsx.ts +92 -0
- package/src/message.ts +47 -0
- package/src/plugin.ts +148 -66
- package/src/prompt.ts +290 -0
- package/src/schema.ts +273 -0
- package/src/types-generator.ts +7 -3
- package/src/types.ts +80 -31
- package/src/utils.ts +313 -0
- package/tests/adapter.test.ts +36 -22
- package/tests/app.test.ts +30 -0
- package/tests/command.test.ts +545 -0
- package/tests/component-new.test.ts +348 -0
- package/tests/config.test.ts +1 -1
- package/tests/errors.test.ts +311 -0
- package/tests/expression-evaluation.test.ts +258 -0
- package/tests/message.test.ts +402 -0
- package/tests/plugin.test.ts +284 -143
- package/tests/utils.test.ts +80 -0
- package/tsconfig.json +3 -4
- package/dist/adapter.d.ts +0 -22
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js.map +0 -1
- package/dist/app.d.ts +0 -69
- package/dist/app.d.ts.map +0 -1
- package/dist/app.js.map +0 -1
- package/dist/bot.d.ts +0 -9
- package/dist/bot.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -12
- package/dist/index.js.map +0 -1
- package/dist/logger.d.ts +0 -3
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -3
- package/dist/logger.js.map +0 -1
- package/dist/plugin.d.ts +0 -41
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js +0 -95
- package/dist/plugin.js.map +0 -1
- package/dist/types-generator.js.map +0 -1
- package/dist/types.d.ts +0 -69
- package/dist/types.d.ts.map +0 -1
- package/src/logger.ts +0 -3
- package/tests/logger.test.ts +0 -170
- package/tsconfig.tsbuildinfo +0 -1
- /package/{dist → lib}/bot.js +0 -0
- /package/{dist → lib}/bot.js.map +0 -0
- /package/{dist → lib}/config.d.ts +0 -0
- /package/{dist → lib}/types-generator.d.ts +0 -0
- /package/{dist → lib}/types.js +0 -0
- /package/{dist → lib}/types.js.map +0 -0
package/src/utils.ts
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import {Dict, MessageElement, MessageSegment, SendContent} from "./types";
|
|
2
|
+
|
|
3
|
+
export function getValueWithRuntime(template: string, ctx: Dict) {
|
|
4
|
+
const result = evaluate(template, ctx);
|
|
5
|
+
if (result === `return(${template})`) return undefined;
|
|
6
|
+
return result;
|
|
7
|
+
}
|
|
8
|
+
export const evaluate = <S, T = any>(exp: string, context: S) => {
|
|
9
|
+
const result = execute<S, T>(`return(${exp})`, context);
|
|
10
|
+
// 如果结果是原始表达式,说明访问被阻止,返回 undefined
|
|
11
|
+
if (result === `return(${exp})`) return undefined;
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
const evalCache: Record<string, Function> = Object.create(null);
|
|
15
|
+
export const execute = <S, T = any>(exp: string, context: S):T => {
|
|
16
|
+
const fn = evalCache[exp] || (evalCache[exp] = toFunction(exp));
|
|
17
|
+
context={
|
|
18
|
+
...context,
|
|
19
|
+
process:undefined,
|
|
20
|
+
global:undefined,
|
|
21
|
+
Buffer:undefined,
|
|
22
|
+
crypto:undefined
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
return fn.apply(context, [context]);
|
|
26
|
+
} catch {
|
|
27
|
+
return exp as T;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const toFunction = (exp: string): Function => {
|
|
32
|
+
try {
|
|
33
|
+
return new Function(`$data`, `with($data){${exp}}`);
|
|
34
|
+
} catch {
|
|
35
|
+
return () => {};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export function compiler(template: string, ctx: Dict) {
|
|
39
|
+
const matched = [...template.matchAll(/\${([^}]*?)}/g)];
|
|
40
|
+
for (const item of matched) {
|
|
41
|
+
const tpl = item[1];
|
|
42
|
+
let value = getValueWithRuntime(tpl, ctx);
|
|
43
|
+
if (value === tpl) continue;
|
|
44
|
+
if (typeof value !== 'string') value = JSON.stringify(value, null, 2);
|
|
45
|
+
template = template.replace(`\${${item[1]}}`, value);
|
|
46
|
+
}
|
|
47
|
+
return template;
|
|
48
|
+
}
|
|
49
|
+
export function segment<T extends object>(type:string,data:T){
|
|
50
|
+
return {
|
|
51
|
+
type,
|
|
52
|
+
data
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export namespace segment{
|
|
56
|
+
export function escape<T>(text: T): T {
|
|
57
|
+
if (typeof text !== 'string') return text;
|
|
58
|
+
return text
|
|
59
|
+
.replace(/&/g, '&')
|
|
60
|
+
.replace(/</g, '<')
|
|
61
|
+
.replace(/>/g, '>')
|
|
62
|
+
.replace(/"/g, '"')
|
|
63
|
+
.replace(/'/g, ''') as T;
|
|
64
|
+
}
|
|
65
|
+
export function unescape<T>(text: T): T {
|
|
66
|
+
if (typeof text !== 'string') return text;
|
|
67
|
+
return text
|
|
68
|
+
.replace(/</g, '<')
|
|
69
|
+
.replace(/>/g, '>')
|
|
70
|
+
.replace(/"/g, '"')
|
|
71
|
+
.replace(/'/g, "'")
|
|
72
|
+
.replace(/&/g, '&') as T;
|
|
73
|
+
}
|
|
74
|
+
export function text(text:string){
|
|
75
|
+
return segment('text',{text});
|
|
76
|
+
}
|
|
77
|
+
export function face(id:string,text?:string){
|
|
78
|
+
return segment('face',{id,text});
|
|
79
|
+
}
|
|
80
|
+
export function from(content: SendContent): SendContent {
|
|
81
|
+
if (!Array.isArray(content)) content=[content];
|
|
82
|
+
const toString=(template:string|MessageElement)=>{
|
|
83
|
+
if(typeof template!=='string') return [template]
|
|
84
|
+
template=unescape(template);
|
|
85
|
+
const result: MessageElement[] = [];
|
|
86
|
+
const closingReg = /<(\S+)(\s[^>]+)?\/>/;
|
|
87
|
+
const twinningReg = /<(\S+)(\s[^>]+)?>([\s\S]*?)<\/\1>/;
|
|
88
|
+
while (template.length) {
|
|
89
|
+
const [_, type, attrStr = '', child = ''] = template.match(twinningReg) || template.match(closingReg) || [];
|
|
90
|
+
if (!type) break;
|
|
91
|
+
const isClosing = closingReg.test(template);
|
|
92
|
+
const matched = isClosing ? `<${type}${attrStr}/>` : `<${type}${attrStr}>${child}</${type}>`;
|
|
93
|
+
const index = template.indexOf(matched);
|
|
94
|
+
const prevText = template.slice(0, index);
|
|
95
|
+
if (prevText)
|
|
96
|
+
result.push({
|
|
97
|
+
type: 'text',
|
|
98
|
+
data: {
|
|
99
|
+
text: unescape(prevText),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
template = template.slice(index + matched.length);
|
|
103
|
+
const attrArr = [...attrStr.matchAll(/\s([^=]+)(?=(?=="([^"]+)")|(?=='([^']+)'))/g)];
|
|
104
|
+
const data = Object.fromEntries(
|
|
105
|
+
attrArr.map(([source, key, v1, v2]) => {
|
|
106
|
+
const value = v1 || v2;
|
|
107
|
+
try {
|
|
108
|
+
return [key, JSON.parse(unescape(value))];
|
|
109
|
+
} catch {
|
|
110
|
+
return [key, unescape(value)];
|
|
111
|
+
}
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
if (child) {
|
|
115
|
+
data.message = toString(child).map(({ type, data }) => ({ type, ...data }));
|
|
116
|
+
}
|
|
117
|
+
result.push({
|
|
118
|
+
type: type,
|
|
119
|
+
data,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (template.length) {
|
|
123
|
+
result.push({
|
|
124
|
+
type: 'text',
|
|
125
|
+
data: {
|
|
126
|
+
text: unescape(template),
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
return content.reduce((result,item)=>{
|
|
133
|
+
result.push(...toString(item))
|
|
134
|
+
return result;
|
|
135
|
+
},[] as MessageElement[])
|
|
136
|
+
}
|
|
137
|
+
export function raw(content:SendContent){
|
|
138
|
+
if(!Array.isArray(content)) content=[content]
|
|
139
|
+
return content.map(item=>{
|
|
140
|
+
if(typeof item==='string') return item
|
|
141
|
+
const {type,data}=item
|
|
142
|
+
if(type==='text') return data.text
|
|
143
|
+
return data.text?`{${type}}(${data.text})`:`{${type}}`;
|
|
144
|
+
}).join('')
|
|
145
|
+
}
|
|
146
|
+
export function toString(content:SendContent){
|
|
147
|
+
if(!Array.isArray(content)) content=[content]
|
|
148
|
+
return content.map(item=>{
|
|
149
|
+
if(typeof item==='string') return item
|
|
150
|
+
let {type,data}=item
|
|
151
|
+
if(typeof type==='function') type=type.name
|
|
152
|
+
if(type==='text') return data.text
|
|
153
|
+
return `<${type} ${Object.keys(data).map(key=>`${key}='${escape(JSON.stringify(data[key]))}'`).join(' ')}/>`
|
|
154
|
+
}).join('')
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function remove<T>(list: T[], fn: (item: T) => boolean): void;
|
|
159
|
+
export function remove<T>(list: T[], item: T): void;
|
|
160
|
+
export function remove<T>(list: T[], arg: T | ((item: T) => boolean)) {
|
|
161
|
+
const index =
|
|
162
|
+
typeof arg === 'function' && !list.every(item => typeof item === 'function')
|
|
163
|
+
? list.findIndex(arg as (item: T) => boolean)
|
|
164
|
+
: list.indexOf(arg as T);
|
|
165
|
+
if (index !== -1) list.splice(index, 1);
|
|
166
|
+
}
|
|
167
|
+
export function isEmpty<T>(item:T){
|
|
168
|
+
if(Array.isArray(item)) return item.length===0
|
|
169
|
+
if(typeof item==='object'){
|
|
170
|
+
if(!item) return true
|
|
171
|
+
return Reflect.ownKeys(item).length===0
|
|
172
|
+
}
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export namespace Time {
|
|
177
|
+
export const millisecond = 1;
|
|
178
|
+
export const second = 1000;
|
|
179
|
+
export const minute = second * 60;
|
|
180
|
+
export const hour = minute * 60;
|
|
181
|
+
export const day = hour * 24;
|
|
182
|
+
export const week = day * 7;
|
|
183
|
+
|
|
184
|
+
let timezoneOffset = new Date().getTimezoneOffset();
|
|
185
|
+
|
|
186
|
+
export function setTimezoneOffset(offset: number) {
|
|
187
|
+
timezoneOffset = offset;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function getTimezoneOffset() {
|
|
191
|
+
return timezoneOffset;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function getDateNumber(date: number | Date = new Date(), offset?: number) {
|
|
195
|
+
if (typeof date === 'number') date = new Date(date);
|
|
196
|
+
if (offset === undefined) offset = timezoneOffset;
|
|
197
|
+
return Math.floor((date.valueOf() / minute - offset) / 1440);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function fromDateNumber(value: number, offset?: number) {
|
|
201
|
+
const date = new Date(value * day);
|
|
202
|
+
if (offset === undefined) offset = timezoneOffset;
|
|
203
|
+
return new Date(+date + offset * minute);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const numeric = /\d+(?:\.\d+)?/.source;
|
|
207
|
+
const timeRegExp = new RegExp(
|
|
208
|
+
`^${['w(?:eek(?:s)?)?', 'd(?:ay(?:s)?)?', 'h(?:our(?:s)?)?', 'm(?:in(?:ute)?(?:s)?)?', 's(?:ec(?:ond)?(?:s)?)?']
|
|
209
|
+
.map(unit => `(${numeric}${unit})?`)
|
|
210
|
+
.join('')}$`,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
export function parseTime(source: string) {
|
|
214
|
+
const capture = timeRegExp.exec(source);
|
|
215
|
+
if (!capture) return 0;
|
|
216
|
+
return (
|
|
217
|
+
(parseFloat(capture[1]) * week || 0) +
|
|
218
|
+
(parseFloat(capture[2]) * day || 0) +
|
|
219
|
+
(parseFloat(capture[3]) * hour || 0) +
|
|
220
|
+
(parseFloat(capture[4]) * minute || 0) +
|
|
221
|
+
(parseFloat(capture[5]) * second || 0)
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function parseDate(date: string) {
|
|
226
|
+
const parsed = parseTime(date);
|
|
227
|
+
if (parsed) {
|
|
228
|
+
date = (Date.now() + parsed) as any;
|
|
229
|
+
} else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
|
|
230
|
+
date = `${new Date().toLocaleDateString()}-${date}`;
|
|
231
|
+
} else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
|
|
232
|
+
date = `${new Date().getFullYear()}-${date}`;
|
|
233
|
+
}
|
|
234
|
+
return date ? new Date(date) : new Date();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function formatTimeShort(ms: number) {
|
|
238
|
+
const abs = Math.abs(ms);
|
|
239
|
+
if (abs >= day - hour / 2) {
|
|
240
|
+
return Math.round(ms / day) + 'd';
|
|
241
|
+
} else if (abs >= hour - minute / 2) {
|
|
242
|
+
return Math.round(ms / hour) + 'h';
|
|
243
|
+
} else if (abs >= minute - second / 2) {
|
|
244
|
+
return Math.round(ms / minute) + 'm';
|
|
245
|
+
} else if (abs >= second) {
|
|
246
|
+
return Math.round(ms / second) + 's';
|
|
247
|
+
}
|
|
248
|
+
return ms + 'ms';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function formatTime(ms: number) {
|
|
252
|
+
let result: string;
|
|
253
|
+
if (ms >= day - hour / 2) {
|
|
254
|
+
ms += hour / 2;
|
|
255
|
+
result = Math.floor(ms / day) + ' 天';
|
|
256
|
+
if (ms % day > hour) {
|
|
257
|
+
result += ` ${Math.floor((ms % day) / hour)} 小时`;
|
|
258
|
+
}
|
|
259
|
+
} else if (ms >= hour - minute / 2) {
|
|
260
|
+
ms += minute / 2;
|
|
261
|
+
result = Math.floor(ms / hour) + ' 小时';
|
|
262
|
+
if (ms % hour > minute) {
|
|
263
|
+
result += ` ${Math.floor((ms % hour) / minute)} 分钟`;
|
|
264
|
+
}
|
|
265
|
+
} else if (ms >= minute - second / 2) {
|
|
266
|
+
ms += second / 2;
|
|
267
|
+
result = Math.floor(ms / minute) + ' 分钟';
|
|
268
|
+
if (ms % minute > second) {
|
|
269
|
+
result += ` ${Math.floor((ms % minute) / second)} 秒`;
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
result = Math.round(ms / second) + ' 秒';
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const dayMap = ['日', '一', '二', '三', '四', '五', '六'];
|
|
278
|
+
|
|
279
|
+
function toDigits(source: number, length = 2) {
|
|
280
|
+
return source.toString().padStart(length, '0');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function template(template: string, time = new Date()) {
|
|
284
|
+
return template
|
|
285
|
+
.replace('yyyy', time.getFullYear().toString())
|
|
286
|
+
.replace('yy', time.getFullYear().toString().slice(2))
|
|
287
|
+
.replace('MM', toDigits(time.getMonth() + 1))
|
|
288
|
+
.replace('dd', toDigits(time.getDate()))
|
|
289
|
+
.replace('hh', toDigits(time.getHours()))
|
|
290
|
+
.replace('mm', toDigits(time.getMinutes()))
|
|
291
|
+
.replace('ss', toDigits(time.getSeconds()))
|
|
292
|
+
.replace('SSS', toDigits(time.getMilliseconds(), 3));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function toHourMinute(time: Date) {
|
|
296
|
+
return `${toDigits(time.getHours())}:${toDigits(time.getMinutes())}`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function formatTimeInterval(time: Date, interval?: number) {
|
|
300
|
+
if (!interval) {
|
|
301
|
+
return template('yyyy-MM-dd hh:mm:ss', time);
|
|
302
|
+
} else if (interval === day) {
|
|
303
|
+
return `每天 ${toHourMinute(time)}`;
|
|
304
|
+
} else if (interval === week) {
|
|
305
|
+
return `每周${dayMap[time.getDay()]} ${toHourMinute(time)}`;
|
|
306
|
+
} else {
|
|
307
|
+
return `${template('yyyy-MM-dd hh:mm:ss', time)} 起每隔 ${formatTime(interval)}`;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
export function sleep(ms: number) {
|
|
312
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
313
|
+
}
|
package/tests/adapter.test.ts
CHANGED
|
@@ -8,20 +8,27 @@ import type { BotConfig } from '../src/types'
|
|
|
8
8
|
describe('适配器类测试', () => {
|
|
9
9
|
// 创建测试用的Bot类
|
|
10
10
|
class TestBot implements Bot {
|
|
11
|
-
connected = false
|
|
11
|
+
$connected = false
|
|
12
|
+
$config: BotConfig
|
|
12
13
|
|
|
13
|
-
constructor(public plugin: Plugin,
|
|
14
|
+
constructor(public plugin: Plugin, config: BotConfig) {
|
|
15
|
+
this.$config = config
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async $connect(): Promise<void> {
|
|
19
|
+
this.$connected = true
|
|
20
|
+
}
|
|
14
21
|
|
|
15
|
-
async
|
|
16
|
-
this
|
|
22
|
+
async $disconnect(): Promise<void> {
|
|
23
|
+
this.$connected = false
|
|
17
24
|
}
|
|
18
25
|
|
|
19
|
-
async
|
|
20
|
-
this
|
|
26
|
+
async $sendMessage(): Promise<void> {
|
|
27
|
+
if (!this.$connected) throw new Error('机器人未连接')
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
$formatMessage(message: any): any {
|
|
31
|
+
return message
|
|
25
32
|
}
|
|
26
33
|
}
|
|
27
34
|
|
|
@@ -68,8 +75,8 @@ describe('适配器类测试', () => {
|
|
|
68
75
|
expect(adapter.bots.size).toBe(2)
|
|
69
76
|
expect(adapter.bots.get('test-bot-1')).toBeDefined()
|
|
70
77
|
expect(adapter.bots.get('test-bot-2')).toBeDefined()
|
|
71
|
-
expect(adapter.bots.get('test-bot-1')
|
|
72
|
-
expect(adapter.bots.get('test-bot-2')
|
|
78
|
+
expect(adapter.bots.get('test-bot-1')?.$connected).toBe(true)
|
|
79
|
+
expect(adapter.bots.get('test-bot-2')?.$connected).toBe(true)
|
|
73
80
|
|
|
74
81
|
expect(loggerSpy).toHaveBeenCalledWith('bot test-bot-1 of adapter test-adapter connected')
|
|
75
82
|
expect(loggerSpy).toHaveBeenCalledWith('bot test-bot-2 of adapter test-adapter connected')
|
|
@@ -106,7 +113,7 @@ describe('适配器类测试', () => {
|
|
|
106
113
|
describe('错误处理测试', () => {
|
|
107
114
|
it('应该处理机器人连接失败', async () => {
|
|
108
115
|
class FailingBot extends TestBot {
|
|
109
|
-
async connect(): Promise<void> {
|
|
116
|
+
async $connect(): Promise<void> {
|
|
110
117
|
throw new Error('连接失败')
|
|
111
118
|
}
|
|
112
119
|
}
|
|
@@ -123,7 +130,7 @@ describe('适配器类测试', () => {
|
|
|
123
130
|
|
|
124
131
|
it('应该处理机器人断开连接失败', async () => {
|
|
125
132
|
class FailingBot extends TestBot {
|
|
126
|
-
async disconnect(): Promise<void> {
|
|
133
|
+
async $disconnect(): Promise<void> {
|
|
127
134
|
throw new Error('断开连接失败')
|
|
128
135
|
}
|
|
129
136
|
}
|
|
@@ -148,20 +155,27 @@ describe('适配器类测试', () => {
|
|
|
148
155
|
}
|
|
149
156
|
|
|
150
157
|
class ExtendedBot implements Bot<ExtendedBotConfig> {
|
|
151
|
-
connected = false
|
|
158
|
+
$connected = false
|
|
159
|
+
$config: ExtendedBotConfig
|
|
152
160
|
|
|
153
|
-
constructor(public plugin: Plugin,
|
|
161
|
+
constructor(public plugin: Plugin, config: ExtendedBotConfig) {
|
|
162
|
+
this.$config = config
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async $connect(): Promise<void> {
|
|
166
|
+
this.$connected = true
|
|
167
|
+
}
|
|
154
168
|
|
|
155
|
-
async
|
|
156
|
-
this
|
|
169
|
+
async $disconnect(): Promise<void> {
|
|
170
|
+
this.$connected = false
|
|
157
171
|
}
|
|
158
172
|
|
|
159
|
-
async
|
|
160
|
-
this
|
|
173
|
+
async $sendMessage(): Promise<void> {
|
|
174
|
+
if (!this.$connected) throw new Error('机器人未连接')
|
|
161
175
|
}
|
|
162
176
|
|
|
163
|
-
|
|
164
|
-
|
|
177
|
+
$formatMessage(message: any): any {
|
|
178
|
+
return message
|
|
165
179
|
}
|
|
166
180
|
}
|
|
167
181
|
|
|
@@ -180,8 +194,8 @@ describe('适配器类测试', () => {
|
|
|
180
194
|
|
|
181
195
|
const bot = extendedAdapter.bots.get('extended-bot')
|
|
182
196
|
expect(bot).toBeDefined()
|
|
183
|
-
expect(bot
|
|
184
|
-
expect(bot
|
|
197
|
+
expect(bot?.$config.token).toBe('test-token')
|
|
198
|
+
expect(bot?.$config.platform).toBe('test-platform')
|
|
185
199
|
})
|
|
186
200
|
})
|
|
187
201
|
})
|
package/tests/app.test.ts
CHANGED
|
@@ -137,6 +137,36 @@ describe('App类测试', () => {
|
|
|
137
137
|
it('当上下文不存在时应该抛出错误', () => {
|
|
138
138
|
expect(() => app.getContext('non-existent')).toThrow("can't find Context of non-existent")
|
|
139
139
|
})
|
|
140
|
+
|
|
141
|
+
it('应该正确设置和获取上下文描述', async () => {
|
|
142
|
+
// 创建测试插件
|
|
143
|
+
const plugin = app.createDependency('test-plugin-desc', 'test-plugin-desc.ts')
|
|
144
|
+
|
|
145
|
+
// 注册带描述的上下文
|
|
146
|
+
const context = {
|
|
147
|
+
name: 'test-context',
|
|
148
|
+
description: '这是一个测试上下文,用于验证描述字段功能',
|
|
149
|
+
mounted: () => ({ testValue: 'test' }),
|
|
150
|
+
dispose: () => {}
|
|
151
|
+
}
|
|
152
|
+
plugin.register(context)
|
|
153
|
+
|
|
154
|
+
// 等待插件挂载
|
|
155
|
+
await plugin.mounted()
|
|
156
|
+
|
|
157
|
+
// 使用上下文来验证功能
|
|
158
|
+
plugin.useContext('test-context', () => {
|
|
159
|
+
// 验证上下文可以正常获取(先测试基本功能)
|
|
160
|
+
const retrievedContext = app.getContext('test-context')
|
|
161
|
+
expect(retrievedContext).toEqual({ testValue: 'test' })
|
|
162
|
+
|
|
163
|
+
// 验证上下文列表包含描述信息
|
|
164
|
+
const contextList = app.contextList
|
|
165
|
+
const testContext = contextList.find(ctx => ctx.name === 'test-context')
|
|
166
|
+
expect(testContext).toBeDefined()
|
|
167
|
+
expect(testContext?.description).toBe('这是一个测试上下文,用于验证描述字段功能')
|
|
168
|
+
})
|
|
169
|
+
})
|
|
140
170
|
})
|
|
141
171
|
|
|
142
172
|
describe('消息处理测试', () => {
|