@zhin.js/plugin-group-suite 1.0.0 → 1.1.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.
Files changed (138) hide show
  1. package/README.md +42 -58
  2. package/agent/tools/group_announce.ts +12 -0
  3. package/commands/checkin.js +11 -0
  4. package/commands/checkin.ts +12 -0
  5. package/commands/forget.js +10 -0
  6. package/commands/forget.ts +12 -0
  7. package/commands/keyword-add/[keyword].js +16 -0
  8. package/commands/keyword-add/[keyword].ts +16 -0
  9. package/commands/keyword-list.js +14 -0
  10. package/commands/keyword-list.ts +14 -0
  11. package/commands/keyword-remove/[keyword].js +16 -0
  12. package/commands/keyword-remove/[keyword].ts +15 -0
  13. package/commands/mypoints.js +10 -0
  14. package/commands/mypoints.ts +12 -0
  15. package/commands/mystats.js +10 -0
  16. package/commands/mystats.ts +12 -0
  17. package/commands/rank.js +11 -0
  18. package/commands/rank.ts +12 -0
  19. package/commands/stats-rank.js +13 -0
  20. package/commands/stats-rank.ts +20 -0
  21. package/commands/stats-week.js +13 -0
  22. package/commands/stats-week.ts +20 -0
  23. package/commands/stats.js +14 -0
  24. package/commands/stats.ts +21 -0
  25. package/commands/teach-list/[[page]].js +13 -0
  26. package/commands/teach-list/[[page]].ts +14 -0
  27. package/commands/teach-regex.js +11 -0
  28. package/commands/teach-regex.ts +12 -0
  29. package/commands/teach.js +11 -0
  30. package/commands/teach.ts +12 -0
  31. package/lib/analysis.d.ts +1 -1
  32. package/lib/analysis.d.ts.map +1 -1
  33. package/lib/checkin-lib.d.ts +27 -0
  34. package/lib/checkin-lib.d.ts.map +1 -0
  35. package/lib/checkin-lib.js +134 -0
  36. package/lib/checkin-lib.js.map +1 -0
  37. package/lib/config.d.ts +4 -57
  38. package/lib/config.d.ts.map +1 -1
  39. package/lib/config.js +18 -40
  40. package/lib/config.js.map +1 -1
  41. package/lib/db-store.d.ts +12 -0
  42. package/lib/db-store.d.ts.map +1 -0
  43. package/lib/db-store.js +38 -0
  44. package/lib/db-store.js.map +1 -0
  45. package/lib/index.d.ts +9 -1
  46. package/lib/index.d.ts.map +1 -1
  47. package/lib/index.js +7 -29
  48. package/lib/index.js.map +1 -1
  49. package/lib/keyword-store.d.ts +6 -0
  50. package/lib/keyword-store.d.ts.map +1 -0
  51. package/lib/keyword-store.js +21 -0
  52. package/lib/keyword-store.js.map +1 -0
  53. package/lib/memory-store.d.ts +31 -0
  54. package/lib/memory-store.d.ts.map +1 -0
  55. package/lib/memory-store.js +63 -0
  56. package/lib/memory-store.js.map +1 -0
  57. package/lib/runtime-state.d.ts +25 -0
  58. package/lib/runtime-state.d.ts.map +1 -0
  59. package/lib/runtime-state.js +21 -0
  60. package/lib/runtime-state.js.map +1 -0
  61. package/lib/shared-runtime.d.ts +26 -0
  62. package/lib/shared-runtime.d.ts.map +1 -0
  63. package/lib/shared-runtime.js +34 -0
  64. package/lib/shared-runtime.js.map +1 -0
  65. package/lib/stats-lib.d.ts +41 -0
  66. package/lib/stats-lib.d.ts.map +1 -0
  67. package/lib/stats-lib.js +177 -0
  68. package/lib/stats-lib.js.map +1 -0
  69. package/lib/tables.d.ts +4 -0
  70. package/lib/tables.d.ts.map +1 -0
  71. package/lib/tables.js +40 -0
  72. package/lib/tables.js.map +1 -0
  73. package/lib/teach-lib.d.ts +25 -0
  74. package/lib/teach-lib.d.ts.map +1 -0
  75. package/lib/teach-lib.js +233 -0
  76. package/lib/teach-lib.js.map +1 -0
  77. package/middlewares/keyword-reply.js +36 -0
  78. package/middlewares/keyword-reply.ts +37 -0
  79. package/middlewares/stats-count.js +20 -0
  80. package/middlewares/stats-count.ts +22 -0
  81. package/middlewares/teach-reply.js +21 -0
  82. package/middlewares/teach-reply.ts +22 -0
  83. package/package.json +53 -14
  84. package/plugin.js +40 -0
  85. package/schema.json +17 -0
  86. package/src/analysis.ts +1 -1
  87. package/src/checkin-lib.ts +165 -0
  88. package/src/config.ts +23 -57
  89. package/src/db-store.ts +51 -0
  90. package/src/index.ts +47 -33
  91. package/src/keyword-store.ts +24 -0
  92. package/src/memory-store.ts +90 -0
  93. package/src/runtime-state.ts +44 -0
  94. package/src/shared-runtime.ts +44 -0
  95. package/src/stats-lib.ts +205 -0
  96. package/src/tables.ts +46 -0
  97. package/src/teach-lib.ts +301 -0
  98. package/tools/checkin-query/handler.js +22 -0
  99. package/tools/checkin-query/handler.ts +2 -2
  100. package/tools/checkin-rank/handler.js +15 -0
  101. package/tools/checkin-rank/handler.ts +1 -1
  102. package/tools/stats-query/handler.js +28 -0
  103. package/tools/stats-query/handler.ts +2 -2
  104. package/tools/stats-user/handler.js +29 -0
  105. package/tools/stats-user/handler.ts +3 -2
  106. package/lib/admin.d.ts +0 -4
  107. package/lib/admin.d.ts.map +0 -1
  108. package/lib/admin.js +0 -123
  109. package/lib/admin.js.map +0 -1
  110. package/lib/checkin.d.ts +0 -5
  111. package/lib/checkin.d.ts.map +0 -1
  112. package/lib/checkin.js +0 -168
  113. package/lib/checkin.js.map +0 -1
  114. package/lib/daily-analysis.d.ts +0 -7
  115. package/lib/daily-analysis.d.ts.map +0 -1
  116. package/lib/daily-analysis.js +0 -334
  117. package/lib/daily-analysis.js.map +0 -1
  118. package/lib/shared.d.ts +0 -25
  119. package/lib/shared.d.ts.map +0 -1
  120. package/lib/shared.js +0 -40
  121. package/lib/shared.js.map +0 -1
  122. package/lib/stats.d.ts +0 -12
  123. package/lib/stats.d.ts.map +0 -1
  124. package/lib/stats.js +0 -256
  125. package/lib/stats.js.map +0 -1
  126. package/lib/teach.d.ts +0 -4
  127. package/lib/teach.d.ts.map +0 -1
  128. package/lib/teach.js +0 -294
  129. package/lib/teach.js.map +0 -1
  130. package/plugin.yml +0 -3
  131. package/src/admin.ts +0 -145
  132. package/src/checkin.ts +0 -196
  133. package/src/daily-analysis.ts +0 -458
  134. package/src/shared.ts +0 -51
  135. package/src/stats.ts +0 -297
  136. package/src/teach.ts +0 -307
  137. /package/{skills/checkin/SKILL.md → agent/skills/checkin.md} +0 -0
  138. /package/{skills/group-daily-analysis/SKILL.md → agent/skills/group-daily-analysis.md} +0 -0
