@wahaha216/koishi-plugin-jmcomic 0.1.0 → 0.2.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/lib/abstract/JMClientAbstract.d.ts +7 -1
- package/lib/entity/JMAppClient.d.ts +2 -1
- package/lib/entity/JMHtmlClient.d.ts +2 -1
- package/lib/index.js +129 -29
- package/lib/processors/jmProcessor.d.ts +1 -0
- package/lib/utils/Queue.d.ts +17 -1
- package/package.json +1 -1
- package/readme.md +22 -9
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import crypto from "crypto";
|
|
2
|
-
import { IJMUser } from "../types/JMClient";
|
|
2
|
+
import { IJMSearchResult, IJMUser } from "../types/JMClient";
|
|
3
3
|
import { JMPhotoAbstract } from "./JMPhotoAbstract";
|
|
4
4
|
import { JMAlbumAbstract } from "./JMAlbumAbstract";
|
|
5
5
|
export declare abstract class JMClientAbstract {
|
|
@@ -14,6 +14,12 @@ export declare abstract class JMClientAbstract {
|
|
|
14
14
|
* @returns JM用户信息
|
|
15
15
|
*/
|
|
16
16
|
abstract login(username: string, password: string): Promise<IJMUser>;
|
|
17
|
+
/**
|
|
18
|
+
* JM搜索
|
|
19
|
+
* @param keyword 关键词
|
|
20
|
+
* @returns JM搜索结果
|
|
21
|
+
*/
|
|
22
|
+
abstract search(keyword: string): Promise<IJMSearchResult>;
|
|
17
23
|
/**
|
|
18
24
|
* 根据本子ID获取本子信息
|
|
19
25
|
* @param id 本子ID
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Config } from "..";
|
|
2
2
|
import { Logger, HTTP } from "koishi";
|
|
3
|
-
import { IJMUser } from "../types/JMClient";
|
|
3
|
+
import { IJMUser, IJMSearchResult } from "../types/JMClient";
|
|
4
4
|
import { JMClientAbstract } from "../abstract/JMClientAbstract";
|
|
5
5
|
import { JMAppPhoto } from "./JMAppPhoto";
|
|
6
6
|
import { JMAppAlbum } from "./JMAppAlbum";
|
|
@@ -30,6 +30,7 @@ export declare class JMAppClient extends JMClientAbstract {
|
|
|
30
30
|
* @returns 用户信息
|
|
31
31
|
*/
|
|
32
32
|
login(username: string, password: string): Promise<IJMUser>;
|
|
33
|
+
search(keyword: string): Promise<IJMSearchResult>;
|
|
33
34
|
getAlbumById(id: string): Promise<JMAppAlbum>;
|
|
34
35
|
getPhotoById(id: string): Promise<JMAppPhoto>;
|
|
35
36
|
downloadByAlbum(album: JMAppAlbum): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Config } from "..";
|
|
2
2
|
import { Logger, HTTP } from "koishi";
|
|
3
3
|
import { JMClientAbstract } from "../abstract/JMClientAbstract";
|
|
4
|
-
import { IJMUser } from "../types/JMClient";
|
|
4
|
+
import { IJMSearchResult, IJMUser } from "../types/JMClient";
|
|
5
5
|
import { JMHtmlAlbum } from "./JMHtmlAlbum";
|
|
6
6
|
import { JMHtmlPhoto } from "./JMHtmlPhoto";
|
|
7
7
|
export declare class JMHtmlClient extends JMClientAbstract {
|
|
@@ -19,6 +19,7 @@ export declare class JMHtmlClient extends JMClientAbstract {
|
|
|
19
19
|
private http;
|
|
20
20
|
constructor(root: string, http: HTTP, config: Config, logger: Logger);
|
|
21
21
|
login(username: string, password: string): Promise<IJMUser>;
|
|
22
|
+
search(keyword: string): Promise<IJMSearchResult>;
|
|
22
23
|
getAlbumById(id: string): Promise<JMHtmlAlbum>;
|
|
23
24
|
getPhotoById(id: string): Promise<JMHtmlPhoto>;
|
|
24
25
|
downloadByAlbum(album: JMHtmlAlbum): Promise<void>;
|
package/lib/index.js
CHANGED
|
@@ -33,14 +33,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
33
33
|
// src/locales/zh-CN.yml
|
|
34
34
|
var require_zh_CN = __commonJS({
|
|
35
35
|
"src/locales/zh-CN.yml"(exports2, module2) {
|
|
36
|
-
module2.exports = { commands: { jm: { description: "下载JM漫画,无需python!", examples: "jm album 本子数字ID\njm album info 本子数字ID\njm photo 本子章节数字ID", album: { examples: "jm album 数字ID", messages: { addedToQueue: "已将 {id} 添加到处理队列,请稍候。", notExistError: "找不到该ID对应的本子", mysqlError: "已尝试所有备用地址,但是JM坏掉了" }, info: { examples: "jm album info 本子数字ID", messages: { notExistError: "找不到该ID对应的本子", mysqlError: "已尝试所有备用地址,但是JM坏掉了" } } }, photo: { examples: "jm photo 本子章节数字ID", messages: { addedToQueue: "已将 {id} 添加到处理队列,请稍候。", notExistError: "找不到该ID对应的章节", mysqlError: "已尝试所有备用地址,但是JM坏掉了" } }, queue: { examples: "jm queue", messages: { emptyQueue: "当前没有正在处理或者等待处理的任务", msgFormat: "ID: {id}, 类型: {type}, 状态: {status}\n", task: { pending: "等待中...", processing: "处理中...", failed: "发生未知错误", completed: "已完成", unknown: "未定义状态" }, type: { album: "本子", photo: "章节" } } } } }, _config: [{ $desc: "基础设置", sendMethod: "发送方式", fileMethod: "文件获取方式<br>`buffer`: 读取成buffer后发送给bot实现端<br>`file`: 以`file:///` 本地路径形式发送,如docker环境,请在bot实现端同时映射/koishi目录", password: "密码,留空则不加密", fileName: "文件名定义<br>`{{name}}`:标题<br>`{{id}}`:章节或者本子ID<br>`{{index}}`:多章节本子自动填充`1` 、 `2`" }, { level: "压缩级别,0~9,0为仅存储" }, { $desc: "限制相关设置", retryCount: "重试次数限制", concurrentDownloadLimit: "同时下载数量限制", concurrentDecodeLimit: "同时解密数量限制", concurrentQueueLimit: "同时处理数量限制" }, { $desc: "缓存设置", cache: "缓存文件" }, { autoDelete: "自动删除缓存,**依赖cron服务**" }, { cron: "5位cron表达式", deleteInStart: "启动时检测", keepDays: "缓存保留时间(天)" }, { $desc: "开发者选项", debug: "调试模式,输出更多信息" }] };
|
|
36
|
+
module2.exports = { commands: { jm: { description: "下载JM漫画,无需python!", examples: "jm album 本子数字ID\njm album info 本子数字ID\njm photo 本子章节数字ID", album: { examples: "jm album 数字ID", messages: { addedToQueue: "已将 {id} 添加到处理队列,请稍候。", queueFirst: "已将 {id} 添加到处理队列,即将开始处理", queuePosition: "已将 {id} 添加到处理队列\n前面还有 {ahead} 个任务等待或正在处理", queueProcessing: "已将 {id} 添加到处理队列\n当前任务状态:{status}", notExistError: "找不到该ID对应的本子", mysqlError: "已尝试所有备用地址,但是JM坏掉了" }, info: { examples: "jm album info 本子数字ID", messages: { notExistError: "找不到该ID对应的本子", mysqlError: "已尝试所有备用地址,但是JM坏掉了" } } }, photo: { examples: "jm photo 本子章节数字ID", messages: { addedToQueue: "已将 {id} 添加到处理队列,请稍候。", queueFirst: "已将 {id} 添加到处理队列,即将开始处理", queuePosition: "已将 {id} 添加到处理队列\n前面还有 {ahead} 个任务等待或正在处理", queueProcessing: "已将 {id} 添加到处理队列\n当前任务状态:{status}", notExistError: "找不到该ID对应的章节", mysqlError: "已尝试所有备用地址,但是JM坏掉了" } }, queue: { examples: "jm queue", messages: { emptyQueue: "当前没有正在处理或者等待处理的任务", msgFormat: "ID: {id}, 类型: {type}, 状态: {status}\n", task: { pending: "等待中...", processing: "处理中...", failed: "发生未知错误", completed: "已完成", unknown: "未定义状态" }, type: { album: "本子", photo: "章节" } } }, search: { example: "jm search <关键词>", messages: { emptyKeywordError: "请输入搜索关键词", id: "JMID", name: "名称", author: "作者", category: "分类", description: "描述" } } } }, _config: [{ $desc: "基础设置", sendMethod: "发送方式", fileMethod: "文件获取方式<br>`buffer`: 读取成buffer后发送给bot实现端<br>`file`: 以`file:///` 本地路径形式发送,如docker环境,请在bot实现端同时映射/koishi目录", password: "密码,留空则不加密", fileName: "文件名定义<br>`{{name}}`:标题<br>`{{id}}`:章节或者本子ID<br>`{{index}}`:多章节本子自动填充`1` 、 `2`" }, { level: "压缩级别,0~9,0为仅存储" }, { $desc: "限制相关设置", retryCount: "重试次数限制", concurrentDownloadLimit: "同时下载数量限制", concurrentDecodeLimit: "同时解密数量限制", concurrentQueueLimit: "同时处理数量限制" }, { $desc: "缓存设置", cache: "缓存文件" }, { autoDelete: "自动删除缓存,**依赖cron服务**" }, { cron: "5位cron表达式", deleteInStart: "启动时检测", keepDays: "缓存保留时间(天)" }, { $desc: "开发者选项", debug: "调试模式,输出更多信息" }] };
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
// src/locales/en-US.yml
|
|
41
41
|
var require_en_US = __commonJS({
|
|
42
42
|
"src/locales/en-US.yml"(exports2, module2) {
|
|
43
|
-
module2.exports = { commands: { jm: { description: "Download JM comics without python!", examples: "jm album albumID\njm album info albumID\njm photo photoID", album: { examples: "jm album albumID", messages: { addedToQueue: "Album {id} has been added to the processing queue. Please wait.", notExistError: "albumID not found", mysqlError: "All alternate addresses have been tried. But no response." }, info: { examples: "jm album info albumID", messages: { notExistError: "albumID not found", mysqlError: "All alternate addresses have been tried. But no response." } } }, photo: { examples: "jm photo photoID", messages: { addedToQueue: "Photo {id} has been added to the processing queue. Please wait.", notExistError: "photoID not found", mysqlError: "All alternate addresses have been tried. But no response." } }, queue: { examples: "jm queue", messages: { emptyQueue: "There are currently no tasks being processed or waiting.", msgFormat: "ID: {id}, type: {type}, status: {status}\n", task: { Pending: "Pending...", Processing: "Processing...", failed: "Failed (Unknown Error)", completed: "Completed", unknown: "Unknown Status" }, type: { album: "Album", photo: "Photo" } } } } }, _config: [{ $desc: "Basic settings", sendMethod: "Send method", fileMethod: "File acquisition method<br>`buffer`: Read as buffer and send it to the bot implementation.<br>`file`: Send it in the local path of `file:///`. For example, if in the docker environment, please map the `/koishi` directory at the bot implementation.", retryCount: "Retry limit", password: "Password, leave blank without encryption", fileName: "File name definition<br>`{{name}}`: Title<br>`{{id}}`: Chapter or Book ID<br>`{{index}}`: Multi-chapter book auto-filling `_1` `_2`" }, { $desc: "Limit settings", retryCount: "Retry limit", concurrentDownloadLimit: "Concurrent Download Limit", concurrentDecodeLimit: "Concurrent Decode Limit", concurrentQueueLimit: "Concurrent Queue Limit" }, { level: "Compression level, 0~9, 0 is only stores" }, { $desc: "Cache settings", cache: "Cache files" }, { autoDelete: "Automatically delete cache, **need cron services**" }, { cron: "5-bit cron expression", deleteInStart: "Detection on startup", keepDays: "Cache retention time (days)" }, { $desc: "Developer Options", debug: "Debug mode, output more information" }] };
|
|
43
|
+
module2.exports = { commands: { jm: { description: "Download JM comics without python!", examples: "jm album albumID\njm album info albumID\njm photo photoID", album: { examples: "jm album albumID", messages: { addedToQueue: "Album {id} has been added to the processing queue. Please wait.", queueFirst: "Added {id} to the processing queue, starting shortly.", queuePosition: "Added {id} to the processing queue.\nThere are {ahead} tasks ahead or currently processing.", queueProcessing: "Added {id} to the processing queue.\nCurrent task status: {status}", notExistError: "albumID not found", mysqlError: "All alternate addresses have been tried. But no response." }, info: { examples: "jm album info albumID", messages: { notExistError: "albumID not found", mysqlError: "All alternate addresses have been tried. But no response." } } }, photo: { examples: "jm photo photoID", messages: { addedToQueue: "Photo {id} has been added to the processing queue. Please wait.", queueFirst: "Added {id} to the processing queue, starting shortly.", queuePosition: "Added {id} to the processing queue.\nThere are {ahead} tasks ahead or currently processing.", queueProcessing: "Added {id} to the processing queue.\nCurrent task status: {status}", notExistError: "photoID not found", mysqlError: "All alternate addresses have been tried. But no response." } }, queue: { examples: "jm queue", messages: { emptyQueue: "There are currently no tasks being processed or waiting.", msgFormat: "ID: {id}, type: {type}, status: {status}\n", task: { Pending: "Pending...", Processing: "Processing...", failed: "Failed (Unknown Error)", completed: "Completed", unknown: "Unknown Status" }, type: { album: "Album", photo: "Photo" } } }, search: { example: "jm search <keyword>", messages: { emptyKeywordError: "Please enter search keywords", id: "JMID", name: "name", author: "author", category: "category", description: "description" } } } }, _config: [{ $desc: "Basic settings", sendMethod: "Send method", fileMethod: "File acquisition method<br>`buffer`: Read as buffer and send it to the bot implementation.<br>`file`: Send it in the local path of `file:///`. For example, if in the docker environment, please map the `/koishi` directory at the bot implementation.", retryCount: "Retry limit", password: "Password, leave blank without encryption", fileName: "File name definition<br>`{{name}}`: Title<br>`{{id}}`: Chapter or Book ID<br>`{{index}}`: Multi-chapter book auto-filling `_1` `_2`" }, { $desc: "Limit settings", retryCount: "Retry limit", concurrentDownloadLimit: "Concurrent Download Limit", concurrentDecodeLimit: "Concurrent Decode Limit", concurrentQueueLimit: "Concurrent Queue Limit" }, { level: "Compression level, 0~9, 0 is only stores" }, { $desc: "Cache settings", cache: "Cache files" }, { autoDelete: "Automatically delete cache, **need cron services**" }, { cron: "5-bit cron expression", deleteInStart: "Detection on startup", keepDays: "Cache retention time (days)" }, { $desc: "Developer Options", debug: "Debug mode, output more information" }] };
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
|
|
@@ -791,6 +791,24 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
791
791
|
);
|
|
792
792
|
return this.decodeBase64(res.data, timestamp);
|
|
793
793
|
}
|
|
794
|
+
async search(keyword) {
|
|
795
|
+
if (this.config.debug) this.logger.info(`搜索本子: ${keyword}`);
|
|
796
|
+
const timestamp = this.getTimeStamp();
|
|
797
|
+
const { token, tokenparam } = this.getTokenAndTokenParam(timestamp);
|
|
798
|
+
const searchRes = await requestWithUrlSwitch(
|
|
799
|
+
"/search",
|
|
800
|
+
"POST",
|
|
801
|
+
{
|
|
802
|
+
params: { search_query: keyword },
|
|
803
|
+
headers: { token, tokenparam },
|
|
804
|
+
responseType: "json"
|
|
805
|
+
},
|
|
806
|
+
this.http,
|
|
807
|
+
this.config,
|
|
808
|
+
this.logger
|
|
809
|
+
);
|
|
810
|
+
return this.decodeBase64(searchRes.data, timestamp);
|
|
811
|
+
}
|
|
794
812
|
async getAlbumById(id) {
|
|
795
813
|
if (this.config.debug) this.logger.info(`获取本子(${id})信息`);
|
|
796
814
|
const timestamp = this.getTimeStamp();
|
|
@@ -1123,7 +1141,7 @@ var createJmProcessor = /* @__PURE__ */ __name((processorConfig, http, config, l
|
|
|
1123
1141
|
debug
|
|
1124
1142
|
} = processorConfig;
|
|
1125
1143
|
return async (payload) => {
|
|
1126
|
-
const { id, session, messageId } = payload;
|
|
1144
|
+
const { id, session, messageId, scope } = payload;
|
|
1127
1145
|
try {
|
|
1128
1146
|
const jmClient = new JMAppClient(root, http, config, logger);
|
|
1129
1147
|
let filePath;
|
|
@@ -1229,13 +1247,13 @@ var createJmProcessor = /* @__PURE__ */ __name((processorConfig, http, config, l
|
|
|
1229
1247
|
if (error instanceof AlbumNotExistError || error instanceof PhotoNotExistError) {
|
|
1230
1248
|
await session.send([
|
|
1231
1249
|
import_koishi.h.quote(messageId),
|
|
1232
|
-
import_koishi.h.text(session.text(
|
|
1250
|
+
import_koishi.h.text(session.text(`${scope}.notExistError`))
|
|
1233
1251
|
// 假设 .notExistError 可以通用
|
|
1234
1252
|
]);
|
|
1235
1253
|
} else if (error instanceof MySqlError) {
|
|
1236
1254
|
await session.send([
|
|
1237
1255
|
import_koishi.h.quote(messageId),
|
|
1238
|
-
import_koishi.h.text(session.text(
|
|
1256
|
+
import_koishi.h.text(session.text(`${scope}.mysqlError`))
|
|
1239
1257
|
]);
|
|
1240
1258
|
} else {
|
|
1241
1259
|
await session.send([
|
|
@@ -1287,8 +1305,9 @@ var Queue = class {
|
|
|
1287
1305
|
this.logger.info(
|
|
1288
1306
|
`[任务添加] 任务ID: ${payload} 类型: ${payload.type} ID: ${payload.id}`
|
|
1289
1307
|
);
|
|
1308
|
+
const { pendingAhead, queuePosition } = this.getTaskQueuePosition(task.id);
|
|
1290
1309
|
setTimeout(() => this._processQueue(), 0);
|
|
1291
|
-
return task;
|
|
1310
|
+
return { task, pendingAhead, queuePosition };
|
|
1292
1311
|
}
|
|
1293
1312
|
/**
|
|
1294
1313
|
* 获取任务状态
|
|
@@ -1302,15 +1321,42 @@ var Queue = class {
|
|
|
1302
1321
|
getAllTasks() {
|
|
1303
1322
|
return this.tasks;
|
|
1304
1323
|
}
|
|
1324
|
+
/**
|
|
1325
|
+
* 获取指定任务在队列中的位置信息
|
|
1326
|
+
* @param taskId 任务ID
|
|
1327
|
+
* @returns {pendingAhead: number, queuePosition: number}
|
|
1328
|
+
* pendingAhead: 在此任务之前有多少个待处理任务
|
|
1329
|
+
* queuePosition: 此任务在所有待处理任务中的位置(从1开始)
|
|
1330
|
+
*/
|
|
1331
|
+
getTaskQueuePosition(taskId) {
|
|
1332
|
+
let pendingAhead = 0;
|
|
1333
|
+
let queuePosition = 0;
|
|
1334
|
+
let found = false;
|
|
1335
|
+
for (let i = 0; i < this.tasks.length; i++) {
|
|
1336
|
+
const currentTask = this.tasks[i];
|
|
1337
|
+
if (currentTask.id === taskId) {
|
|
1338
|
+
found = true;
|
|
1339
|
+
if (currentTask.status === "pending" || currentTask.status === "processing") {
|
|
1340
|
+
queuePosition = pendingAhead + 1;
|
|
1341
|
+
}
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
if (currentTask.status === "pending" || currentTask.status === "processing") {
|
|
1345
|
+
pendingAhead++;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
if (!found) {
|
|
1349
|
+
return { pendingAhead: -1, queuePosition: -1 };
|
|
1350
|
+
}
|
|
1351
|
+
return { pendingAhead, queuePosition };
|
|
1352
|
+
}
|
|
1305
1353
|
/**
|
|
1306
1354
|
* 检查并处理队列中的任务
|
|
1307
1355
|
*/
|
|
1308
1356
|
_processQueue() {
|
|
1309
1357
|
while (this.activeTasks < this.concurrency) {
|
|
1310
1358
|
const task = this.tasks.find((t) => t.status === "pending");
|
|
1311
|
-
if (!task)
|
|
1312
|
-
break;
|
|
1313
|
-
}
|
|
1359
|
+
if (!task) break;
|
|
1314
1360
|
this.activeTasks++;
|
|
1315
1361
|
task.status = "processing";
|
|
1316
1362
|
if (this.config.debug)
|
|
@@ -1443,35 +1489,43 @@ async function apply(ctx, config) {
|
|
|
1443
1489
|
config,
|
|
1444
1490
|
logger
|
|
1445
1491
|
);
|
|
1446
|
-
|
|
1492
|
+
const handleAlbumOrPhoto = /* @__PURE__ */ __name(async (session, id, type) => {
|
|
1447
1493
|
const messageId = session.messageId;
|
|
1448
|
-
if (!/^\d+$/.test(
|
|
1494
|
+
if (!/^\d+$/.test(id)) {
|
|
1449
1495
|
await session.send([
|
|
1450
1496
|
import_koishi2.h.quote(messageId),
|
|
1451
1497
|
import_koishi2.h.text("输入的ID不合法,请检查")
|
|
1452
1498
|
]);
|
|
1453
1499
|
return;
|
|
1454
1500
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1501
|
+
const { task, pendingAhead, queuePosition } = queue.add({
|
|
1502
|
+
type,
|
|
1503
|
+
id,
|
|
1504
|
+
session,
|
|
1505
|
+
messageId,
|
|
1506
|
+
scope: session.scope
|
|
1507
|
+
});
|
|
1508
|
+
const params = {
|
|
1509
|
+
id,
|
|
1510
|
+
ahead: pendingAhead,
|
|
1511
|
+
pos: queuePosition,
|
|
1512
|
+
status: task.status
|
|
1513
|
+
};
|
|
1514
|
+
const msg = [import_koishi2.h.quote(messageId)];
|
|
1515
|
+
if (pendingAhead === 0 && queuePosition === 1) {
|
|
1516
|
+
msg.push(import_koishi2.h.text(session.text(".queueFirst", params)));
|
|
1517
|
+
} else if (pendingAhead > 0) {
|
|
1518
|
+
msg.push(import_koishi2.h.text(session.text(".queuePosition", params)));
|
|
1519
|
+
} else {
|
|
1520
|
+
msg.push(import_koishi2.h.text(session.text(".queueProcessing", params)));
|
|
1521
|
+
}
|
|
1522
|
+
await session.send(msg);
|
|
1523
|
+
}, "handleAlbumOrPhoto");
|
|
1524
|
+
ctx.command("jm.album <albumId:string>").alias("本子").action(async ({ session, options }, albumId) => {
|
|
1525
|
+
await handleAlbumOrPhoto(session, albumId, "album");
|
|
1460
1526
|
});
|
|
1461
1527
|
ctx.command("jm.photo <photoId:string>").alias("本子章节").action(async ({ session }, photoId) => {
|
|
1462
|
-
|
|
1463
|
-
if (!/^\d+$/.test(photoId)) {
|
|
1464
|
-
await session.send([
|
|
1465
|
-
import_koishi2.h.quote(messageId),
|
|
1466
|
-
import_koishi2.h.text("输入的ID不合法,请检查")
|
|
1467
|
-
]);
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
queue.add({ type: "album", id: photoId, session, messageId });
|
|
1471
|
-
await session.send([
|
|
1472
|
-
import_koishi2.h.quote(messageId),
|
|
1473
|
-
import_koishi2.h.text(session.text(".addedToQueue", { id: photoId }))
|
|
1474
|
-
]);
|
|
1528
|
+
await handleAlbumOrPhoto(session, photoId, "photo");
|
|
1475
1529
|
});
|
|
1476
1530
|
ctx.command("jm.album.info <albumId:string>").alias("本子信息").action(async ({ session, options }, albumId) => {
|
|
1477
1531
|
const messageId = session.messageId;
|
|
@@ -1517,6 +1571,52 @@ async function apply(ctx, config) {
|
|
|
1517
1571
|
}
|
|
1518
1572
|
}
|
|
1519
1573
|
});
|
|
1574
|
+
ctx.command("jm.search <keyword:string>").alias("本子搜索").action(async ({ session, options }, keyword) => {
|
|
1575
|
+
const messageId = session.messageId;
|
|
1576
|
+
if (!keyword) {
|
|
1577
|
+
await session.send([
|
|
1578
|
+
import_koishi2.h.quote(messageId),
|
|
1579
|
+
import_koishi2.h.text(session.text(".emptyKeywordError"))
|
|
1580
|
+
]);
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
try {
|
|
1584
|
+
const jmClient = new JMAppClient(root, ctx.http, config, logger);
|
|
1585
|
+
const searchResult = await jmClient.search(keyword);
|
|
1586
|
+
console.log(JSON.stringify(searchResult));
|
|
1587
|
+
const contents = searchResult.content;
|
|
1588
|
+
const fragment = [import_koishi2.h.quote(messageId)];
|
|
1589
|
+
contents.forEach((content) => {
|
|
1590
|
+
fragment.push(`${session.text(".id")}: ${content.id}
|
|
1591
|
+
`);
|
|
1592
|
+
fragment.push(`${session.text(".name")}: ${content.name}
|
|
1593
|
+
`);
|
|
1594
|
+
fragment.push(`${session.text(".author")}: ${content.author}
|
|
1595
|
+
`);
|
|
1596
|
+
fragment.push(`${session.text(".category")}: ${content.category.title}
|
|
1597
|
+
`);
|
|
1598
|
+
fragment.push(
|
|
1599
|
+
`${session.text(".description")}: ${content.description}
|
|
1600
|
+
`
|
|
1601
|
+
);
|
|
1602
|
+
});
|
|
1603
|
+
await session.send(fragment);
|
|
1604
|
+
} catch (error) {
|
|
1605
|
+
if (error instanceof AlbumNotExistError) {
|
|
1606
|
+
await session.send([
|
|
1607
|
+
import_koishi2.h.quote(messageId),
|
|
1608
|
+
import_koishi2.h.text(session.text(".notExistError"))
|
|
1609
|
+
]);
|
|
1610
|
+
} else if (error instanceof MySqlError) {
|
|
1611
|
+
await session.send([
|
|
1612
|
+
import_koishi2.h.quote(messageId),
|
|
1613
|
+
import_koishi2.h.text(session.text(".mysqlError"))
|
|
1614
|
+
]);
|
|
1615
|
+
} else {
|
|
1616
|
+
throw new Error(error);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1520
1620
|
ctx.command("jm.queue").alias("本子队列").action(async ({ session, options }) => {
|
|
1521
1621
|
const messageId = session.messageId;
|
|
1522
1622
|
const allTasks = queue.getAllTasks();
|
package/lib/utils/Queue.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ type TaskProcessor = (payload: JmTaskPayload) => Promise<void>;
|
|
|
21
21
|
interface QueueOptions {
|
|
22
22
|
concurrency?: number;
|
|
23
23
|
}
|
|
24
|
+
interface AddTaskResult {
|
|
25
|
+
task: Task;
|
|
26
|
+
pendingAhead: number;
|
|
27
|
+
queuePosition: number;
|
|
28
|
+
}
|
|
24
29
|
export declare class Queue {
|
|
25
30
|
private tasks;
|
|
26
31
|
private processor;
|
|
@@ -38,7 +43,7 @@ export declare class Queue {
|
|
|
38
43
|
/**
|
|
39
44
|
* 向队列添加一个新任务
|
|
40
45
|
*/
|
|
41
|
-
add(payload: JmTaskPayload):
|
|
46
|
+
add(payload: JmTaskPayload): AddTaskResult;
|
|
42
47
|
/**
|
|
43
48
|
* 获取任务状态
|
|
44
49
|
*/
|
|
@@ -47,6 +52,17 @@ export declare class Queue {
|
|
|
47
52
|
* 获取所有任务的只读列表
|
|
48
53
|
*/
|
|
49
54
|
getAllTasks(): Readonly<Task[]>;
|
|
55
|
+
/**
|
|
56
|
+
* 获取指定任务在队列中的位置信息
|
|
57
|
+
* @param taskId 任务ID
|
|
58
|
+
* @returns {pendingAhead: number, queuePosition: number}
|
|
59
|
+
* pendingAhead: 在此任务之前有多少个待处理任务
|
|
60
|
+
* queuePosition: 此任务在所有待处理任务中的位置(从1开始)
|
|
61
|
+
*/
|
|
62
|
+
getTaskQueuePosition(taskId: string): {
|
|
63
|
+
pendingAhead: number;
|
|
64
|
+
queuePosition: number;
|
|
65
|
+
};
|
|
50
66
|
/**
|
|
51
67
|
* 检查并处理队列中的任务
|
|
52
68
|
*/
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
jm album xxxxxx
|
|
11
11
|
jm album info xxxxxx
|
|
12
12
|
jm photo xxxxxx
|
|
13
|
+
jm queue
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
可在配置中配置是发送 PDF 还是 ZIP 压缩包,支持加密。
|
|
@@ -17,18 +18,30 @@ jm photo xxxxxx
|
|
|
17
18
|
## 更新日志
|
|
18
19
|
|
|
19
20
|
<details>
|
|
20
|
-
<summary>0.
|
|
21
|
-
|
|
21
|
+
<summary>0.2.0</summary>
|
|
22
|
+
|
|
23
|
+
1. 简易搜索功能
|
|
24
|
+
2. 修复队列丢失i18n key的问题
|
|
25
|
+
|
|
26
|
+
</details>
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
<details>
|
|
29
|
+
<summary>0.1.1</summary>
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
1. 添加队列时返回队列信息
|
|
32
|
+
2. 提取代码
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
</details>
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
<details>
|
|
37
|
+
<summary>0.1.0</summary>
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
1. 队列系统
|
|
40
|
+
2. 下载并发与解密并发限制
|
|
41
|
+
3. 修改配置页面顺序、分类
|
|
42
|
+
4. 不再直接暴露变量,改为逐级传递
|
|
43
|
+
5. 统一暴露Error类
|
|
44
|
+
6. 添加域名切换条件
|
|
32
45
|
|
|
33
46
|
</details>
|
|
34
47
|
|
|
@@ -44,9 +57,9 @@ jm photo xxxxxx
|
|
|
44
57
|
|
|
45
58
|
<details>
|
|
46
59
|
<summary>0.0.4</summary>
|
|
47
|
-
1.文件名移除前后空格
|
|
48
60
|
|
|
49
|
-
|
|
61
|
+
1. 文件名移除前后空格
|
|
62
|
+
2. 新增文件发送配置,用于配置文件是以 buffer 读取后发送还是以本地地址的形式发送。docker 中使用 file 形式需要在 bot 实现端同时映射/koishi 目录
|
|
50
63
|
|
|
51
64
|
</details>
|
|
52
65
|
|