@zhin.js/core 1.0.3 → 1.0.5

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/src/app.ts CHANGED
@@ -19,20 +19,22 @@ import { fileURLToPath } from "url";
19
19
  import { generateEnvTypes } from "./types-generator.js";
20
20
  import logger, { setName } from "@zhin.js/logger";
21
21
  import { sleep } from "./utils.js";
22
-
22
+ import { PermissionChecker, Permissions } from "./permissions.js";
23
23
  // 创建静态logger用于配置加载等静态操作
24
24
  setName("Zhin");
25
25
  import { MessageMiddleware, Plugin } from "./plugin.js";
26
26
  import { Adapter } from "./adapter";
27
27
  import { MessageCommand } from "./command";
28
28
  import { Component } from "./component";
29
- import { RelatedDatabase,DocumentDatabase,KeyValueDatabase,Schema,Registry} from "@zhin.js/database";
29
+ import { RelatedDatabase, DocumentDatabase, KeyValueDatabase, Schema, Registry } from "@zhin.js/database";
30
30
  import { DatabaseLogTransport } from "./log-transport.js";
31
31
  import { SystemLog, SystemLogSchema } from "./models/system-log.js";
32
+ import { User, UserSchema } from './models/user.js'
32
33
  import { addTransport, removeTransport } from "@zhin.js/logger";