@@ -1,458 +0,0 @@
1
- /**
2
- * 群日常分析(group-suite 子模块)
3
- */
4
- import { formatCompact, Cron, MessageCommand, segment, type Plugin } from "zhin.js";
5
- import type { InboxMessageRow } from "./analysis.js";
6
- import {
7
- buildAnalysisReportData,
8
- computeBasicStats,
9
- formatTextReport,
10
- appendLLMReport,
11
- parseLLMResponse,
12
- extractText,
13
- type AnalysisResult,
14
- type LLMAnalysis,
15
- } from "./analysis.js";
16
- import { buildAnalysisReportHtml, ANALYSIS_REPORT_CANVAS } from "./analysis-card.js";
17
-
18
- const INBOX_TABLE = "unified_inbox_message";
19
- const SETTINGS_TABLE = "group_daily_analysis_settings";
20
-
21
- import type { GroupSuiteConfig } from "./config.js";
22
-
23
- export function registerDailyAnalysis(
24
- plugin: Plugin,
25
- config: GroupSuiteConfig,
26
- ): void {
27
- const { logger, root, addCommand, useContext, addCron, onDispose } = plugin;
28
-
29
- // ─── 数据库与收件箱 ───────────────────────────────────────────────────────────
30
-
31
- let _db: any = null;
32
- let _settingsModel: any = null;
33
-
34
- function getInboxModel(): any {
35
- if (!_db) {
36
- const database = root.inject("database");
37
- if (database) _db = database;
38
- }
39
- return _db?.models?.get(INBOX_TABLE) ?? null;
40
- }
41
-
42
- function getSettingsModel(): any {
43
- return _db?.models?.get(SETTINGS_TABLE) ?? null;
44
- }
45
-
46
- function getChannelFromMessage(
47
- message: any,
48
- ): {
49
- channelId: string;
50
- channelType: string;
51
- adapter?: string;
52
- botId?: string;
53
- } | null {
54
- const ch = message?.$channel;
55
- if (!ch?.id) return null;
56
- return {
57
- channelId: String(ch.id),
58
- channelType: String(ch.type || "private"),
59
- adapter: message?.$adapter,
60
- botId: message?.$bot,
61
- };
62
- }
63
-
64
- useContext("database", (db) => {
65
- _db = db;
66
- db.define(SETTINGS_TABLE, {
67
- id: { type: "integer", primary: true, autoIncrement: true },
68
- channel_id: { type: "text", nullable: false },
69
- channel_type: { type: "text", nullable: false },
70
- adapter: { type: "text", default: "" },
71
- bot_id: { type: "text", default: "" },
72
- enabled: { type: "integer", default: 1 },
73
- updated_at: { type: "text", default: "" },
74
- });
75
- });
76
-
77
- // ─── 群组是否参与分析(白名单/黑名单 + 分析设置)──────────────────────────────
78
-
79
- function isGroupInList(groupId: string, list: string[]): boolean {
80
- return list.some((id) => id === groupId || id === String(groupId));
81
- }
82
-
83
- async function isAnalysisEnabledForChannel(
84
- channelId: string,
85
- channelType: string,
86
- adapter: string,
87
- ): Promise<boolean> {
88
- if (channelType !== "group") return false;
89
- if (
90
- config.analysisGroupsBlock.length > 0 &&
91
- isGroupInList(channelId, config.analysisGroupsBlock)
92
- )
93
- return false;
94
- if (
95
- config.analysisGroups.length > 0 &&
96
- !isGroupInList(channelId, config.analysisGroups)
97
- )
98
- return false;
99
- const Settings = getSettingsModel();
100
- if (!Settings) return true;
101
- try {
102
- const rows: any[] = await Settings.select().where({
103
- channel_id: channelId,
104
- channel_type: channelType,
105
- adapter: adapter || "",
106
- });
107
- if (rows.length === 0) return true;
108
- return (rows[0].enabled ?? 1) === 1;
109
- } catch {
110
- return true;
111
- }
112
- }
113
-
114
- async function setAnalysisEnabledForChannel(
115
- channelId: string,
116
- channelType: string,
117
- adapter: string,
118
- botId: string,
119
- enabled: boolean,
120
- ): Promise<void> {
121
- const Settings = getSettingsModel();
122
- if (!Settings) return;
123
- const ts = new Date().toISOString();
124
- const rows: any[] = await Settings.select().where({
125
- channel_id: channelId,
126
- channel_type: channelType,
127
- adapter: adapter || "",
128
- });
129
- if (rows.length > 0) {
130
- await Settings.update({
131
- enabled: enabled ? 1 : 0,
132
- updated_at: ts,
133
- bot_id: botId || rows[0].bot_id || "",
134
- }).where({ id: rows[0].id });
135
- } else {
136
- await Settings.insert({
137
- channel_id: channelId,
138
- channel_type: channelType,
139
- adapter: adapter || "",
140
- bot_id: botId || "",
141
- enabled: enabled ? 1 : 0,
142
- updated_at: ts,
143
- });
144
- }
145
- }
146
-
147
- // ─── 从收件箱查询消息并执行分析 ─────────────────────────────────────────────────
148
-
149
- function getTimeRangeDays(days: number): {
150
- start: number;
151
- end: number;
152
- startStr: string;
153
- endStr: string;
154
- } {
155
- const end = Date.now();
156
- const start = end - days * 24 * 60 * 60 * 1000;
157
- const dStart = new Date(start);
158
- const dEnd = new Date(end);
159
- const fmt = (d: Date) =>
160
- `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
161
- d.getDate(),
162
- ).padStart(2, "0")}`;
163
- return { start, end, startStr: fmt(dStart), endStr: fmt(dEnd) };
164
- }
165
-
166
- async function queryInboxMessages(
167
- channelId: string,
168
- adapter: string,
169
- botId: string,
170
- startTs: number,
171
- endTs: number,
172
- limit: number,
173
- ): Promise<InboxMessageRow[]> {
174
- const Inbox = getInboxModel();
175
- if (!Inbox) return [];
176
- try {
177
- let rows: any[];
178
- try {
179
- rows = await Inbox.select()
180
- .where({
181
- channel_id: channelId,
182
- channel_type: "group",
183
- adapter: adapter || "",
184
- bot_id: botId || "",
185
- created_at: { $gte: startTs, $lte: endTs },
186
- })
187
- .orderBy("created_at", "ASC")
188
- .limit(limit);
189
- } catch {
190
- rows = await Inbox.select().where({
191
- channel_id: channelId,
192
- channel_type: "group",
193
- adapter: adapter || "",
194
- bot_id: botId || "",
195
- });
196
- rows = (rows || [])
197
- .filter((r: any) => r.created_at >= startTs && r.created_at <= endTs)
198
- .sort((a: any, b: any) => a.created_at - b.created_at)
199
- .slice(0, limit);
200
- }
201
- return rows as InboxMessageRow[];
202
- } catch (e) {
203
- logger.warn(formatCompact( { op: "inbox", ok: false, error: (e as Error)?.message }));
204
- return [];
205
- }
206
- }
207
-
208
- async function runAnalysis(
209
- channelId: string,
210
- channelName: string,
211
- adapter: string,
212
- botId: string,
213
- days: number,
214
- ): Promise<AnalysisResult | string> {
215
- const Inbox = getInboxModel();
216
- if (!Inbox) {
217
- return "请先在配置中启用 inbox.enabled 并配置 database,以使用群日常分析。";
218
- }
219
- const { start, end, startStr, endStr } = getTimeRangeDays(days);
220
- const rows = await queryInboxMessages(
221
- channelId,
222
- adapter,
223
- botId,
224
- start,
225
- end,
226
- config.analysisMaxMessages,
227
- );
228
- if (rows.length === 0) {
229
- return `在 ${startStr} 至 ${endStr} 区间内没有找到本群消息记录,请确认收件箱已启用并有过群消息。`;
230
- }
231
- const stats = computeBasicStats(rows);
232
- const meta = {
233
- channelName: channelName || undefined,
234
- days,
235
- startDate: startStr,
236
- endDate: endStr,
237
- };
238
- let textReport = formatTextReport(stats, meta);
239
- let llm: LLMAnalysis | undefined;
240
-
241
- // 可选:LLM 话题/金句/用户画像(灵感来自 astrbot_plugin_qq_group_daily_analysis)
242
- const ai = root.inject("ai") as
243
- | { ask?: (q: string, opts?: { systemPrompt?: string }) => Promise<string> }
244
- | undefined;
245
- if (ai?.ask) {
246
- try {
247
- const sampleSize = Math.min(rows.length, 150);
248
- const sample = rows.slice(-sampleSize);
249
- const conversation = sample
250
- .map(
251
- (r) =>
252
- `[${r.sender_name || r.sender_id}]: ${extractText(r).slice(
253
- 0,
254
- 200,
255
- )}`,
256
- )
257
- .join("\n");
258
- const systemPrompt = `你是一个群聊分析助手。根据下面的群聊消息摘要,提取并仅返回一个 JSON 对象,包含以下三个数组(均为中文):
259
- - topics: 数组,每项 { "topic": "话题关键词", "summary": "简短说明" }
260
- - quotes: 数组,每项 { "content": "金句原文", "sender": "发言人", "reason": "入选理由" },最多 5 条
261
- - user_titles: 数组,每项 { "name": "昵称", "user_id": "id", "title": "称号/画像", "reason": "理由" },最多 8 人
262
- 不要输出除 JSON 以外的内容。`;
263
- const answer = await ai.ask(
264
- `请分析以下群聊片段并返回 JSON:\n\n${conversation.slice(0, 6000)}`,
265
- { systemPrompt },
266
- );
267
- if (answer && answer.trim()) {
268
- const parsed = parseLLMResponse(answer);
269
- if (
270
- parsed.topics?.length ||
271
- parsed.quotes?.length ||
272
- parsed.userTitles?.length
273
- ) {
274
- llm = parsed;
275
- textReport = appendLLMReport(textReport, llm);
276
- }
277
- }
278
- } catch (e) {
279
- logger.debug("[LLM 分析跳过", (e as Error)?.message);
280
- }
281
- }
282
-
283
- return { stats, textReport, llm, meta };
284
- }
285
-
286
- function analysisReportReply(report: AnalysisResult) {
287
- return segment.html({
288
- html: buildAnalysisReportHtml(buildAnalysisReportData(report.stats, report.meta, report.llm)),
289
- width: 540,
290
- backgroundColor: ANALYSIS_REPORT_CANVAS,
291
- fileName: "group-daily-analysis.png",
292
- });
293
- }
294
-
295
- // ─── 命令:群分析 ─────────────────────────────────────────────────────────────
296
-
297
- addCommand(
298
- new MessageCommand("群分析 [天数:number]")
299
- .desc("群日常分析", "分析本群近期消息统计(依赖收件箱)")
300
- .action(async (message: any, result: any) => {
301
- const ch = getChannelFromMessage(message);
302
- if (!ch || ch.channelType !== "group") {
303
- return "请在群聊中使用本命令。";
304
- }
305
- const days = Math.min(
306
- 30,
307
- Math.max(1, Number(result.params?.天数) || config.analysisDays),
308
- );
309
- const channelName = (message?.$channel as { name?: string })?.name || "";
310
- const report = await runAnalysis(
311
- ch.channelId,
312
- channelName,
313
- ch.adapter || "",
314
- ch.botId || "",
315
- days,
316
- );
317
- if (typeof report === "string") return report;
318
- return analysisReportReply(report);
319
- }),
320
- );
321
-
322
- // ─── 命令:分析设置 ───────────────────────────────────────────────────────────
323
-
324
- addCommand(
325
- new MessageCommand("分析设置 [操作:text]")
326
- .desc("分析设置", "enable=启用本群分析 / disable=禁用 / status=查看状态")
327
- .action(async (message: any, result: any) => {
328
- const ch = getChannelFromMessage(message);
329
- if (!ch || ch.channelType !== "group") {
330
- return "请在群聊中使用本命令。";
331
- }
332
- const op = (result.params?.操作 || "status").trim().toLowerCase();
333
- const adapter = ch.adapter || "";
334
- const botId = ch.botId || "";
335
- if (op === "enable") {
336
- await setAnalysisEnabledForChannel(
337
- ch.channelId,
338
- ch.channelType,
339
- adapter,
340
- botId,
341
- true,
342
- );
343
- return "已为本群启用日常分析。";
344
- }
345
- if (op === "disable") {
346
- await setAnalysisEnabledForChannel(
347
- ch.channelId,
348
- ch.channelType,
349
- adapter,
350
- botId,
351
- false,
352
- );
353
- return "已为本群关闭日常分析。";
354
- }
355
- if (op === "status") {
356
- const enabled = await isAnalysisEnabledForChannel(
357
- ch.channelId,
358
- ch.channelType,
359
- adapter,
360
- );
361
- return `本群日常分析:${enabled ? "已启用" : "已关闭"}`;
362
- }
363
- return "用法:分析设置 enable | disable | status";
364
- }),
365
- );
366
-
367
- // ─── 定时任务(可选)───────────────────────────────────────────────────────────
368
-
369
- let cronDispose: (() => void) | null = null;
370
-
371
- useContext("database", () => {
372
- if (!config.autoAnalysisEnabled || cronDispose) return;
373
- if (typeof addCron !== "function") return;
374
- try {
375
- cronDispose = addCron(
376
- new Cron(config.autoAnalysisCron, async () => {
377
- const inject = root.inject?.bind(root) as (key: string) => any;
378
- if (typeof inject !== "function") return;
379
- const targets: { channelId: string; adapter: string; botId: string }[] =
380
- [];
381
- const Settings = getSettingsModel();
382
- if (Settings) {
383
- const rows: any[] = await Settings.select().where({
384
- channel_type: "group",
385
- enabled: 1,
386
- });
387
- for (const r of rows) {
388
- if (r.channel_id && r.adapter)
389
- targets.push({
390
- channelId: String(r.channel_id),
391
- adapter: String(r.adapter),
392
- botId: String(r.bot_id || ""),
393
- });
394
- }
395
- }
396
- for (const gid of config.analysisGroups) {
397
- if (targets.some((t) => t.channelId === gid)) continue;
398
- const adapters = (root as any).adapters as string[] | undefined;
399
- const adapterName =
400
- Array.isArray(adapters) && adapters[0] ? adapters[0] : "";
401
- if (adapterName)
402
- targets.push({
403
- channelId: String(gid),
404
- adapter: adapterName,
405
- botId: "",
406
- });
407
- }
408
- for (const t of targets) {
409
- if (
410
- config.analysisGroupsBlock.length &&
411
- isGroupInList(t.channelId, config.analysisGroupsBlock)
412
- )
413
- continue;
414
- try {
415
- const report = await runAnalysis(
416
- t.channelId,
417
- "",
418
- t.adapter,
419
- t.botId,
420
- config.analysisDays,
421
- );
422
- if (typeof report === "string") continue;
423
- const adapter = inject(t.adapter);
424
- if (adapter?.sendMessage) {
425
- let botId = t.botId;
426
- if (!botId && adapter.bots?.size) {
427
- const first = adapter.bots.values().next().value;
428
- botId = first?.$id ?? first?.selfId ?? "";
429
- }
430
- const content = analysisReportReply(report);
431
- await adapter
432
- .sendMessage({
433
- context: t.adapter,
434
- bot: botId,
435
- type: "group",
436
- id: t.channelId,
437
- content,
438
- })
439
- .catch(() => {});
440
- }
441
- } catch (e) {
442
- logger.warn(formatCompact( { op: "send", channel: t.channelId, ok: false, error: (e as Error)?.message }));
443
- }
444
- }
445
- }),
446
- );
447
- } catch (e) {
448
- logger.warn(formatCompact( { op: "cron", ok: false, error: (e as Error)?.message }));
449
- }
450
- });
451
-
452
- onDispose(() => {
453
- if (cronDispose) {
454
- cronDispose();
455
- cronDispose = null;
456
- }
457
- });
458
- }
package/src/shared.ts DELETED
@@ -1,51 +0,0 @@
1
- import { segment, type Message, type Plugin } from "zhin.js";
2
-
3
- export function todayStr(): string {
4
- const d = new Date();
5
- return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
6
- }
7
-
8
- export function ts(): string {
9
- return new Date().toISOString();
10
- }
11
-
12
- /** 入站纯文本(与 AI trigger 一致:segment.toString($content),否则 $raw) */
13
- export function extractInboundText(message: Pick<Message, "$raw" | "$content">): string {
14
- if (message.$content?.length) {
15
- const fromContent = segment.toString(message.$content).trim();
16
- if (fromContent) return fromContent;
17
- }
18
- return (message.$raw ?? "").trim();
19
- }
20
-
21
- /** 消息所属群/全局上下文 */
22
- export function getMessageContextKey(message: {
23
- $channel?: { type?: string; id?: string | number };
24
- type?: string;
25
- $group?: { id?: string | number };
26
- $target?: { id?: string | number };
27
- }): { type: string; id: string } {
28
- const channelType = message.$channel?.type ?? message.type;
29
- if (channelType === "group") {
30
- const id =
31
- message.$channel?.id ??
32
- message.$group?.id ??
33
- message.$target?.id ??
34
- "";
35
- return { type: "group", id: String(id) };
36
- }
37
- return { type: "global", id: "" };
38
- }
39
-
40
- export function adapterAllowed(
41
- allowed: string[] | undefined,
42
- adapter: string | undefined,
43
- ): boolean {
44
- if (!allowed?.length) return true;
45
- if (!adapter) return false;
46
- return allowed.includes(adapter);
47
- }
48
-
49
- export function getDatabase(plugin: Plugin): any {
50
- return plugin.root.inject("database" as never) as any;
51
- }