@zhin.js/core 1.0.0
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 +159 -0
- package/dist/adapter.d.ts +22 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +67 -0
- package/dist/adapter.js.map +1 -0
- package/dist/app.d.ts +69 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +307 -0
- package/dist/app.js.map +1 -0
- package/dist/bot.d.ts +9 -0
- package/dist/bot.d.ts.map +1 -0
- package/dist/bot.js +2 -0
- package/dist/bot.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +242 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +3 -0
- package/dist/logger.js.map +1 -0
- package/dist/plugin.d.ts +41 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +95 -0
- package/dist/plugin.js.map +1 -0
- package/dist/types-generator.d.ts +6 -0
- package/dist/types-generator.d.ts.map +1 -0
- package/dist/types-generator.js +69 -0
- package/dist/types-generator.js.map +1 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -0
- package/src/adapter.ts +69 -0
- package/src/app.ts +339 -0
- package/src/bot.ts +9 -0
- package/src/config.ts +276 -0
- package/src/index.ts +14 -0
- package/src/logger.ts +3 -0
- package/src/plugin.ts +122 -0
- package/src/types-generator.ts +74 -0
- package/src/types.ts +74 -0
- package/tests/adapter.test.ts +187 -0
- package/tests/app.test.ts +207 -0
- package/tests/bot.test.ts +132 -0
- package/tests/config.test.ts +328 -0
- package/tests/logger.test.ts +170 -0
- package/tests/plugin.test.ts +226 -0
- package/tests/test-utils.ts +59 -0
- package/tests/types.test.ts +162 -0
- package/tsconfig.json +25 -0
- package/tsconfig.tsbuildinfo +1 -0
package/src/config.ts
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
5
|
+
import { parse as parseToml } from 'toml';
|
|
6
|
+
import { config as loadDotenv } from 'dotenv';
|
|
7
|
+
import type { AppConfig,DefineConfig } from './types.js';
|
|
8
|
+
|
|
9
|
+
export interface ConfigOptions {
|
|
10
|
+
configPath?: string;
|
|
11
|
+
envPath?: string;
|
|
12
|
+
envOverride?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 支持的配置文件格式
|
|
17
|
+
*/
|
|
18
|
+
export type ConfigFormat = 'json' | 'yaml' | 'yml' | 'toml' | 'js' | 'ts';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 环境变量替换正则表达式,支持默认值语法: ${VAR_NAME:-default_value}
|
|
22
|
+
*/
|
|
23
|
+
const ENV_VAR_REGEX = /\$\{([^}]+)\}/g;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 替换字符串中的环境变量
|
|
27
|
+
*/
|
|
28
|
+
function replaceEnvVars(str: string): string {
|
|
29
|
+
return str.replace(ENV_VAR_REGEX, (match, content) => {
|
|
30
|
+
// 解析环境变量名和默认值
|
|
31
|
+
const colonIndex = content.indexOf(':-');
|
|
32
|
+
let envName: string;
|
|
33
|
+
let defaultValue: string | undefined;
|
|
34
|
+
|
|
35
|
+
if (colonIndex !== -1) {
|
|
36
|
+
// 格式: VAR_NAME:-default_value
|
|
37
|
+
envName = content.slice(0, colonIndex);
|
|
38
|
+
defaultValue = content.slice(colonIndex + 2);
|
|
39
|
+
} else {
|
|
40
|
+
// 格式: VAR_NAME
|
|
41
|
+
envName = content;
|
|
42
|
+
defaultValue = undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const envValue = process.env[envName];
|
|
46
|
+
|
|
47
|
+
if (envValue !== undefined) {
|
|
48
|
+
return envValue;
|
|
49
|
+
} else if (defaultValue !== undefined) {
|
|
50
|
+
return defaultValue;
|
|
51
|
+
} else {
|
|
52
|
+
console.warn(`环境变量 ${envName} 未定义,保持原值`);
|
|
53
|
+
return match;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 递归替换对象中的环境变量
|
|
60
|
+
*/
|
|
61
|
+
function replaceEnvVarsInObject(obj: any): any {
|
|
62
|
+
if (typeof obj === 'string') {
|
|
63
|
+
return replaceEnvVars(obj);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (Array.isArray(obj)) {
|
|
67
|
+
return obj.map(item => replaceEnvVarsInObject(item));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (obj && typeof obj === 'object') {
|
|
71
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
72
|
+
obj[key] = replaceEnvVarsInObject(value);
|
|
73
|
+
}
|
|
74
|
+
return obj;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return obj;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 根据文件扩展名解析配置文件
|
|
82
|
+
*/
|
|
83
|
+
async function parseConfigFile(content: string, format: ConfigFormat, filePath?: string): Promise<any> {
|
|
84
|
+
try {
|
|
85
|
+
switch (format) {
|
|
86
|
+
case 'json':
|
|
87
|
+
return JSON.parse(content);
|
|
88
|
+
case 'yaml':
|
|
89
|
+
case 'yml':
|
|
90
|
+
return parseYaml(content);
|
|
91
|
+
case 'toml':
|
|
92
|
+
return parseToml(content);
|
|
93
|
+
case 'js':
|
|
94
|
+
case 'ts':
|
|
95
|
+
if (!filePath) {
|
|
96
|
+
throw new Error('解析 JS/TS 配置文件需要提供文件路径');
|
|
97
|
+
}
|
|
98
|
+
// 使用动态导入加载 JS/TS 模块
|
|
99
|
+
const fileUrl = pathToFileURL(path.resolve(filePath)).href;
|
|
100
|
+
const module = await import(fileUrl);
|
|
101
|
+
// 支持 ES 模块的 default 导出和 CommonJS 模块
|
|
102
|
+
const result = module.default || module;
|
|
103
|
+
if(typeof result === 'function') return await result((process.env||{}) as Record<string,string>);
|
|
104
|
+
return result;
|
|
105
|
+
default:
|
|
106
|
+
throw new Error(`不支持的配置文件格式: ${format}`);
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
throw new Error(`解析配置文件失败: ${error}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 获取配置文件格式
|
|
115
|
+
*/
|
|
116
|
+
function getConfigFormat(filePath: string): ConfigFormat {
|
|
117
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
118
|
+
if (!['json', 'yaml', 'yml', 'toml', 'js', 'ts'].includes(ext)) {
|
|
119
|
+
throw new Error(`不支持的配置文件格式: ${ext}`);
|
|
120
|
+
}
|
|
121
|
+
return ext as ConfigFormat;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 查找配置文件
|
|
126
|
+
*/
|
|
127
|
+
function findConfigFile(cwd: string = process.cwd()): string | null {
|
|
128
|
+
const configNames = [
|
|
129
|
+
// 优先查找 zhin.config.* 格式
|
|
130
|
+
'zhin.config.yaml',
|
|
131
|
+
'zhin.config.yml',
|
|
132
|
+
'zhin.config.json',
|
|
133
|
+
'zhin.config.toml',
|
|
134
|
+
'zhin.config.js',
|
|
135
|
+
'zhin.config.ts',
|
|
136
|
+
// 然后查找 config.* 格式
|
|
137
|
+
'config.yaml',
|
|
138
|
+
'config.yml',
|
|
139
|
+
'config.json',
|
|
140
|
+
'config.toml',
|
|
141
|
+
'config.js',
|
|
142
|
+
'config.ts'
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
for (const name of configNames) {
|
|
146
|
+
const filePath = path.join(cwd, name);
|
|
147
|
+
if (fs.existsSync(filePath)) {
|
|
148
|
+
return filePath;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
export function defineConfig<T extends DefineConfig<AppConfig>>(config: T): T {
|
|
155
|
+
return config;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 加载配置文件
|
|
159
|
+
*/
|
|
160
|
+
export async function loadConfig(options: ConfigOptions = {}): Promise<[string,AppConfig]> {
|
|
161
|
+
const { configPath, envPath, envOverride = true } = options;
|
|
162
|
+
|
|
163
|
+
// 加载环境变量
|
|
164
|
+
if (envPath) {
|
|
165
|
+
loadDotenv({ path: envPath, override: envOverride });
|
|
166
|
+
} else {
|
|
167
|
+
// 尝试加载默认的 .env 文件
|
|
168
|
+
const defaultEnvPath = path.join(process.cwd(), '.env');
|
|
169
|
+
if (fs.existsSync(defaultEnvPath)) {
|
|
170
|
+
loadDotenv({ path: defaultEnvPath, override: envOverride });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 确定配置文件路径
|
|
175
|
+
let finalConfigPath: string;
|
|
176
|
+
|
|
177
|
+
if (configPath) {
|
|
178
|
+
if (!fs.existsSync(configPath)) {
|
|
179
|
+
throw new Error(`配置文件不存在: ${configPath}`);
|
|
180
|
+
}
|
|
181
|
+
finalConfigPath = configPath;
|
|
182
|
+
} else {
|
|
183
|
+
const foundPath = findConfigFile();
|
|
184
|
+
if (!foundPath) {
|
|
185
|
+
throw new Error('未找到配置文件,支持的文件名: zhin.config.[yaml|yml|json|toml|js|ts] 或 config.[yaml|yml|json|toml|js|ts]');
|
|
186
|
+
}
|
|
187
|
+
finalConfigPath = foundPath;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 读取并解析配置文件
|
|
191
|
+
const format = getConfigFormat(finalConfigPath);
|
|
192
|
+
let rawConfig: any;
|
|
193
|
+
|
|
194
|
+
if (format === 'js' || format === 'ts') {
|
|
195
|
+
// JS/TS 文件不需要读取内容,直接解析
|
|
196
|
+
rawConfig = await parseConfigFile('', format, finalConfigPath);
|
|
197
|
+
} else {
|
|
198
|
+
const content = fs.readFileSync(finalConfigPath, 'utf-8');
|
|
199
|
+
rawConfig = await parseConfigFile(content, format, finalConfigPath);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 替换环境变量
|
|
203
|
+
const config = replaceEnvVarsInObject(rawConfig) as AppConfig;
|
|
204
|
+
|
|
205
|
+
// 验证配置
|
|
206
|
+
validateConfig(config);
|
|
207
|
+
|
|
208
|
+
return [finalConfigPath,config];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 验证配置文件结构
|
|
213
|
+
*/
|
|
214
|
+
function validateConfig(config: any): void {
|
|
215
|
+
if (!config) {
|
|
216
|
+
throw new Error('配置文件不能为空');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!config.bots || !Array.isArray(config.bots)) {
|
|
220
|
+
throw new Error('配置文件必须包含 bots 数组');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
for (const [index, bot] of config.bots.entries()) {
|
|
225
|
+
if (!bot.name) {
|
|
226
|
+
throw new Error(`机器人 ${index} 缺少 name 字段`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!bot.context) {
|
|
230
|
+
throw new Error(`机器人 ${bot.name} 缺少 context 字段`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 保存配置文件
|
|
237
|
+
*/
|
|
238
|
+
export function saveConfig(config: AppConfig, filePath: string): void {
|
|
239
|
+
const format = getConfigFormat(filePath);
|
|
240
|
+
let content: string;
|
|
241
|
+
|
|
242
|
+
switch (format) {
|
|
243
|
+
case 'json':
|
|
244
|
+
content = JSON.stringify(config, null, 2);
|
|
245
|
+
break;
|
|
246
|
+
case 'yaml':
|
|
247
|
+
case 'yml':
|
|
248
|
+
content = stringifyYaml(config, { indent: 2 });
|
|
249
|
+
break;
|
|
250
|
+
case 'toml':
|
|
251
|
+
// toml 库没有 stringify 方法,我们需要手动实现或使用其他库
|
|
252
|
+
throw new Error('暂不支持保存 TOML 格式的配置文件');
|
|
253
|
+
default:
|
|
254
|
+
throw new Error(`不支持的配置文件格式: ${format}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 创建默认配置
|
|
262
|
+
*/
|
|
263
|
+
export function createDefaultConfig(format: ConfigFormat = 'yaml'): AppConfig {
|
|
264
|
+
return {
|
|
265
|
+
bots: [
|
|
266
|
+
{
|
|
267
|
+
name: 'onebot11',
|
|
268
|
+
context: 'onebot11',
|
|
269
|
+
url: '${ONEBOT_URL:-ws://localhost:8080}',
|
|
270
|
+
access_token: '${ONEBOT_ACCESS_TOKEN:-}'
|
|
271
|
+
}
|
|
272
|
+
],
|
|
273
|
+
plugin_dirs: ['./src/plugins', 'node_modules'],
|
|
274
|
+
plugins: []
|
|
275
|
+
};
|
|
276
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Zhin Bot Framework - HMR Edition
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
// 核心系统
|
|
6
|
+
export type { Bot } from './bot.js';
|
|
7
|
+
export { loadConfig, saveConfig, createDefaultConfig } from './config.js';
|
|
8
|
+
export * from './types.js';
|
|
9
|
+
export * from './config.js';
|
|
10
|
+
export * from './adapter.js'
|
|
11
|
+
export * from './plugin.js';
|
|
12
|
+
// HMR Bot系统 (主要API)
|
|
13
|
+
export * from './app.js';
|
|
14
|
+
export * from '@zhin.js/hmr';
|
package/src/logger.ts
ADDED
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// 插件类型定义
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import {MaybePromise} from '@zhin.js/types'
|
|
7
|
+
import { Message, BeforeSendHandler, SendOptions} from "./types";
|
|
8
|
+
import {Dependency, Logger,} from "@zhin.js/hmr";
|
|
9
|
+
import {App} from "./app";
|
|
10
|
+
|
|
11
|
+
/** 消息中间件函数 */
|
|
12
|
+
export type MessageMiddleware = (message: Message, next: () => Promise<void>) => MaybePromise<void>;
|
|
13
|
+
|
|
14
|
+
/** 事件监听器函数 */
|
|
15
|
+
export type EventListener<T = any> = (data: T) => void | Promise<void>;
|
|
16
|
+
|
|
17
|
+
/** 定时任务配置 */
|
|
18
|
+
export interface CronJob {
|
|
19
|
+
name: string;
|
|
20
|
+
schedule: string; // cron 表达式
|
|
21
|
+
handler: () => void | Promise<void>;
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Plugin 类
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 插件类:继承自Dependency,提供机器人特定功能
|
|
32
|
+
*/
|
|
33
|
+
export class Plugin extends Dependency<Plugin> {
|
|
34
|
+
middlewares: MessageMiddleware[] = [];
|
|
35
|
+
eventListeners = new Map<string, EventListener[]>();
|
|
36
|
+
cronJobs = new Map<string, CronJob>();
|
|
37
|
+
|
|
38
|
+
#logger?:Logger
|
|
39
|
+
constructor(parent: Dependency<Plugin>, name: string, filePath: string) {
|
|
40
|
+
super(parent, name, filePath);
|
|
41
|
+
this.on('message.receive',this.#handleMessage.bind(this))
|
|
42
|
+
}
|
|
43
|
+
#handleMessage(message:Message){
|
|
44
|
+
const next=async (index:number)=>{
|
|
45
|
+
if(!this.middlewares[index]) return
|
|
46
|
+
const middleware=this.middlewares[index]
|
|
47
|
+
middleware(message,()=>next(index+1))
|
|
48
|
+
}
|
|
49
|
+
next(0)
|
|
50
|
+
}
|
|
51
|
+
beforeSend(handler:BeforeSendHandler){
|
|
52
|
+
this.before('message.send',handler)
|
|
53
|
+
}
|
|
54
|
+
before(event:string,listener:(...args:any[])=>any){
|
|
55
|
+
this.on(`before-${event}`,listener)
|
|
56
|
+
}
|
|
57
|
+
/** 获取所属的App实例 */
|
|
58
|
+
get app(): App {
|
|
59
|
+
return this.parent as App;
|
|
60
|
+
}
|
|
61
|
+
get logger(): Logger {
|
|
62
|
+
if(this.#logger) return this.#logger
|
|
63
|
+
const names = [this.name];
|
|
64
|
+
let temp=this as Dependency<Plugin>
|
|
65
|
+
while(temp.parent){
|
|
66
|
+
names.unshift(temp.parent.name)
|
|
67
|
+
temp=temp.parent
|
|
68
|
+
}
|
|
69
|
+
return this.#logger=this.app.getLogger(...names)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 添加中间件 */
|
|
73
|
+
addMiddleware(middleware: MessageMiddleware): void {
|
|
74
|
+
this.middlewares.push(middleware);
|
|
75
|
+
this.dispatch('middleware.add',middleware)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 添加事件监听器 */
|
|
79
|
+
addEventListener<T = any>(event: string, listener: EventListener<T>): void {
|
|
80
|
+
if (!this.eventListeners.has(event)) {
|
|
81
|
+
this.eventListeners.set(event, []);
|
|
82
|
+
}
|
|
83
|
+
this.eventListeners.get(event)!.push(listener);
|
|
84
|
+
this.dispatch('listener.add',event,listener)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 添加定时任务 */
|
|
88
|
+
addCronJob(job: CronJob): void {
|
|
89
|
+
this.cronJobs.set(job.name, job);
|
|
90
|
+
this.dispatch('cron-job.add',job)
|
|
91
|
+
}
|
|
92
|
+
/** 发送消息 */
|
|
93
|
+
async sendMessage(options:SendOptions): Promise<void> {
|
|
94
|
+
await this.app.sendMessage(options);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** 销毁插件 */
|
|
98
|
+
dispose(): void {
|
|
99
|
+
// 移除所有中间件
|
|
100
|
+
for (const middleware of this.middlewares) {
|
|
101
|
+
this.dispatch('middleware.remove', middleware)
|
|
102
|
+
}
|
|
103
|
+
this.middlewares = []
|
|
104
|
+
|
|
105
|
+
// 移除所有事件监听器
|
|
106
|
+
for (const [event, listeners] of this.eventListeners) {
|
|
107
|
+
for (const listener of listeners) {
|
|
108
|
+
this.dispatch('listener.remove', event, listener)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.eventListeners.clear()
|
|
112
|
+
|
|
113
|
+
// 移除所有定时任务
|
|
114
|
+
for (const [name, job] of this.cronJobs) {
|
|
115
|
+
this.dispatch('cron-job.remove', job)
|
|
116
|
+
}
|
|
117
|
+
this.cronJobs.clear()
|
|
118
|
+
|
|
119
|
+
// 调用父类的dispose方法
|
|
120
|
+
super.dispose()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 更新 tsconfig.json 的类型声明
|
|
7
|
+
* @param cwd 项目根目录
|
|
8
|
+
*/
|
|
9
|
+
export async function generateEnvTypes(cwd: string): Promise<void> {
|
|
10
|
+
try {
|
|
11
|
+
// 基础类型集合
|
|
12
|
+
const types = new Set(['@zhin.js/types']);
|
|
13
|
+
|
|
14
|
+
// 检查 package.json 中的依赖
|
|
15
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
16
|
+
if (fs.existsSync(pkgPath)) {
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
18
|
+
const allDeps = {
|
|
19
|
+
...(pkg.dependencies || {}),
|
|
20
|
+
...(pkg.devDependencies || {})
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// 检查所有 @zhin.js/ 开头的包
|
|
24
|
+
for (const [name] of Object.entries(allDeps)) {
|
|
25
|
+
if (name.startsWith('@zhin.js/') || name === 'zhin.js') {
|
|
26
|
+
try {
|
|
27
|
+
const depPkgPath = path.join(cwd, 'node_modules', name, 'package.json');
|
|
28
|
+
if (fs.existsSync(depPkgPath)) {
|
|
29
|
+
const depPkg = JSON.parse(fs.readFileSync(depPkgPath, 'utf-8'));
|
|
30
|
+
if (depPkg.types || depPkg.typings) {
|
|
31
|
+
types.add(name);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
// 如果读取失败,跳过这个包
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 更新或创建 tsconfig.json
|
|
43
|
+
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
44
|
+
let tsconfig:Record<string,any> = {};
|
|
45
|
+
|
|
46
|
+
// 读取现有的 tsconfig.json
|
|
47
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
48
|
+
try {
|
|
49
|
+
tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, 'utf-8'));
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(err)
|
|
52
|
+
logger.warn('⚠️ Failed to parse tsconfig.json, creating new one');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 确保 compilerOptions 存在
|
|
57
|
+
if (!tsconfig.compilerOptions) {
|
|
58
|
+
tsconfig.compilerOptions = {};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 合并现有的 types
|
|
62
|
+
const existingTypes = tsconfig.compilerOptions.types || [];
|
|
63
|
+
const allTypes = new Set([...existingTypes, ...types]);
|
|
64
|
+
|
|
65
|
+
// 更新 types 字段
|
|
66
|
+
tsconfig.compilerOptions.types = Array.from(allTypes);
|
|
67
|
+
|
|
68
|
+
// 写入文件
|
|
69
|
+
fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2), 'utf-8');
|
|
70
|
+
logger.info('✅ Updated TypeScript types configuration');
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logger.warn('⚠️ Failed to update TypeScript types:', error);
|
|
73
|
+
}
|
|
74
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {MaybePromise}from '@zhin.js/types'
|
|
2
|
+
|
|
3
|
+
export interface MessageSegment<T extends keyof Segment=keyof Segment> {
|
|
4
|
+
type: T;
|
|
5
|
+
data: Segment[T];
|
|
6
|
+
}
|
|
7
|
+
export interface Segment{
|
|
8
|
+
[key:string]:Record<string, any>
|
|
9
|
+
any:Record<string, any>
|
|
10
|
+
}
|
|
11
|
+
export type MaybeArray<T>=T|T[]
|
|
12
|
+
export type SendContent=MaybeArray<string|MessageSegment>
|
|
13
|
+
export interface MessageSender{
|
|
14
|
+
id: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
}
|
|
17
|
+
export type MessageComponent<T extends object>=(props:T&{children:SendContent})=>MaybePromise<SendContent>
|
|
18
|
+
export interface MessageChannel{
|
|
19
|
+
id: string;
|
|
20
|
+
type: 'group' | 'private' | 'channel';
|
|
21
|
+
}
|
|
22
|
+
export interface Message {
|
|
23
|
+
id: string;
|
|
24
|
+
adapter:string
|
|
25
|
+
bot:string
|
|
26
|
+
content: MessageSegment[];
|
|
27
|
+
sender: MessageSender;
|
|
28
|
+
reply(content:SendContent,quote?:boolean|string):Promise<void>
|
|
29
|
+
channel: MessageChannel;
|
|
30
|
+
timestamp: number;
|
|
31
|
+
raw: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface User {
|
|
35
|
+
user_id: string;
|
|
36
|
+
nickname: string;
|
|
37
|
+
card?: string;
|
|
38
|
+
role?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Group {
|
|
42
|
+
group_id: string;
|
|
43
|
+
group_name: string;
|
|
44
|
+
member_count: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface BotConfig {
|
|
48
|
+
name: string;
|
|
49
|
+
context: string;
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
export interface AppConfig {
|
|
56
|
+
/** 机器人配置列表 */
|
|
57
|
+
bots?: BotConfig[];
|
|
58
|
+
/** 插件目录列表,默认为 ['./plugins', 'node_modules'] */
|
|
59
|
+
plugin_dirs?: string[];
|
|
60
|
+
/** 需要加载的插件列表 */
|
|
61
|
+
plugins?: string[];
|
|
62
|
+
/** 禁用的依赖列表 */
|
|
63
|
+
disable_dependencies?: string[];
|
|
64
|
+
/** 是否启用调试模式 */
|
|
65
|
+
debug?: boolean;
|
|
66
|
+
}
|
|
67
|
+
export type DefineConfig<T> = T | ((env:Record<string,string>)=>MaybePromise<T>);
|
|
68
|
+
|
|
69
|
+
export interface SendOptions extends MessageChannel{
|
|
70
|
+
context:string
|
|
71
|
+
bot:string
|
|
72
|
+
content:SendContent
|
|
73
|
+
}
|
|
74
|
+
export type BeforeSendHandler=(options:SendOptions)=>MaybePromise<SendOptions|void>
|