33
- declare module "@zhin.js/types"{
34
- interface Models{
35
- SystemLog:SystemLog;
34
+ declare module "@zhin.js/types" {
35
+ interface Models {
36
+ SystemLog: SystemLog;
37
+ User: User
36
38
  }
37
39
  }
38
40
 
@@ -46,8 +48,10 @@ declare module "@zhin.js/types"{
46
48
  export class App extends HMR<Plugin> {
47
49
  static currentPlugin: Plugin;
48
50
  private config: AppConfig;
51
+ middlewares: MessageMiddleware[] = [];
49
52
  adapters: string[] = [];
50
- database?: RelatedDatabase<any,Models>|DocumentDatabase<any,Models>|KeyValueDatabase<any,Models>;
53
+ database?: RelatedDatabase<any, Models> | DocumentDatabase<any, Models> | KeyValueDatabase<any, Models>;
54
+ permissions: Permissions = new Permissions(this);
51
55
  private logTransport?: DatabaseLogTransport;
52
56
  /**
53
57
  * 构造函数:初始化应用,加载配置,注册全局异常处理
@@ -77,15 +81,17 @@ export class App extends HMR<Plugin> {
77
81
  super("Zhin", {
78
82
  logger,
79
83
  dirs: finalConfig.plugin_dirs || [],
80
- extensions: new Set([".js", ".ts",".jsx",".tsx"]),
84
+ extensions: new Set([".js", ".ts", ".jsx", ".tsx"]),
81
85
  debug: finalConfig.debug,
82
86
  });
83
87
  this.on("message.send", this.sendMessage.bind(this));
84
88
  process.on("uncaughtException", (e) => {
85
- this.logger.error(e);
89
+ const args=e instanceof Error ? [e.message,{stack:e.stack}] : [e];
90
+ this.logger.error(...args);
86
91
  });
87
92
  process.on("unhandledRejection", (e) => {
88
- this.logger.error(e);
93
+ const args=e instanceof Error ? [e.message,{stack:e.stack}] : [e];
94
+ this.logger.error(...args);
89
95
  });
90
96
  this.config = finalConfig;
91
97
  }
@@ -103,6 +109,9 @@ export class App extends HMR<Plugin> {
103
109
  bots: [],
104
110
  debug: false,
105
111
  };
112
+ middleware(middleware: MessageMiddleware) {
113
+ this.middlewares.push(middleware)
114
+ }
106
115
  /**
107
116
  * 发送消息到指定适配器和机器人
108
117
  * @param options 消息发送参数(包含 context、bot、内容等)
@@ -119,7 +128,7 @@ export class App extends HMR<Plugin> {
119
128
  );
120
129
  return bot.$sendMessage(options);
121
130
  }
122
- async recallMessage(adapter_name:string,bot_name:string,id:string){
131
+ async recallMessage(adapter_name: string, bot_name: string, id: string) {
123
132
  const adapter = this.getContext<Adapter>(adapter_name);
124
133
  if (!adapter)
125
134
  throw new Error(`can't find adapter for name ${adapter_name}`);
@@ -188,14 +197,15 @@ export class App extends HMR<Plugin> {
188
197
 
189
198
  this.logger.info("App configuration updated", this.config);
190
199
  }
191
- get schemas(){
200
+ get schemas() {
192
201
  return this.dependencyList.reduce((result, plugin) => {
193
202
  plugin.schemas.forEach((schema, name) => {
194
203
  result.set(name, schema);
195
204
  });
196
205
  return result;
197
- }, new Map<string,Schema<any>>([
198
- ['SystemLog', SystemLogSchema]
206
+ }, new Map<string, Schema<any>>([
207
+ ['SystemLog', SystemLogSchema],
208
+ ['User', UserSchema]
199
209
  ]));
200
210
  }
201
211
  /** 使用插件 */
@@ -211,15 +221,15 @@ export class App extends HMR<Plugin> {
211
221
  this.use(pluginName);
212
222
  }
213
223
  await sleep(200);
214
- const schemas:Record<string,Schema>={};
224
+ const schemas: Record<string, Schema> = {};
215
225
  for (const [name, schema] of this.schemas) {
216
- schemas[name]=schema;
226
+ schemas[name] = schema;
217
227
  }
218
228
  if (this.config.database) {
219
- this.database=Registry.create((this.config.database as any).dialect,this.config.database,schemas);
229
+ this.database = Registry.create((this.config.database as any).dialect, this.config.database, schemas);
220
230
  await this.database?.start();
221
231
  this.logger.info(`database init success`);
222
-
232
+
223
233
  // 初始化日志传输器
224
234
  this.logTransport = new DatabaseLogTransport(this);
225
235
  addTransport(this.logTransport);
@@ -227,7 +237,7 @@ export class App extends HMR<Plugin> {
227
237
  } else {
228
238
  this.logger.info(`database not configured, skipping database init`);
229
239
  }
230
- this.dispatch("database.ready",this.database);
240
+ this.dispatch("database.ready", this.database);
231
241
  // 等待所有插件就绪
232
242
  await this.waitForReady();
233
243
  this.logger.info("started successfully");
@@ -237,14 +247,14 @@ export class App extends HMR<Plugin> {
237
247
  /** 停止App */
238
248
  async stop(): Promise<void> {
239
249
  this.logger.info("Stopping app...");
240
-
250
+
241
251
  // 停止日志清理任务并移除日志传输器
242
252
  if (this.logTransport) {
243
253
  this.logTransport.stopCleanup();
244
254
  removeTransport(this.logTransport);
245
255
  this.logger.info("database log transport removed");
246
256
  }
247
-
257
+
248
258
  // 销毁所有插件
249
259
  this.dispose();
250
260
 
@@ -315,7 +325,10 @@ export function defineModel<T extends Record<string, any>>(
315
325
  const plugin = usePlugin();
316
326
  return plugin.defineModel(name, schema);
317
327
  }
318
-
328
+ export function addPermit<T extends RegisteredAdapter>(name: string | RegExp, checker: PermissionChecker<T>) {
329
+ const plugin = usePlugin()
330
+ return plugin.addPermit(name, checker)
331
+ }
319
332
  /** 获取当前插件实例 */
320
333
  export function usePlugin(): Plugin {
321
334
  const hmr = HMR.currentHMR;
@@ -371,7 +384,7 @@ export function addMiddleware(middleware: MessageMiddleware): void {
371
384
  const plugin = usePlugin();
372
385
  plugin.addMiddleware(middleware);
373
386
  }
374
- export function onDatabaseReady(callback: (database: RelatedDatabase<any,Models>|DocumentDatabase<any,Models>|KeyValueDatabase<any,Models>) => PromiseLike<void>) {
387
+ export function onDatabaseReady(callback: (database: RelatedDatabase<any, Models> | DocumentDatabase<any, Models> | KeyValueDatabase<any, Models>) => PromiseLike<void>) {
375
388
  const plugin = usePlugin();
376
389
  if (plugin.app.database?.isStarted) callback(plugin.app.database);
377
390
  plugin.on("database.ready", callback);
package/src/command.ts CHANGED
@@ -3,6 +3,8 @@ import {AdapterMessage, SendContent} from "./types.js";
3
3
  import {RegisteredAdapters} from "@zhin.js/types";
4
4
  import type {Message} from "./message.js";
5
5
  import {MaybePromise} from "@zhin.js/types";
6
+ import { Plugin } from "./plugin.js";
7
+ import { PluginError } from "./errors.js";
6
8
 
7
9
  /**
8
10
  * MessageCommand类:命令系统核心,基于segment-matcher实现。
@@ -10,15 +12,8 @@ import {MaybePromise} from "@zhin.js/types";
10
12
  */
11
13
  export class MessageCommand<T extends keyof RegisteredAdapters=keyof RegisteredAdapters> extends SegmentMatcher{
12
14
  #callbacks:MessageCommand.Callback<T>[]=[];
15
+ #permissions:string[]=[];
13
16
  #checkers:MessageCommand.Checker<T>[]=[]
14
- /**
15
- * 限定命令作用域(适配器名)
16
- * @param scopes 适配器名列表
17
- */
18
- scope<R extends T>(...scopes:R[]):MessageCommand<R>{
19
- this.#checkers.push((m)=>(scopes as string[]).includes(m.$adapter))
20
- return this as MessageCommand<R>
21
- }
22
17
  /**
23
18
  * 注册命令回调
24
19
  * @param callback 命令处理函数
@@ -27,12 +22,25 @@ export class MessageCommand<T extends keyof RegisteredAdapters=keyof RegisteredA
27
22
  this.#callbacks.push(callback)
28
23
  return this as MessageCommand<T>;
29
24
  }
25
+ permit(...permissions:string[]){
26
+ this.#permissions.push(...permissions)
27
+ return this as MessageCommand<T>;
28
+ }
30
29
  /**
31
30
  * 处理消息,自动匹配命令并执行回调
32
31
  * @param message 消息对象
32
+ * @param plugin 插件实例
33
33
  * @returns 命令返回内容或undefined
34
34
  */
35
- async handle(message:Message<AdapterMessage<T>>):Promise<SendContent|undefined>{
35
+ async handle(message:Message<AdapterMessage<T>>,plugin:Plugin):Promise<SendContent|undefined>{
36
+ for(const permission of this.#permissions){
37
+ const permit=plugin.getPermit(permission)
38
+ if(!permit) {
39
+ throw new PluginError(`权限 ${permission} 不存在`,plugin.name)
40
+ }
41
+ const result=await permit.check(permission,message)
42
+ if(!result) return;
43
+ }
36
44
  for(const check of this.#checkers){
37
45
  const result=await check(message)
38
46
  if(!result) return;
@@ -0,0 +1,15 @@
1
+ import { Schema } from '@zhin.js/database'
2
+ export interface User{
3
+ id:string
4
+ name?:string
5
+ password?:string
6
+ third_part:string[];
7
+ permissions?:string[]
8
+ }
9
+ export const UserSchema:Schema<User>={
10
+ id:{type:"text",nullable:false},
11
+ name:{type:'text',nullable:true},
12
+ password:{type:'text',nullable:true},
13
+ third_part:{type:'json',default:[]},
14
+ permissions:{type:'json',default:[]}
15
+ }
@@ -0,0 +1,62 @@
1
+ import { MaybePromise } from "@zhin.js/types";
2
+ import { RegisteredAdapter } from "./types.js";
3
+ import { Message } from "./message.js";
4
+ import { AdapterMessage } from "./types.js";
5
+ import { App } from "./app.js";
6
+ export type PermissionItem<T extends RegisteredAdapter = RegisteredAdapter> = {
7
+ name: string | RegExp
8
+ check: PermissionChecker<T>
9
+ }
10
+ export type PermissionChecker<T extends RegisteredAdapter = RegisteredAdapter> = (name: string, message: Message<AdapterMessage<T>>) => MaybePromise<boolean>
11
+ export class Permissions extends Array<PermissionItem>{
12
+ constructor(private readonly app: App) {
13
+ super();
14
+ this.add(Permissions.define(/^adapter\([^)]+\)$/, (name, message) => {
15
+ return message.$adapter === name.replace(/^adapter\(([^)]+)\)$/, '$1');
16
+ }));
17
+ this.add(Permissions.define(/^group\([^)]+\)$/, (name, message) => {
18
+ const match=name.match(/^group\(([^)]+)\)$/);
19
+ if(!match) return false;
20
+ const id=match[1];
21
+ if(message.$channel.type !== 'group') return false;
22
+ if(id===''||id==='*') return true;
23
+ return message.$channel.id === id;
24
+ }));
25
+ this.add(Permissions.define(/^private\([^)]+\)$/, (name, message) => {
26
+ const match=name.match(/^private\(([^)]+)\)$/);
27
+ if(!match) return false;
28
+ const id=match[1];
29
+ if(message.$channel.type !== 'private') return false;
30
+ if(id===''||id==='*') return true;
31
+ return message.$channel.id === id;
32
+ }));
33
+ this.add(Permissions.define(/^channel\([^)]+\)$/, (name, message) => {
34
+ const match=name.match(/^channel\(([^)]+)\)$/);
35
+ if(!match) return false;
36
+ const id=match[1];
37
+ if(message.$channel.type !== 'channel') return false;
38
+ if(id===''||id==='*') return true;
39
+ return message.$channel.id === id;
40
+ }));
41
+ this.add(Permissions.define(/^user\([^)]+\)$/,(name,message)=>{
42
+ const match=name.match(/^channel\(([^)]+)\)$/);
43
+ if(!match) return false;
44
+ const id=match[1];
45
+ return message.$sender.id===id;
46
+ }))
47
+ }
48
+ add(permission: PermissionItem) {
49
+ this.push(permission);
50
+ }
51
+ get(name: string): PermissionItem | undefined {
52
+ return this.app.dependencyList.reduce((result,dep)=>{
53
+ result.push(...dep.permissions)
54
+ return result;
55
+ },[...this]).find(p => new RegExp(p.name).test(name));
56
+ }
57
+ }
58
+ export namespace Permissions{
59
+ export function define<T extends RegisteredAdapter = RegisteredAdapter>(name: string | RegExp, check: PermissionChecker<T>): PermissionItem<T> {
60
+ return { name, check };
61
+ }
62
+ }
package/src/plugin.ts CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import {MaybePromise} from '@zhin.js/types'
7
7
  import {AdapterMessage, BeforeSendHandler, RegisteredAdapter, SendOptions} from "./types.js";
8
+ import { PermissionItem,PermissionChecker } from './permissions.js';
8
9
  import {Message} from './message.js'
9
10
  import {Dependency, Logger,} from "@zhin.js/hmr";
10
11
  import {App} from "./app";
@@ -31,6 +32,7 @@ export type MessageMiddleware<P extends RegisteredAdapter=RegisteredAdapter> = (
31
32
  export class Plugin extends Dependency<Plugin> {
32
33
  middlewares: MessageMiddleware<RegisteredAdapter>[] = [];
33
34
  components: Map<string, Component<any>> = new Map();
35
+ permissions: PermissionItem<RegisteredAdapter>[]=[];
34
36
  schemas: Map<string,Schema<any>>=new Map();
35
37
  commands:MessageCommand[]=[];
36
38
  crons:Cron[]=[];
@@ -48,7 +50,7 @@ export class Plugin extends Dependency<Plugin> {
48
50
  // 注册命令处理为默认中间件
49
51
  this.addMiddleware(async (message,next)=>{
50
52
  for(const command of this.commands){
51
- const result=await command.handle(message);
53
+ const result=await command.handle(message,this);
52
54
  if(result) message.$reply(result);
53
55
  }
54
56
  return next()
@@ -90,20 +92,26 @@ export class Plugin extends Dependency<Plugin> {
90
92
  try {
91
93
  await message.$reply('抱歉,处理您的消息时出现了错误。')
92
94
  } catch (replyError) {
93
- // 静默处理回复错误,避免错误循环
94
- // console.error 已替换为注释
95
95
  }
96
96
  }
97
97
  }
98
+ addPermit<T extends RegisteredAdapter>(name:string|RegExp,check:PermissionChecker<T>){
99
+ this.permissions.push({name,check});
100
+ return this;
101
+ }
102
+ getPermit<T extends RegisteredAdapter>(name:string):PermissionItem<T>|undefined{
103
+ return this.app.permissions.get(name);
104
+ }
98
105
  cron(cronExpression:string,callback:()=>void){
99
106
  const cronJob = new Cron(cronExpression,callback);
100
107
  this.crons.push(cronJob);
101
108
  return this;
102
109
  }
103
110
  async #runMiddlewares(message: Message, index: number): Promise<void> {
104
- if (index >= this.middlewares.length) return
111
+ const middlewareList=[...this.app.middlewares,...this.middlewares]
112
+ if (index >= middlewareList.length) return
105
113
 
106
- const middleware = this.middlewares[index]
114
+ const middleware = middlewareList[index]
107
115
 
108
116
  try {
109
117
  await middleware(message, () => this.#runMiddlewares(message, index + 1))
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {MaybePromise,RegisteredAdapters}from '@zhin.js/types'
2
- import {MessageChannel} from "./message.js";
2
+ import {MessageChannel,Message} from "./message.js";
3
3
  import {Adapter} from "./adapter.js";
4
4
  import {Bot,BotConfig} from "./bot.js";
5
5
  import { Databases,Registry } from "@zhin.js/database";
@@ -57,6 +57,7 @@ export type SendContent=MaybeArray<string|MessageElement>
57
57
  export interface MessageSender{
58
58
  id: string;
59
59
  name?: string;
60
+ permissions?:string[]
60
61
  }
61
62
  /**
62
63
  * 通用字典类型