karin-plugin-qgroup-file2openlist 0.0.1
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 +171 -0
- package/config/config.json +20 -0
- package/lib/apps/example.js +41 -0
- package/lib/apps/groupFiles.js +12 -0
- package/lib/apps/groupSyncConfig.js +398 -0
- package/lib/apps/groupSyncScheduler.js +188 -0
- package/lib/apps/handler.js +24 -0
- package/lib/apps/render.js +89 -0
- package/lib/apps/sendMsg.js +128 -0
- package/lib/apps/task.js +19 -0
- package/lib/chunk-5WVKHIPK.js +38 -0
- package/lib/chunk-IZS467MR.js +47 -0
- package/lib/chunk-QVWWPGHK.js +1138 -0
- package/lib/dir.js +6 -0
- package/lib/index.js +7 -0
- package/lib/web.config.js +756 -0
- package/package.json +67 -0
- package/resources/image//345/220/257/347/250/213/345/256/243/345/217/221.png +0 -0
- package/resources/template/test.html +21 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import {
|
|
2
|
+
syncGroupFilesToOpenListCore
|
|
3
|
+
} from "../chunk-QVWWPGHK.js";
|
|
4
|
+
import {
|
|
5
|
+
config
|
|
6
|
+
} from "../chunk-5WVKHIPK.js";
|
|
7
|
+
import "../chunk-IZS467MR.js";
|
|
8
|
+
|
|
9
|
+
// src/apps/groupSyncScheduler.ts
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { getAllBot, karin, logger } from "node-karin";
|
|
12
|
+
var normalizeMode = (value, fallback) => {
|
|
13
|
+
const v = String(value ?? "").trim().toLowerCase();
|
|
14
|
+
if (v === "full" || v === "\u5168\u91CF") return "full";
|
|
15
|
+
if (v === "incremental" || v === "\u589E\u91CF" || v === "inc") return "incremental";
|
|
16
|
+
return fallback;
|
|
17
|
+
};
|
|
18
|
+
var parseCronField = (field, min, max) => {
|
|
19
|
+
const trimmed = String(field ?? "").trim();
|
|
20
|
+
if (!trimmed || trimmed === "*" || trimmed === "?") return null;
|
|
21
|
+
const allowed = /* @__PURE__ */ new Set();
|
|
22
|
+
const parts = trimmed.split(",").map((p) => p.trim()).filter(Boolean);
|
|
23
|
+
for (const part of parts) {
|
|
24
|
+
const [rangePart, stepPart] = part.split("/");
|
|
25
|
+
const step = stepPart ? Number(stepPart) : 1;
|
|
26
|
+
if (!Number.isFinite(step) || step <= 0) return null;
|
|
27
|
+
const applyRange = (start, end) => {
|
|
28
|
+
const s = Math.max(min, start);
|
|
29
|
+
const e = Math.min(max, end);
|
|
30
|
+
for (let v = s; v <= e; v += step) allowed.add(v);
|
|
31
|
+
};
|
|
32
|
+
if (rangePart === "*" || rangePart === "?") {
|
|
33
|
+
applyRange(min, max);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const rangeMatch = rangePart.match(/^(\d+)-(\d+)$/);
|
|
37
|
+
if (rangeMatch) {
|
|
38
|
+
applyRange(Number(rangeMatch[1]), Number(rangeMatch[2]));
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const single = Number(rangePart);
|
|
42
|
+
if (Number.isFinite(single)) {
|
|
43
|
+
applyRange(single, single);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return allowed;
|
|
47
|
+
};
|
|
48
|
+
var cronMatches = (expr, date) => {
|
|
49
|
+
const parts = String(expr ?? "").trim().split(/\s+/).filter(Boolean);
|
|
50
|
+
if (parts.length !== 5 && parts.length !== 6) return false;
|
|
51
|
+
const fields = parts.length === 6 ? parts : ["0", ...parts];
|
|
52
|
+
const sec = date.getSeconds();
|
|
53
|
+
const min = date.getMinutes();
|
|
54
|
+
const hour = date.getHours();
|
|
55
|
+
const dom = date.getDate();
|
|
56
|
+
const month = date.getMonth() + 1;
|
|
57
|
+
const dowRaw = date.getDay();
|
|
58
|
+
const dow = dowRaw === 0 ? 0 : dowRaw;
|
|
59
|
+
const allowedSec = parseCronField(fields[0], 0, 59);
|
|
60
|
+
const allowedMin = parseCronField(fields[1], 0, 59);
|
|
61
|
+
const allowedHour = parseCronField(fields[2], 0, 23);
|
|
62
|
+
const allowedDom = parseCronField(fields[3], 1, 31);
|
|
63
|
+
const allowedMonth = parseCronField(fields[4], 1, 12);
|
|
64
|
+
const allowedDow = parseCronField(fields[5], 0, 7);
|
|
65
|
+
const match = (allowed, value) => !allowed || allowed.has(value);
|
|
66
|
+
const dowOk = !allowedDow || allowedDow.has(dow) || dow === 0 && allowedDow.has(7);
|
|
67
|
+
return match(allowedSec, sec) && match(allowedMin, min) && match(allowedHour, hour) && match(allowedDom, dom) && match(allowedMonth, month) && dowOk;
|
|
68
|
+
};
|
|
69
|
+
var parseTimeWindows = (value) => {
|
|
70
|
+
const raw = String(value ?? "").trim();
|
|
71
|
+
if (!raw) return [];
|
|
72
|
+
const items = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
73
|
+
const windows = [];
|
|
74
|
+
for (const item of items) {
|
|
75
|
+
const match = item.match(/^(\d{1,2}):(\d{2})-(\d{1,2}):(\d{2})$/);
|
|
76
|
+
if (!match) continue;
|
|
77
|
+
const sh = Number(match[1]);
|
|
78
|
+
const sm = Number(match[2]);
|
|
79
|
+
const eh = Number(match[3]);
|
|
80
|
+
const em = Number(match[4]);
|
|
81
|
+
if (![sh, sm, eh, em].every((n) => Number.isFinite(n))) continue;
|
|
82
|
+
if (sh < 0 || sh > 23 || eh < 0 || eh > 23) continue;
|
|
83
|
+
if (sm < 0 || sm > 59 || em < 0 || em > 59) continue;
|
|
84
|
+
const start = sh * 60 + sm;
|
|
85
|
+
const end = eh * 60 + em;
|
|
86
|
+
if (start === end) continue;
|
|
87
|
+
if (end > start) {
|
|
88
|
+
windows.push({ start, end });
|
|
89
|
+
} else {
|
|
90
|
+
windows.push({ start, end: 24 * 60 });
|
|
91
|
+
windows.push({ start: 0, end });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return windows;
|
|
95
|
+
};
|
|
96
|
+
var isNowAllowed = (timeWindows, now) => {
|
|
97
|
+
const windows = parseTimeWindows(String(timeWindows ?? ""));
|
|
98
|
+
if (!windows.length) return true;
|
|
99
|
+
const minutes = now.getHours() * 60 + now.getMinutes();
|
|
100
|
+
return windows.some((w) => minutes >= w.start && minutes < w.end);
|
|
101
|
+
};
|
|
102
|
+
var lastTriggered = /* @__PURE__ */ new Map();
|
|
103
|
+
var runScheduledSync = async (groupId) => {
|
|
104
|
+
const cfg = config();
|
|
105
|
+
const defaults = cfg.groupSyncDefaults ?? {};
|
|
106
|
+
const targets = Array.isArray(cfg.groupSyncTargets) ? cfg.groupSyncTargets : [];
|
|
107
|
+
const target = targets.find((it) => String(it?.groupId) === String(groupId));
|
|
108
|
+
if (!target) return;
|
|
109
|
+
if (target?.enabled === false) return;
|
|
110
|
+
if (target?.schedule?.enabled !== true) return;
|
|
111
|
+
const mode = normalizeMode(target?.mode, normalizeMode(defaults?.mode, "incremental"));
|
|
112
|
+
const targetDir = String(target?.targetDir ?? "").trim() ? String(target.targetDir) : path.posix.join(String(cfg.openlistTargetDir ?? "/"), String(groupId));
|
|
113
|
+
const folderId = String(target?.sourceFolderId ?? "").trim() ? String(target.sourceFolderId) : void 0;
|
|
114
|
+
const maxFiles = typeof target?.maxFiles === "number" ? target.maxFiles : void 0;
|
|
115
|
+
const urlConcurrency = typeof target?.urlConcurrency === "number" ? target.urlConcurrency : typeof defaults?.urlConcurrency === "number" ? defaults.urlConcurrency : 3;
|
|
116
|
+
const transferConcurrency = typeof target?.transferConcurrency === "number" ? target.transferConcurrency : typeof defaults?.transferConcurrency === "number" ? defaults.transferConcurrency : 3;
|
|
117
|
+
const fileTimeoutSec = typeof target?.fileTimeoutSec === "number" ? target.fileTimeoutSec : typeof defaults?.fileTimeoutSec === "number" ? defaults.fileTimeoutSec : 600;
|
|
118
|
+
const retryTimes = typeof target?.retryTimes === "number" ? target.retryTimes : typeof defaults?.retryTimes === "number" ? defaults.retryTimes : 2;
|
|
119
|
+
const retryDelayMs = typeof target?.retryDelayMs === "number" ? target.retryDelayMs : typeof defaults?.retryDelayMs === "number" ? defaults.retryDelayMs : 1500;
|
|
120
|
+
const progressReportEvery = typeof target?.progressReportEvery === "number" ? target.progressReportEvery : typeof defaults?.progressReportEvery === "number" ? defaults.progressReportEvery : 10;
|
|
121
|
+
const downloadLimitKbps = typeof target?.downloadLimitKbps === "number" ? target.downloadLimitKbps : typeof defaults?.downloadLimitKbps === "number" ? defaults.downloadLimitKbps : 0;
|
|
122
|
+
const uploadLimitKbps = typeof target?.uploadLimitKbps === "number" ? target.uploadLimitKbps : typeof defaults?.uploadLimitKbps === "number" ? defaults.uploadLimitKbps : 0;
|
|
123
|
+
const flat = typeof target?.flat === "boolean" ? target.flat : Boolean(defaults?.flat ?? false);
|
|
124
|
+
const bots = getAllBot();
|
|
125
|
+
if (!bots.length) {
|
|
126
|
+
logger.warn(`[\u7FA4\u6587\u4EF6\u5B9A\u65F6\u540C\u6B65][${groupId}] \u672A\u627E\u5230\u53EF\u7528Bot\uFF0C\u8DF3\u8FC7`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
let lastError;
|
|
130
|
+
for (const bot of bots) {
|
|
131
|
+
try {
|
|
132
|
+
await syncGroupFilesToOpenListCore({
|
|
133
|
+
bot,
|
|
134
|
+
groupId: String(groupId),
|
|
135
|
+
folderId,
|
|
136
|
+
maxFiles,
|
|
137
|
+
flat,
|
|
138
|
+
targetDir,
|
|
139
|
+
mode,
|
|
140
|
+
urlConcurrency: Math.max(1, Math.floor(urlConcurrency) || 1),
|
|
141
|
+
transferConcurrency: Math.max(1, Math.floor(transferConcurrency) || 1),
|
|
142
|
+
fileTimeoutSec: Math.max(10, Math.floor(fileTimeoutSec) || 10),
|
|
143
|
+
retryTimes: Math.max(0, Math.floor(retryTimes) || 0),
|
|
144
|
+
retryDelayMs: Math.max(0, Math.floor(retryDelayMs) || 0),
|
|
145
|
+
progressReportEvery: Math.max(0, Math.floor(progressReportEvery) || 0),
|
|
146
|
+
downloadLimitKbps: Math.max(0, Math.floor(downloadLimitKbps) || 0),
|
|
147
|
+
uploadLimitKbps: Math.max(0, Math.floor(uploadLimitKbps) || 0),
|
|
148
|
+
report: (msg) => logger.info(`[\u7FA4\u6587\u4EF6\u5B9A\u65F6\u540C\u6B65][${groupId}] ${msg}`)
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
153
|
+
if (msg.includes("\u6B63\u5728\u8FDB\u884C\u4E2D")) {
|
|
154
|
+
logger.info(`[\u7FA4\u6587\u4EF6\u5B9A\u65F6\u540C\u6B65][${groupId}] \u540C\u6B65\u4EFB\u52A1\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8DF3\u8FC7`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
lastError = error;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (lastError) logger.error(lastError);
|
|
162
|
+
};
|
|
163
|
+
var groupFileSyncScheduler = karin.task("\u7FA4\u6587\u4EF6\u540C\u6B65\u8C03\u5EA6\u5668", "* * * * * *", async () => {
|
|
164
|
+
const cfg = config();
|
|
165
|
+
const targets = Array.isArray(cfg.groupSyncTargets) ? cfg.groupSyncTargets : [];
|
|
166
|
+
const now = /* @__PURE__ */ new Date();
|
|
167
|
+
const stamp = Math.floor(now.getTime() / 1e3);
|
|
168
|
+
for (const target of targets) {
|
|
169
|
+
const groupId = String(target?.groupId ?? "").trim();
|
|
170
|
+
if (!groupId) continue;
|
|
171
|
+
if (target?.enabled === false) continue;
|
|
172
|
+
if (target?.schedule?.enabled !== true) continue;
|
|
173
|
+
const cron = String(target?.schedule?.cron ?? "").trim();
|
|
174
|
+
if (!cron) continue;
|
|
175
|
+
if (!cronMatches(cron, now)) continue;
|
|
176
|
+
const key = `${groupId}:${cron}`;
|
|
177
|
+
if (lastTriggered.get(key) === stamp) continue;
|
|
178
|
+
lastTriggered.set(key, stamp);
|
|
179
|
+
if (!isNowAllowed(target?.timeWindows, now)) {
|
|
180
|
+
logger.info(`[\u7FA4\u6587\u4EF6\u5B9A\u65F6\u540C\u6B65][${groupId}] \u547D\u4E2Dcron\u4F46\u4E0D\u5728\u65F6\u6BB5\u5185\uFF0C\u8DF3\u8FC7`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
void runScheduledSync(groupId);
|
|
184
|
+
}
|
|
185
|
+
}, { log: false, type: "skip" });
|
|
186
|
+
export {
|
|
187
|
+
groupFileSyncScheduler
|
|
188
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/apps/handler.ts
|
|
2
|
+
import { karin, handler } from "node-karin";
|
|
3
|
+
var test = karin.handler("test.image", (args, reject) => {
|
|
4
|
+
return "Handler\u5904\u7406\u5B8C\u6210";
|
|
5
|
+
});
|
|
6
|
+
var testHandler = karin.command(/^#?测试handler$/, async (e) => {
|
|
7
|
+
const msg = "\u6D4B\u8BD5handler";
|
|
8
|
+
const res = await handler.call("test.image", { e, msg });
|
|
9
|
+
await e.reply(res);
|
|
10
|
+
return true;
|
|
11
|
+
}, {
|
|
12
|
+
/** 插件优先级 */
|
|
13
|
+
priority: 9999,
|
|
14
|
+
/** 插件触发是否打印触发日志 */
|
|
15
|
+
log: true,
|
|
16
|
+
/** 插件名称 */
|
|
17
|
+
name: "\u6D4B\u8BD5handler",
|
|
18
|
+
/** 谁可以触发这个插件 'all' | 'master' | 'admin' | 'group.owner' | 'group.admin' */
|
|
19
|
+
permission: "all"
|
|
20
|
+
});
|
|
21
|
+
export {
|
|
22
|
+
test,
|
|
23
|
+
testHandler
|
|
24
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
config
|
|
3
|
+
} from "../chunk-5WVKHIPK.js";
|
|
4
|
+
import {
|
|
5
|
+
dir
|
|
6
|
+
} from "../chunk-IZS467MR.js";
|
|
7
|
+
|
|
8
|
+
// src/apps/render.ts
|
|
9
|
+
import { karin, render, segment, logger } from "node-karin";
|
|
10
|
+
var image = karin.command(/^#?测试渲染$/, async (e) => {
|
|
11
|
+
try {
|
|
12
|
+
const html = dir.defResourcesDir + "/template/test.html";
|
|
13
|
+
const image2 = dir.defResourcesDir + "/image/\u542F\u7A0B\u5BA3\u53D1.png";
|
|
14
|
+
const img = await render.render({
|
|
15
|
+
name: "render",
|
|
16
|
+
encoding: "base64",
|
|
17
|
+
file: html,
|
|
18
|
+
data: {
|
|
19
|
+
file: image2,
|
|
20
|
+
pluResPath: process.cwd()
|
|
21
|
+
},
|
|
22
|
+
pageGotoParams: {
|
|
23
|
+
waitUntil: "networkidle2"
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
await e.reply(segment.image(`base64://${img}`));
|
|
27
|
+
return true;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error(error);
|
|
30
|
+
await e.reply(JSON.stringify(error));
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}, {
|
|
34
|
+
/** 插件优先级 */
|
|
35
|
+
priority: 9999,
|
|
36
|
+
/** 插件触发是否打印触发日志 */
|
|
37
|
+
log: true,
|
|
38
|
+
/** 插件名称 */
|
|
39
|
+
name: "\u6D4B\u8BD5\u6E32\u67D3",
|
|
40
|
+
/** 谁可以触发这个插件 'all' | 'master' | 'admin' | 'group.owner' | 'group.admin' */
|
|
41
|
+
permission: "all"
|
|
42
|
+
});
|
|
43
|
+
var renderUrl = karin.command(/^#?渲染/, async (e) => {
|
|
44
|
+
const file = e.msg.replace(/^#?渲染/, "").trim();
|
|
45
|
+
try {
|
|
46
|
+
const img = await render.render({
|
|
47
|
+
name: "render",
|
|
48
|
+
encoding: "base64",
|
|
49
|
+
file: file || "https://whitechi73.github.io/OpenShamrock/",
|
|
50
|
+
type: "png",
|
|
51
|
+
pageGotoParams: {
|
|
52
|
+
waitUntil: "networkidle2"
|
|
53
|
+
},
|
|
54
|
+
setViewport: {
|
|
55
|
+
width: 1920,
|
|
56
|
+
height: 1080,
|
|
57
|
+
deviceScaleFactor: 1
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
await e.reply(segment.image(`base64://${img}`));
|
|
61
|
+
return true;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logger.error(error);
|
|
64
|
+
await e.reply(error.message);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}, {
|
|
68
|
+
/** 插件优先级 */
|
|
69
|
+
priority: 9999,
|
|
70
|
+
/** 插件触发是否打印触发日志 */
|
|
71
|
+
log: true,
|
|
72
|
+
/** 插件名称 */
|
|
73
|
+
name: "\u6E32\u67D3demo",
|
|
74
|
+
/** 谁可以触发这个插件 'all' | 'master' | 'admin' | 'group.owner' | 'group.admin' */
|
|
75
|
+
permission: "master"
|
|
76
|
+
});
|
|
77
|
+
var screenshot = karin.command("^#\u6D4B\u8BD5\u622A\u56FE$", async (e) => {
|
|
78
|
+
const { screenshotUrl } = config();
|
|
79
|
+
const img = await karin.render(screenshotUrl);
|
|
80
|
+
await e.reply(segment.image(`base64://${img}`));
|
|
81
|
+
return true;
|
|
82
|
+
}, {
|
|
83
|
+
name: "\u6D4B\u8BD5\u622A\u56FE"
|
|
84
|
+
});
|
|
85
|
+
export {
|
|
86
|
+
image,
|
|
87
|
+
renderUrl,
|
|
88
|
+
screenshot
|
|
89
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
config
|
|
3
|
+
} from "../chunk-5WVKHIPK.js";
|
|
4
|
+
import "../chunk-IZS467MR.js";
|
|
5
|
+
|
|
6
|
+
// src/apps/sendMsg.ts
|
|
7
|
+
import { karin, segment, common } from "node-karin";
|
|
8
|
+
var yiyanApi = karin.command(/^#一言$/, async (e) => {
|
|
9
|
+
const { yiyanApi: yiyanApi2 } = config();
|
|
10
|
+
await e.reply(segment.image(yiyanApi2));
|
|
11
|
+
}, {
|
|
12
|
+
name: "\u4E00\u8A00api"
|
|
13
|
+
});
|
|
14
|
+
var sendMsg = karin.command(/^#测试主动消息$/, async (e) => {
|
|
15
|
+
const selfId = e.selfId;
|
|
16
|
+
const contact = e.contact;
|
|
17
|
+
const messages = [
|
|
18
|
+
"\u2728 \u54C7\uFF01\u8FD9\u662F\u4E00\u6761\u8D85\u53EF\u7231\u7684\u4E3B\u52A8\u6D88\u606F\uFF0C10\u79D2\u540E\u5C31\u4F1A\u795E\u79D8\u6D88\u5931\u54E6~ \u2728",
|
|
19
|
+
"\u{1F338} \u53EE\u549A\uFF01\u6211\u4E3B\u52A8\u627E\u4F60\u804A\u5929\u5566\uFF0C\u53EF\u60DC10\u79D2\u540E\u6211\u5C31\u8981\u6E9C\u8D70\u5566~ \u{1F338}",
|
|
20
|
+
"\u{1F380} \u4F60\u597D\u5440\uFF01\u8FD9\u662F\u4E00\u6761\u4F1A\u81EA\u5DF1\u8DD1\u6389\u7684\u6D88\u606F\uFF0C\u5012\u8BA1\u65F610\u79D2\u5F00\u59CB\uFF01\u{1F380}",
|
|
21
|
+
"\u{1F36D} \u7A81\u7136\u51FA\u73B0\u7684\u751C\u751C\u6D88\u606F\uFF01\u522B\u6025\u7740\u56DE\u590D\uFF0C10\u79D2\u540E\u6211\u5C31\u6D88\u5931\u5566~ \u{1F36D}"
|
|
22
|
+
];
|
|
23
|
+
const randomMsg = messages[Math.floor(Math.random() * messages.length)];
|
|
24
|
+
const text = `
|
|
25
|
+
${randomMsg}`;
|
|
26
|
+
const { messageId } = await karin.sendMsg(selfId, contact, text, { recallMsg: 10 });
|
|
27
|
+
console.log(`\u2705 \u6D88\u606F\u5DF2\u9001\u8FBE\uFF0C\u6D88\u606FID\uFF1A${messageId}`);
|
|
28
|
+
return true;
|
|
29
|
+
}, {
|
|
30
|
+
/** 插件优先级 */
|
|
31
|
+
priority: 9999,
|
|
32
|
+
/** 插件触发是否打印触发日志 */
|
|
33
|
+
log: true,
|
|
34
|
+
/** 插件名称 */
|
|
35
|
+
name: "\u53EF\u7231\u4E3B\u52A8\u6D88\u606Fdemo",
|
|
36
|
+
/** 谁可以触发这个插件 'all' | 'master' | 'admin' | 'group.owner' | 'group.admin' */
|
|
37
|
+
permission: "all"
|
|
38
|
+
});
|
|
39
|
+
var forwardMessage = karin.command(/^#测试转发$/, async (e) => {
|
|
40
|
+
const message = [
|
|
41
|
+
segment.text("\u{1F31F} \u8FD9\u662F\u8F6C\u53D1\u7684\u7B2C\u4E00\u6761\u6D88\u606F \u{1F31F}"),
|
|
42
|
+
segment.text("\u2728 \u8FD9\u662F\u8F6C\u53D1\u7684\u7B2C\u4E8C\u6761\u6D88\u606F \u2728"),
|
|
43
|
+
segment.text("\u{1F496} \u8FD9\u662F\u8F6C\u53D1\u7684\u6700\u540E\u4E00\u6761\u6D88\u606F \u{1F496}")
|
|
44
|
+
];
|
|
45
|
+
const content = common.makeForward(message, e.selfId, e.bot.account.name);
|
|
46
|
+
await e.bot.sendForwardMsg(e.contact, content);
|
|
47
|
+
return true;
|
|
48
|
+
}, {
|
|
49
|
+
/** 插件优先级 */
|
|
50
|
+
priority: 9999,
|
|
51
|
+
/** 插件触发是否打印触发日志 */
|
|
52
|
+
log: true,
|
|
53
|
+
/** 插件名称 */
|
|
54
|
+
name: "\u53EF\u7231\u8F6C\u53D1demo",
|
|
55
|
+
/** 谁可以触发这个插件 'all' | 'master' | 'admin' | 'group.owner' | 'group.admin' */
|
|
56
|
+
permission: "all"
|
|
57
|
+
});
|
|
58
|
+
var randomEmoji = karin.command(/^#随机表情$/, async (e) => {
|
|
59
|
+
const emojiUrls = [
|
|
60
|
+
"https://i.imgur.com/XaUdU2C.gif",
|
|
61
|
+
"https://i.imgur.com/wF2RkHB.gif",
|
|
62
|
+
"https://i.imgur.com/7voHalT.jpg",
|
|
63
|
+
"https://i.imgur.com/QMlZUdZ.gif",
|
|
64
|
+
"https://i.imgur.com/o2JQjAn.gif"
|
|
65
|
+
];
|
|
66
|
+
const randomUrl = emojiUrls[Math.floor(Math.random() * emojiUrls.length)];
|
|
67
|
+
const message = [
|
|
68
|
+
segment.text("\u{1F389} \u968F\u673A\u8868\u60C5\u5305\u6765\u5566\uFF01"),
|
|
69
|
+
segment.image(randomUrl)
|
|
70
|
+
];
|
|
71
|
+
await e.reply(message);
|
|
72
|
+
return true;
|
|
73
|
+
}, {
|
|
74
|
+
priority: 9999,
|
|
75
|
+
log: true,
|
|
76
|
+
name: "\u968F\u673A\u8868\u60C5\u5305demo",
|
|
77
|
+
permission: "all"
|
|
78
|
+
});
|
|
79
|
+
var dailyQuote = karin.command(/^#每日一言$/, async (e) => {
|
|
80
|
+
const quotes = [
|
|
81
|
+
"\u4ECA\u5929\u4E5F\u662F\u5145\u6EE1\u5E0C\u671B\u7684\u4E00\u5929\uFF01\u52A0\u6CB9\uFF01\u2728",
|
|
82
|
+
"\u4EBA\u751F\u5C31\u50CF\u4E00\u76D2\u5DE7\u514B\u529B\uFF0C\u4F60\u6C38\u8FDC\u4E0D\u77E5\u9053\u4E0B\u4E00\u5757\u662F\u4EC0\u4E48\u5473\u9053\u3002\u{1F36B}",
|
|
83
|
+
"\u5FAE\u7B11\u662F\u4E16\u754C\u4E0A\u6700\u7F8E\u4E3D\u7684\u8BED\u8A00\u3002\u{1F60A}",
|
|
84
|
+
"\u6210\u529F\u4E0D\u662F\u7EC8\u70B9\uFF0C\u5931\u8D25\u4E5F\u4E0D\u662F\u7EC8\u7ED3\uFF0C\u91CD\u8981\u7684\u662F\u7EE7\u7EED\u524D\u8FDB\u7684\u52C7\u6C14\u3002\u{1F680}",
|
|
85
|
+
"\u505A\u81EA\u5DF1\u7684\u592A\u9633\uFF0C\u4E0D\u5FC5\u4EF0\u671B\u522B\u4EBA\uFF01\u{1F4AB}",
|
|
86
|
+
"\u751F\u6D3B\u5C31\u50CF\u9A91\u81EA\u884C\u8F66\uFF0C\u60F3\u4FDD\u6301\u5E73\u8861\u5C31\u5F97\u524D\u8FDB\u3002\u{1F6B2}",
|
|
87
|
+
"\u6700\u91CD\u8981\u7684\u662F\u7231\u81EA\u5DF1\uFF0C\u56E0\u4E3A\u8FD9\u6837\u4F60\u7684\u7075\u9B42\u624D\u4F1A\u53D1\u5149\u3002\u{1F496}"
|
|
88
|
+
];
|
|
89
|
+
const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];
|
|
90
|
+
const message = [
|
|
91
|
+
segment.text(`\u{1F4AD} \u6BCF\u65E5\u4E00\u8A00\uFF1A${randomQuote}`)
|
|
92
|
+
];
|
|
93
|
+
await e.reply(message);
|
|
94
|
+
return true;
|
|
95
|
+
}, {
|
|
96
|
+
priority: 9999,
|
|
97
|
+
log: true,
|
|
98
|
+
name: "\u6BCF\u65E5\u4E00\u8A00demo",
|
|
99
|
+
permission: "all"
|
|
100
|
+
});
|
|
101
|
+
var weatherForecast = karin.command(/^#今日天气$/, async (e) => {
|
|
102
|
+
const weathers = [
|
|
103
|
+
"\u2600\uFE0F \u6674\u5929\uFF0C\u6E29\u5EA625\xB0C\uFF0C\u9002\u5408\u51FA\u95E8\u73A9\u800D~",
|
|
104
|
+
"\u{1F327}\uFE0F \u5C0F\u96E8\uFF0C\u6E29\u5EA618\xB0C\uFF0C\u8BB0\u5F97\u5E26\u4F1E\u54E6\uFF01",
|
|
105
|
+
"\u26C5 \u591A\u4E91\uFF0C\u6E29\u5EA622\xB0C\uFF0C\u9634\u6674\u4E0D\u5B9A\u7684\u4E00\u5929~",
|
|
106
|
+
"\u{1F32B}\uFE0F \u96FE\u5929\uFF0C\u6E29\u5EA615\xB0C\uFF0C\u80FD\u89C1\u5EA6\u8F83\u4F4E\uFF0C\u51FA\u884C\u6CE8\u610F\u5B89\u5168\uFF01",
|
|
107
|
+
"\u{1F324}\uFE0F \u5C40\u90E8\u6674\u6717\uFF0C\u6E29\u5EA620\xB0C\uFF0C\u5076\u6709\u5C0F\u4E91\u6735\u906E\u6321\u9633\u5149~"
|
|
108
|
+
];
|
|
109
|
+
const randomWeather = weathers[Math.floor(Math.random() * weathers.length)];
|
|
110
|
+
const message = [
|
|
111
|
+
segment.text(`\u{1F308} \u4ECA\u65E5\u5929\u6C14\u9884\u62A5\uFF1A${randomWeather}`)
|
|
112
|
+
];
|
|
113
|
+
await e.reply(message);
|
|
114
|
+
return true;
|
|
115
|
+
}, {
|
|
116
|
+
priority: 9999,
|
|
117
|
+
log: true,
|
|
118
|
+
name: "\u5929\u6C14\u9884\u62A5demo",
|
|
119
|
+
permission: "all"
|
|
120
|
+
});
|
|
121
|
+
export {
|
|
122
|
+
dailyQuote,
|
|
123
|
+
forwardMessage,
|
|
124
|
+
randomEmoji,
|
|
125
|
+
sendMsg,
|
|
126
|
+
weatherForecast,
|
|
127
|
+
yiyanApi
|
|
128
|
+
};
|
package/lib/apps/task.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/apps/task.ts
|
|
2
|
+
import { karin, logger } from "node-karin";
|
|
3
|
+
var Task = karin.task("\u6BCF\u5206\u949F\u7684\u8DA3\u5473\u95EE\u5019", "0 */1 * * * *", () => {
|
|
4
|
+
const funnyMessages = [
|
|
5
|
+
"\u4F60\u597D\u5440\uFF0C\u53C8\u89C1\u9762\u4E86\uFF01",
|
|
6
|
+
"\u53C8\u8FC7\u53BB\u4E00\u5206\u949F\uFF0C\u6211\u8FD8\u5728\u575A\u6301\u5DE5\u4F5C~",
|
|
7
|
+
"\u563F\uFF0C\u770B\u770B\u8C01\u53C8\u6309\u65F6\u62A5\u5230\u4E86\uFF01",
|
|
8
|
+
"\u65F6\u95F4\u8FC7\u5F97\u771F\u5FEB\uFF0C\u6211\u90FD\u6570\u4E0D\u6E05\u8FD9\u662F\u7B2C\u51E0\u6B21\u89C1\u4F60\u4E86",
|
|
9
|
+
"\u53EE\u549A\uFF01\u60A8\u7684\u6BCF\u5206\u949F\u63D0\u9192\u670D\u52A1\u5DF2\u9001\u8FBE",
|
|
10
|
+
"\u5DE5\u4F5Cing...\u522B\u62C5\u5FC3\uFF0C\u6211\u4E0D\u4F1A\u5077\u61D2\u7684",
|
|
11
|
+
"\u6EF4\u7B54\u6EF4\u7B54\uFF0C\u65F6\u949F\u5728\u8D70\uFF0C\u6211\u5728\u966A\u4F60",
|
|
12
|
+
"\u6211\u662F\u6700\u52E4\u52B3\u7684\u4EFB\u52A1\uFF0C\u4ECE\u4E0D\u7F3A\u52E4\uFF01"
|
|
13
|
+
];
|
|
14
|
+
const randomIndex = Math.floor(Math.random() * funnyMessages.length);
|
|
15
|
+
logger.info(`\u{1F389} ${funnyMessages[randomIndex]} \u{1F389}`);
|
|
16
|
+
}, { log: false });
|
|
17
|
+
export {
|
|
18
|
+
Task
|
|
19
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
dir
|
|
3
|
+
} from "./chunk-IZS467MR.js";
|
|
4
|
+
|
|
5
|
+
// src/utils/config.ts
|
|
6
|
+
import {
|
|
7
|
+
watch,
|
|
8
|
+
logger,
|
|
9
|
+
filesByExt,
|
|
10
|
+
copyConfigSync,
|
|
11
|
+
requireFileSync
|
|
12
|
+
} from "node-karin";
|
|
13
|
+
copyConfigSync(dir.defConfigDir, dir.ConfigDir, [".json"]);
|
|
14
|
+
var config = () => {
|
|
15
|
+
const cfg = requireFileSync(`${dir.ConfigDir}/config.json`, { force: true });
|
|
16
|
+
const def = requireFileSync(`${dir.defConfigDir}/config.json`, { force: true });
|
|
17
|
+
return { ...def, ...cfg };
|
|
18
|
+
};
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
const list = filesByExt(dir.ConfigDir, ".json", "abs");
|
|
21
|
+
list.forEach((file) => watch(file, (old, now) => {
|
|
22
|
+
logger.info([
|
|
23
|
+
"QAQ: \u68C0\u6D4B\u5230\u914D\u7F6E\u6587\u4EF6\u66F4\u65B0",
|
|
24
|
+
`\u8FD9\u662F\u65E7\u6570\u636E: ${old}`,
|
|
25
|
+
`\u8FD9\u662F\u65B0\u6570\u636E: ${now}`
|
|
26
|
+
].join("\n"));
|
|
27
|
+
}));
|
|
28
|
+
}, 2e3);
|
|
29
|
+
|
|
30
|
+
// src/utils/common.ts
|
|
31
|
+
import lodash from "node-karin/lodash";
|
|
32
|
+
import moment from "node-karin/moment";
|
|
33
|
+
var time = (format = "YYYY-MM-DD HH:mm:ss") => moment().format(format);
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
config,
|
|
37
|
+
time
|
|
38
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// src/dir.ts
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { URL, fileURLToPath } from "url";
|
|
4
|
+
import { karinPathBase, requireFileSync } from "node-karin";
|
|
5
|
+
var pluginDir = fileURLToPath(new URL("../", import.meta.url));
|
|
6
|
+
var pluginName = path.basename(pluginDir);
|
|
7
|
+
var pkg = requireFileSync(path.join(pluginDir, "package.json"));
|
|
8
|
+
var dir = {
|
|
9
|
+
/** 根目录绝对路径 */
|
|
10
|
+
pluginDir,
|
|
11
|
+
/** 插件目录名称 */
|
|
12
|
+
pluginName,
|
|
13
|
+
/** package.json */
|
|
14
|
+
pkg,
|
|
15
|
+
/** 插件版本 package.json 的 version */
|
|
16
|
+
get version() {
|
|
17
|
+
return pkg.version;
|
|
18
|
+
},
|
|
19
|
+
/** 插件名称 package.json 的 name */
|
|
20
|
+
get name() {
|
|
21
|
+
return pkg.name;
|
|
22
|
+
},
|
|
23
|
+
/** 插件默认配置目录 */
|
|
24
|
+
get defConfigDir() {
|
|
25
|
+
return path.join(pluginDir, "config");
|
|
26
|
+
},
|
|
27
|
+
/** 在`@karinjs`中的绝对路径 */
|
|
28
|
+
get karinPath() {
|
|
29
|
+
return path.join(karinPathBase, pluginName);
|
|
30
|
+
},
|
|
31
|
+
/** 插件配置目录 `@karinjs/karin-plugin-xxx/config` */
|
|
32
|
+
get ConfigDir() {
|
|
33
|
+
return path.join(this.karinPath, "config");
|
|
34
|
+
},
|
|
35
|
+
/** 插件资源目录 `@karinjs/karin-plugin-xxx/resources` */
|
|
36
|
+
get defResourcesDir() {
|
|
37
|
+
return path.join(this.karinPath, "resources");
|
|
38
|
+
},
|
|
39
|
+
/** 插件数据目录 `@karinjs/karin-plugin-xxx/data` */
|
|
40
|
+
get DataDir() {
|
|
41
|
+
return path.join(this.karinPath, "data");
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
dir
|
|
47
|
+
};
|