@zhin.js/core 1.0.3 → 1.0.4

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,7 +81,7 @@ 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));
@@ -103,6 +107,9 @@ export class App extends HMR<Plugin> {
103
107
  bots: [],
104
108
  debug: false,
105
109
  };
110
+ middleware(middleware: MessageMiddleware) {
111
+ this.middlewares.push(middleware)
112
+ }
106
113
  /**
107
114
  * 发送消息到指定适配器和机器人
108
115
  * @param options 消息发送参数(包含 context、bot、内容等)
@@ -119,7 +126,7 @@ export class App extends HMR<Plugin> {
119
126
  );
120
127
  return bot.$sendMessage(options);
121
128
  }
122
- async recallMessage(adapter_name:string,bot_name:string,id:string){
129
+ async recallMessage(adapter_name: string, bot_name: string, id: string) {
123
130
  const adapter = this.getContext<Adapter>(adapter_name);
124
131
  if (!adapter)
125
132
  throw new Error(`can't find adapter for name ${adapter_name}`);
@@ -188,14 +195,15 @@ export class App extends HMR<Plugin> {
188
195
 
189
196
  this.logger.info("App configuration updated", this.config);
190
197
  }
191
- get schemas(){
198
+ get schemas() {
192
199
  return this.dependencyList.reduce((result, plugin) => {
193
200
  plugin.schemas.forEach((schema, name) => {
194
201
  result.set(name, schema);
195
202
  });
196
203
  return result;
197
- }, new Map<string,Schema<any>>([
198
- ['SystemLog', SystemLogSchema]
204
+ }, new Map<string, Schema<any>>([
205
+ ['SystemLog', SystemLogSchema],
206
+ ['User', UserSchema]
199
207
  ]));
200
208
  }
201
209
  /** 使用插件 */
@@ -211,15 +219,15 @@ export class App extends HMR<Plugin> {
211
219
  this.use(pluginName);
212
220
  }
213
221
  await sleep(200);
214
- const schemas:Record<string,Schema>={};
222
+ const schemas: Record<string, Schema> = {};
215
223
  for (const [name, schema] of this.schemas) {
216
- schemas[name]=schema;
224
+ schemas[name] = schema;
217
225
  }
218
226
  if (this.config.database) {
219
- this.database=Registry.create((this.config.database as any).dialect,this.config.database,schemas);
227
+ this.database = Registry.create((this.config.database as any).dialect, this.config.database, schemas);
220
228
  await this.database?.start();
221
229
  this.logger.info(`database init success`);
222
-
230
+
223
231
  // 初始化日志传输器
224
232
  this.logTransport = new DatabaseLogTransport(this);
225
233
  addTransport(this.logTransport);
@@ -227,7 +235,7 @@ export class App extends HMR<Plugin> {
227
235
  } else {
228
236
  this.logger.info(`database not configured, skipping database init`);
229
237
  }
230
- this.dispatch("database.ready",this.database);
238
+ this.dispatch("database.ready", this.database);
231
239
  // 等待所有插件就绪
232
240
  await this.waitForReady();
233
241
  this.logger.info("started successfully");
@@ -237,14 +245,14 @@ export class App extends HMR<Plugin> {
237
245
  /** 停止App */
238
246
  async stop(): Promise<void> {
239
247
  this.logger.info("Stopping app...");
240
-
248
+
241
249
  // 停止日志清理任务并移除日志传输器
242
250
  if (this.logTransport) {
243
251
  this.logTransport.stopCleanup();
244
252
  removeTransport(this.logTransport);
245
253
  this.logger.info("database log transport removed");
246
254
  }
247
-
255
+
248
256
  // 销毁所有插件
249
257
  this.dispose();
250
258
 
@@ -315,7 +323,10 @@ export function defineModel<T extends Record<string, any>>(
315
323
  const plugin = usePlugin();
316
324
  return plugin.defineModel(name, schema);
317
325
  }
318
-
326
+ export function addPermit<T extends RegisteredAdapter>(name: string | RegExp, checker: PermissionChecker<T>) {
327
+ const plugin = usePlugin()
328
+ return plugin.addPermit(name, checker)
329
+ }
319
330
  /** 获取当前插件实例 */
320
331
  export function usePlugin(): Plugin {
321
332
  const hmr = HMR.currentHMR;
@@ -371,7 +382,7 @@ export function addMiddleware(middleware: MessageMiddleware): void {
371
382
  const plugin = usePlugin();
372
383
  plugin.addMiddleware(middleware);
373
384
  }
374
- export function onDatabaseReady(callback: (database: RelatedDatabase<any,Models>|DocumentDatabase<any,Models>|KeyValueDatabase<any,Models>) => PromiseLike<void>) {
385
+ export function onDatabaseReady(callback: (database: RelatedDatabase<any, Models> | DocumentDatabase<any, Models> | KeyValueDatabase<any, Models>) => PromiseLike<void>) {
375
386
  const plugin = usePlugin();
376
387
  if (plugin.app.database?.isStarted) callback(plugin.app.database);
377
388
  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
  * 通用字典类型