mioku-plugin-admin 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/config.ts ADDED
@@ -0,0 +1,114 @@
1
+ import type { RecvAtElement, RecvElement, RecvImageElement } from "napcat-sdk";
2
+
3
+ export interface AdminConfig {
4
+ notifyTarget: number[];
5
+ notifyFriendMsg: boolean;
6
+ notifyFriendRequest: boolean;
7
+ notifyGroupInvite: boolean;
8
+ notifyGroupBan: boolean;
9
+ notifyGroupUnban: boolean;
10
+ notifyGroupKick: boolean;
11
+ }
12
+
13
+ export const DEFAULT_CONFIG: AdminConfig = {
14
+ notifyTarget: [],
15
+ notifyFriendMsg: true,
16
+ notifyFriendRequest: true,
17
+ notifyGroupInvite: true,
18
+ notifyGroupBan: true,
19
+ notifyGroupUnban: true,
20
+ notifyGroupKick: true,
21
+ };
22
+
23
+ export function normalizeConfig(raw: any): AdminConfig {
24
+ return {
25
+ notifyTarget: Array.isArray(raw?.notifyTarget)
26
+ ? raw.notifyTarget.map((v: any) => Number(v)).filter((n: number) => n > 0)
27
+ : DEFAULT_CONFIG.notifyTarget,
28
+ notifyFriendMsg: raw?.notifyFriendMsg ?? DEFAULT_CONFIG.notifyFriendMsg,
29
+ notifyFriendRequest:
30
+ raw?.notifyFriendRequest ?? DEFAULT_CONFIG.notifyFriendRequest,
31
+ notifyGroupInvite:
32
+ raw?.notifyGroupInvite ?? DEFAULT_CONFIG.notifyGroupInvite,
33
+ notifyGroupBan: raw?.notifyGroupBan ?? DEFAULT_CONFIG.notifyGroupBan,
34
+ notifyGroupUnban: raw?.notifyGroupUnban ?? DEFAULT_CONFIG.notifyGroupUnban,
35
+ notifyGroupKick: raw?.notifyGroupKick ?? DEFAULT_CONFIG.notifyGroupKick,
36
+ };
37
+ }
38
+
39
+ // 格式化秒数为可读时长
40
+ export function formatDuration(seconds: number): string {
41
+ if (seconds <= 0) return "0秒";
42
+ const days = Math.floor(seconds / 86400);
43
+ const hours = Math.floor((seconds % 86400) / 3600);
44
+ const minutes = Math.floor((seconds % 3600) / 60);
45
+ const secs = seconds % 60;
46
+ const parts: string[] = [];
47
+ if (days > 0) parts.push(`${days}天`);
48
+ if (hours > 0) parts.push(`${hours}小时`);
49
+ if (minutes > 0) parts.push(`${minutes}分钟`);
50
+ if (secs > 0) parts.push(`${secs}秒`);
51
+ return parts.join("");
52
+ }
53
+
54
+ // 解析禁言时长
55
+ export function parseDuration(text: string): number {
56
+ const match = text.match(/(\d+)\s*(分钟|min|m|小时|hour|h|天|day|d)/i);
57
+ if (!match) return 0;
58
+ const value = parseInt(match[1], 10);
59
+ const unit = match[2].toLowerCase();
60
+ if (unit.startsWith("分") || unit === "min" || unit === "m")
61
+ return value * 60;
62
+ if (unit.startsWith("小") || unit === "hour" || unit === "h")
63
+ return value * 3600;
64
+ if (unit.startsWith("天") || unit === "day" || unit === "d")
65
+ return value * 86400;
66
+ return 0;
67
+ }
68
+
69
+ // 从消息中提取图片URL
70
+ export function extractImageUrl(message: RecvElement[]): string | undefined {
71
+ if (!Array.isArray(message)) return undefined;
72
+ for (const seg of message) {
73
+ if (seg.type === "image") {
74
+ const imageSeg = seg as RecvImageElement;
75
+ return imageSeg.url || imageSeg.file;
76
+ }
77
+ }
78
+ return undefined;
79
+ }
80
+
81
+ // 从消息中提取被@的人的QQ号
82
+ export function getAtUserId(message: RecvElement[]): number | undefined {
83
+ if (!Array.isArray(message)) return undefined;
84
+ const atSeg = message.find(
85
+ (seg): seg is RecvAtElement => seg.type === "at" && seg.qq !== "all",
86
+ );
87
+ if (!atSeg) return undefined;
88
+ const qq = Number(atSeg.qq);
89
+ return Number.isFinite(qq) ? qq : undefined;
90
+ }
91
+
92
+ // 获取群成员头像URL
93
+ export function getAvatarUrl(userId: number): string {
94
+ return `https://q1.qlogo.cn/g?b=qq&nk=${userId}&s=640`;
95
+ }
96
+
97
+ // 获取群头像URL
98
+ export function getGroupAvatarUrl(groupId: number): string {
99
+ return `https://p.qlogo.cn/gh/${groupId}/${groupId}/640/`;
100
+ }
101
+
102
+ // 获取Bot群成员角色
103
+ export async function getMemberRole(
104
+ bot: any,
105
+ groupId: number,
106
+ userId: number,
107
+ ): Promise<string> {
108
+ try {
109
+ const info = await bot.getGroupMemberInfo(groupId, userId);
110
+ return info?.role || "member";
111
+ } catch {
112
+ return "member";
113
+ }
114
+ }
package/index.ts ADDED
@@ -0,0 +1,52 @@
1
+ import { definePlugin, type MiokiContext } from "mioki";
2
+ import type { ConfigService } from "../../src/services/config/tpyes";
3
+ import { setPluginRuntimeState, resetPluginRuntimeState } from "../../src";
4
+ import { DEFAULT_CONFIG, normalizeConfig } from "./config";
5
+ import type { AdminConfig } from "./config";
6
+ import { registerNotificationHandlers } from "./notify";
7
+ import { registerPersonalCommands } from "./commands/personal";
8
+ import { registerGroupAdminCommands } from "./commands/group";
9
+
10
+ interface RuntimeState {
11
+ ctx?: MiokiContext;
12
+ config?: AdminConfig;
13
+ }
14
+
15
+ export default definePlugin({
16
+ name: "admin",
17
+ version: "1.0.0",
18
+ description: "管理插件,提供事件通知与管理指令",
19
+
20
+ async setup(ctx: MiokiContext) {
21
+ const configService = ctx.services?.config as ConfigService | undefined;
22
+
23
+ let config: AdminConfig = { ...DEFAULT_CONFIG };
24
+
25
+ if (configService) {
26
+ await configService.registerConfig("admin", "base", DEFAULT_CONFIG);
27
+ const raw = await configService.getConfig("admin", "base");
28
+ config = normalizeConfig(raw);
29
+ configService.onConfigChange("admin", "base", (next) => {
30
+ config = normalizeConfig(next);
31
+ });
32
+ }
33
+
34
+ setPluginRuntimeState<RuntimeState>("admin", { ctx });
35
+
36
+ const getConfig = () => config;
37
+
38
+ // 注册事件通知
39
+ registerNotificationHandlers(ctx, getConfig);
40
+
41
+ // 注册指令
42
+ registerPersonalCommands(ctx);
43
+ registerGroupAdminCommands(ctx);
44
+
45
+ ctx.logger.info("管理插件加载成功");
46
+
47
+ return () => {
48
+ resetPluginRuntimeState("admin");
49
+ ctx.logger.info("管理插件已卸载");
50
+ };
51
+ },
52
+ });