@wahaha216/koishi-plugin-jmcomic 0.2.5 → 0.2.7
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/JMBlogAbstract.d.ts +21 -0
- package/lib/entity/JMAppBlog.d.ts +6 -0
- package/lib/entity/JMAppClient.d.ts +10 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +358 -69
- package/lib/processors/jmProcessor.d.ts +3 -2
- package/lib/utils/Image.d.ts +3 -2
- package/package.json +1 -1
- package/readme.md +36 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IJMBlogInfo, IJMBlogRelatedBlog, IJMBlogRelatedComics } from "../types/JMClient";
|
|
2
|
+
export declare abstract class JMBlogAbstract {
|
|
3
|
+
/**
|
|
4
|
+
* 文库信息
|
|
5
|
+
*/
|
|
6
|
+
protected info: IJMBlogInfo;
|
|
7
|
+
/**
|
|
8
|
+
* 关联的本子
|
|
9
|
+
*/
|
|
10
|
+
protected related_comics: IJMBlogRelatedComics[];
|
|
11
|
+
/**
|
|
12
|
+
* 相关文库
|
|
13
|
+
*/
|
|
14
|
+
protected related_blogs: IJMBlogRelatedBlog[];
|
|
15
|
+
get Info(): IJMBlogInfo;
|
|
16
|
+
set Info(value: IJMBlogInfo);
|
|
17
|
+
get RelatedComics(): IJMBlogRelatedComics[];
|
|
18
|
+
set RelatedComics(value: IJMBlogRelatedComics[]);
|
|
19
|
+
get RelatedBlogs(): IJMBlogRelatedBlog[];
|
|
20
|
+
set RelatedBlogs(value: IJMBlogRelatedBlog[]);
|
|
21
|
+
}
|
|
@@ -5,6 +5,8 @@ import { JMClientAbstract } from "../abstract/JMClientAbstract";
|
|
|
5
5
|
import { JMAppPhoto } from "./JMAppPhoto";
|
|
6
6
|
import { JMAppAlbum } from "./JMAppAlbum";
|
|
7
7
|
import { JMPhotoAbstract } from "../abstract/JMPhotoAbstract";
|
|
8
|
+
import { JMAppBlog } from "./JMAppBlog";
|
|
9
|
+
import Puppeteer from "koishi-plugin-puppeteer";
|
|
8
10
|
export declare class JMAppClient extends JMClientAbstract {
|
|
9
11
|
static APP_VERSION: string;
|
|
10
12
|
static APP_TOKEN_SECRET: string;
|
|
@@ -22,7 +24,8 @@ export declare class JMAppClient extends JMClientAbstract {
|
|
|
22
24
|
* koishi http
|
|
23
25
|
*/
|
|
24
26
|
private http;
|
|
25
|
-
|
|
27
|
+
private puppeteer;
|
|
28
|
+
constructor(root: string, http: HTTP, config: Config, logger: Logger, puppeteer: Puppeteer);
|
|
26
29
|
/**
|
|
27
30
|
* 登录,未完成
|
|
28
31
|
* @param username 用户名
|
|
@@ -33,13 +36,19 @@ export declare class JMAppClient extends JMClientAbstract {
|
|
|
33
36
|
search(keyword: string): Promise<IJMSearchResult>;
|
|
34
37
|
getAlbumById(id: string): Promise<JMAppAlbum>;
|
|
35
38
|
getPhotoById(id: string): Promise<JMAppPhoto>;
|
|
39
|
+
getBlogById(id: string): Promise<JMAppBlog>;
|
|
36
40
|
downloadByAlbum(album: JMAppAlbum): Promise<void>;
|
|
37
41
|
downloadByPhoto(photo: JMAppPhoto, type?: "photo" | "album", albumId?: string, single?: boolean): Promise<void>;
|
|
42
|
+
downloadFirstImageByAlbum(album: JMAppAlbum): Promise<string>;
|
|
43
|
+
downloadAndDecodeFirstImageByPhoto(photo: JMAppPhoto, type?: "photo" | "album", albumId?: string, single?: boolean): Promise<string>;
|
|
38
44
|
decodeByPhoto(photo: JMPhotoAbstract, type?: "photo" | "album", albumId?: string, single?: boolean): Promise<void>;
|
|
39
45
|
albumToPdf(album: JMAppAlbum, password?: string): Promise<string | string[]>;
|
|
40
46
|
photoToPdf(photo: JMAppPhoto, pdfName: string, type?: "photo" | "album", albumId?: string, single?: boolean, password?: string): Promise<string>;
|
|
47
|
+
blogToPdf(blog: JMAppBlog, password?: string): Promise<string>;
|
|
48
|
+
private writeBlogPdf;
|
|
41
49
|
albumToZip(album: JMAppAlbum, password?: string, level?: number): Promise<string>;
|
|
42
50
|
photoToZip(photo: JMPhotoAbstract, zipName: string, password?: string, level?: number): Promise<string>;
|
|
51
|
+
blogToZip(blog: JMAppBlog, password?: string, level?: number): Promise<string>;
|
|
43
52
|
/**
|
|
44
53
|
* 获取时间戳
|
|
45
54
|
* @returns 时间戳
|
package/lib/index.d.ts
CHANGED
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} 添加到处理队列,请稍候。", 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: "描述", pagination: "共 {total} 条, 当前第 {page} 页, 每页 {pageSize} 条" } } } }, _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: "章节" } } }, blog: { examples: "jm album 数字ID", messages: { addedToQueue: "已将 {id} 添加到处理队列,请稍候。", queueFirst: "已将 {id} 添加到处理队列,即将开始处理", queuePosition: "已将 {id} 添加到处理队列\n前面还有 {ahead} 个任务等待或正在处理", queueProcessing: "已将 {id} 添加到处理队列\n当前任务状态:{status}", notExistError: "找不到该ID对应的本子", mysqlError: "已尝试所有备用地址,但是JM坏掉了" } }, search: { example: "jm search <关键词>", messages: { emptyKeywordError: "请输入搜索关键词", id: "JMID", name: "名称", author: "作者", category: "分类", description: "描述", pagination: "共 {total} 条, 当前第 {page} 页, 每页 {pageSize} 条" } } } }, _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为仅存储" }, { listeningJMId: "监听 `JM ID` 关键词,当出现JMxxxxx的关键词时,发送第一张图片" }, { $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.", 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", pagination: "Total {total}, current {page} page, each page {pageSize}" } } } }, _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.",
|
|
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", pagination: "Total {total}, current {page} page, each page {pageSize}" } }, blog: { examples: "jm blog blogID", messages: { addedToQueue: "Blog {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: "blogID not found", mysqlError: "All alternate addresses have been tried. But no response." } } } }, _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.", 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`" }, { level: "Compression level, 0~9, 0 is only stores" }, { listeningJMId: "Monitors for the `JM ID` keyword. When the 'JMxxxxx' keyword is detected, the first image will be sent." }, { $desc: "Limit settings", retryCount: "Retry limit", concurrentDownloadLimit: "Concurrent Download Limit", concurrentDecodeLimit: "Concurrent Decode Limit", concurrentQueueLimit: "Concurrent Queue Limit" }, { $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
|
|
|
@@ -54,7 +54,7 @@ __export(src_exports, {
|
|
|
54
54
|
});
|
|
55
55
|
module.exports = __toCommonJS(src_exports);
|
|
56
56
|
var import_koishi2 = require("koishi");
|
|
57
|
-
var
|
|
57
|
+
var import_path4 = require("path");
|
|
58
58
|
|
|
59
59
|
// src/utils/Utils.ts
|
|
60
60
|
var import_fs = require("fs");
|
|
@@ -657,6 +657,7 @@ var import_fs2 = __toESM(require("fs"));
|
|
|
657
657
|
var import_sharp = __toESM(require("sharp"));
|
|
658
658
|
var import_archiver = __toESM(require("archiver"));
|
|
659
659
|
var import_archiver_zip_encrypted = __toESM(require("archiver-zip-encrypted"));
|
|
660
|
+
var import_path2 = require("path");
|
|
660
661
|
async function decodeImage(imageBuffer, num, path) {
|
|
661
662
|
if (num > 0) {
|
|
662
663
|
const metadata = await (0, import_sharp.default)(Buffer.from(imageBuffer)).metadata();
|
|
@@ -725,9 +726,28 @@ async function archiverImage(directorys, outputPath, password, level = 9) {
|
|
|
725
726
|
await archive.finalize();
|
|
726
727
|
}
|
|
727
728
|
__name(archiverImage, "archiverImage");
|
|
729
|
+
async function archiverFile(path, outputPath, password, level = 9) {
|
|
730
|
+
if (!import_archiver.default.isRegisteredFormat("zip-encrypted")) {
|
|
731
|
+
import_archiver.default.registerFormat("zip-encrypted", import_archiver_zip_encrypted.default);
|
|
732
|
+
}
|
|
733
|
+
const output = import_fs2.default.createWriteStream(outputPath);
|
|
734
|
+
const options = {
|
|
735
|
+
zlib: { level }
|
|
736
|
+
// 压缩级别
|
|
737
|
+
};
|
|
738
|
+
if (password) {
|
|
739
|
+
options["encryptionMethod"] = "aes256";
|
|
740
|
+
options["password"] = password;
|
|
741
|
+
}
|
|
742
|
+
const archive = import_archiver.default.create(password ? "zip-encrypted" : "zip", options);
|
|
743
|
+
archive.pipe(output);
|
|
744
|
+
archive.file(path, { name: (0, import_path2.basename)(path) });
|
|
745
|
+
await archive.finalize();
|
|
746
|
+
}
|
|
747
|
+
__name(archiverFile, "archiverFile");
|
|
728
748
|
|
|
729
749
|
// src/entity/JMAppClient.ts
|
|
730
|
-
var
|
|
750
|
+
var import_path3 = require("path");
|
|
731
751
|
var import_sharp2 = __toESM(require("sharp"));
|
|
732
752
|
var import_muhammara = require("muhammara");
|
|
733
753
|
|
|
@@ -753,6 +773,64 @@ var PhotoNotExistError = class extends Error {
|
|
|
753
773
|
}
|
|
754
774
|
};
|
|
755
775
|
|
|
776
|
+
// src/abstract/JMBlogAbstract.ts
|
|
777
|
+
var JMBlogAbstract = class {
|
|
778
|
+
static {
|
|
779
|
+
__name(this, "JMBlogAbstract");
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* 文库信息
|
|
783
|
+
*/
|
|
784
|
+
info;
|
|
785
|
+
/**
|
|
786
|
+
* 关联的本子
|
|
787
|
+
*/
|
|
788
|
+
related_comics;
|
|
789
|
+
/**
|
|
790
|
+
* 相关文库
|
|
791
|
+
*/
|
|
792
|
+
related_blogs;
|
|
793
|
+
// --- Info 的 Getter 和 Setter ---
|
|
794
|
+
get Info() {
|
|
795
|
+
return this.info;
|
|
796
|
+
}
|
|
797
|
+
set Info(value) {
|
|
798
|
+
this.info = value;
|
|
799
|
+
}
|
|
800
|
+
// --- RelatedComics 的 Getter 和 Setter ---
|
|
801
|
+
get RelatedComics() {
|
|
802
|
+
return this.related_comics;
|
|
803
|
+
}
|
|
804
|
+
set RelatedComics(value) {
|
|
805
|
+
this.related_comics = value;
|
|
806
|
+
}
|
|
807
|
+
// --- RelatedBlogs 的 Getter 和 Setter ---
|
|
808
|
+
get RelatedBlogs() {
|
|
809
|
+
return this.related_blogs;
|
|
810
|
+
}
|
|
811
|
+
set RelatedBlogs(value) {
|
|
812
|
+
this.related_blogs = value;
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
// src/entity/JMAppBlog.ts
|
|
817
|
+
var JMAppBlog = class _JMAppBlog extends JMBlogAbstract {
|
|
818
|
+
static {
|
|
819
|
+
__name(this, "JMAppBlog");
|
|
820
|
+
}
|
|
821
|
+
constructor(json) {
|
|
822
|
+
super();
|
|
823
|
+
for (const key in json) {
|
|
824
|
+
if (Object.prototype.hasOwnProperty.call(this, key)) {
|
|
825
|
+
this[key] = json[key];
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
static fromJson(json) {
|
|
830
|
+
return new _JMAppBlog(json);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
|
|
756
834
|
// src/entity/JMAppClient.ts
|
|
757
835
|
var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
758
836
|
static {
|
|
@@ -774,11 +852,13 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
774
852
|
* koishi http
|
|
775
853
|
*/
|
|
776
854
|
http;
|
|
777
|
-
|
|
855
|
+
puppeteer;
|
|
856
|
+
constructor(root, http, config, logger, puppeteer) {
|
|
778
857
|
super(root);
|
|
779
858
|
this.config = config;
|
|
780
859
|
this.logger = logger;
|
|
781
860
|
this.http = http;
|
|
861
|
+
this.puppeteer = puppeteer;
|
|
782
862
|
}
|
|
783
863
|
/**
|
|
784
864
|
* 登录,未完成
|
|
@@ -880,6 +960,26 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
880
960
|
photo.setImageNames(image_ids);
|
|
881
961
|
return photo;
|
|
882
962
|
}
|
|
963
|
+
async getBlogById(id) {
|
|
964
|
+
if (this.config.debug) this.logger.info(`获取文库(${id})信息`);
|
|
965
|
+
const timestamp = this.getTimeStamp();
|
|
966
|
+
const { token, tokenparam } = this.getTokenAndTokenParam(timestamp);
|
|
967
|
+
const res = await requestWithUrlSwitch(
|
|
968
|
+
"/blog",
|
|
969
|
+
"POST",
|
|
970
|
+
{
|
|
971
|
+
params: { id },
|
|
972
|
+
headers: { token, tokenparam },
|
|
973
|
+
responseType: "json"
|
|
974
|
+
},
|
|
975
|
+
this.http,
|
|
976
|
+
this.config,
|
|
977
|
+
this.logger
|
|
978
|
+
);
|
|
979
|
+
const blog_json = this.decodeBase64(res.data, timestamp);
|
|
980
|
+
if (!blog_json?.info?.title) throw new AlbumNotExistError();
|
|
981
|
+
return JMAppBlog.fromJson(blog_json);
|
|
982
|
+
}
|
|
883
983
|
async downloadByAlbum(album) {
|
|
884
984
|
const id = album.getId();
|
|
885
985
|
const path = `${this.root}/album/${id}`;
|
|
@@ -935,6 +1035,60 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
935
1035
|
if (this.config.debug) this.logger.info(`${id} 下载完成,开始解密图片`);
|
|
936
1036
|
await this.decodeByPhoto(photo, type, albumId, single);
|
|
937
1037
|
}
|
|
1038
|
+
async downloadFirstImageByAlbum(album) {
|
|
1039
|
+
const id = album.getId();
|
|
1040
|
+
const path = `${this.root}/album/${id}`;
|
|
1041
|
+
await (0, import_promises2.mkdir)(path, { recursive: true });
|
|
1042
|
+
const photos = album.getPhotos();
|
|
1043
|
+
return await this.downloadAndDecodeFirstImageByPhoto(
|
|
1044
|
+
photos[0],
|
|
1045
|
+
"album",
|
|
1046
|
+
id,
|
|
1047
|
+
photos.length === 1
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
async downloadAndDecodeFirstImageByPhoto(photo, type = "photo", albumId = "", single = false) {
|
|
1051
|
+
const image = photo.getImages()[0];
|
|
1052
|
+
const id = photo.getId();
|
|
1053
|
+
let path = `${this.root}/${type}/${id}/origin`;
|
|
1054
|
+
if (type === "album") {
|
|
1055
|
+
if (single) {
|
|
1056
|
+
path = `${this.root}/${type}/${albumId}/origin`;
|
|
1057
|
+
} else {
|
|
1058
|
+
path = `${this.root}/${type}/${albumId}/origin/${id}`;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
if (this.config.debug) this.logger.info(`存储目录:${path}`);
|
|
1062
|
+
await (0, import_promises2.mkdir)(path, { recursive: true });
|
|
1063
|
+
const url = `/media/photos/${id}/${image}`;
|
|
1064
|
+
if (this.config.debug) this.logger.info(`下载图片:${url}`);
|
|
1065
|
+
const res = await requestWithUrlSwitch(
|
|
1066
|
+
url,
|
|
1067
|
+
"GET",
|
|
1068
|
+
{ responseType: "arraybuffer" },
|
|
1069
|
+
this.http,
|
|
1070
|
+
this.config,
|
|
1071
|
+
this.logger,
|
|
1072
|
+
"IMAGE"
|
|
1073
|
+
);
|
|
1074
|
+
await saveImage(res, `${path}/${image}`);
|
|
1075
|
+
if (this.config.debug) this.logger.info(`${id} 下载完成,开始解密图片`);
|
|
1076
|
+
let decodedPath = `${this.root}/${type}/${id}/decoded`;
|
|
1077
|
+
if (type === "album" && !single) {
|
|
1078
|
+
path = `${this.root}/${type}/${albumId}/origin/${id}`;
|
|
1079
|
+
decodedPath = `${this.root}/${type}/${albumId}/decoded/${id}`;
|
|
1080
|
+
}
|
|
1081
|
+
await (0, import_promises2.mkdir)(decodedPath, { recursive: true });
|
|
1082
|
+
const scramble_id = await this.requestScrambleId(id);
|
|
1083
|
+
photo.generateSplitNumbers(scramble_id);
|
|
1084
|
+
const splitNumber = photo.getSplitNumbers()[0];
|
|
1085
|
+
const imagePath = `${path}/${image}`;
|
|
1086
|
+
if (this.config.debug) this.logger.info(`解密图片:${imagePath}`);
|
|
1087
|
+
const decodedImagePath = `${decodedPath}/${image}`;
|
|
1088
|
+
const imageBuffer = await (0, import_promises2.readFile)(imagePath);
|
|
1089
|
+
await decodeImage(imageBuffer, splitNumber, decodedImagePath);
|
|
1090
|
+
return decodedImagePath;
|
|
1091
|
+
}
|
|
938
1092
|
async decodeByPhoto(photo, type = "photo", albumId = "", single = false) {
|
|
939
1093
|
const images = photo.getImages();
|
|
940
1094
|
const id = photo.getId();
|
|
@@ -1000,11 +1154,11 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
1000
1154
|
const id = photo.getId();
|
|
1001
1155
|
pdfName = sanitizeFileName(pdfName);
|
|
1002
1156
|
if (this.config.debug) this.logger.info(`开始生成PDF ${pdfName}.pdf`);
|
|
1003
|
-
let path = (0,
|
|
1157
|
+
let path = (0, import_path3.join)(this.root, type, `${id}`);
|
|
1004
1158
|
if (type === "album") {
|
|
1005
|
-
path = (0,
|
|
1159
|
+
path = (0, import_path3.join)(this.root, type, `${albumId}`);
|
|
1006
1160
|
}
|
|
1007
|
-
const pdfPath = (0,
|
|
1161
|
+
const pdfPath = (0, import_path3.join)(path, `${pdfName}.pdf`);
|
|
1008
1162
|
let pdfDoc;
|
|
1009
1163
|
try {
|
|
1010
1164
|
pdfDoc = new import_muhammara.Recipe("new", pdfPath, {
|
|
@@ -1013,17 +1167,17 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
1013
1167
|
} catch (error) {
|
|
1014
1168
|
throw new Error(error);
|
|
1015
1169
|
}
|
|
1016
|
-
const tempPath = (0,
|
|
1170
|
+
const tempPath = (0, import_path3.join)(path, `temp_${id}`);
|
|
1017
1171
|
await (0, import_promises2.mkdir)(tempPath);
|
|
1018
1172
|
for (const image of images) {
|
|
1019
|
-
let imagePath = (0,
|
|
1173
|
+
let imagePath = (0, import_path3.join)(path, "decoded", image);
|
|
1020
1174
|
if (type === "album" && !single) {
|
|
1021
|
-
imagePath = (0,
|
|
1175
|
+
imagePath = (0, import_path3.join)(path, "decoded", `${id}`, image);
|
|
1022
1176
|
}
|
|
1023
1177
|
const buffer = await (0, import_promises2.readFile)(imagePath);
|
|
1024
|
-
const ext = (0,
|
|
1178
|
+
const ext = (0, import_path3.extname)(imagePath);
|
|
1025
1179
|
const jpgName = image.replace(ext, ".jpg");
|
|
1026
|
-
const jpgPath = (0,
|
|
1180
|
+
const jpgPath = (0, import_path3.join)(tempPath, jpgName);
|
|
1027
1181
|
const sharpInstance = (0, import_sharp2.default)(buffer);
|
|
1028
1182
|
await sharpInstance.jpeg().toFile(jpgPath);
|
|
1029
1183
|
const metadata = await sharpInstance.metadata();
|
|
@@ -1047,24 +1201,81 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
1047
1201
|
}
|
|
1048
1202
|
return pdfPath;
|
|
1049
1203
|
}
|
|
1204
|
+
async blogToPdf(blog, password) {
|
|
1205
|
+
const pdfName = sanitizeFileName(blog.Info.title);
|
|
1206
|
+
if (this.config.debug) this.logger.info(`开始生成PDF ${pdfName}.pdf`);
|
|
1207
|
+
const path = (0, import_path3.join)(this.root, "blog", `${blog.Info.id}`);
|
|
1208
|
+
await (0, import_promises2.mkdir)(path, { recursive: true });
|
|
1209
|
+
const pdfPath = (0, import_path3.join)(path, `${pdfName}.pdf`);
|
|
1210
|
+
const pdfTempPath = (0, import_path3.join)(path, `temp.pdf`);
|
|
1211
|
+
await this.writeBlogPdf(blog, pdfTempPath);
|
|
1212
|
+
try {
|
|
1213
|
+
const recipe = new import_muhammara.Recipe(pdfTempPath, pdfPath);
|
|
1214
|
+
if (password) {
|
|
1215
|
+
recipe.encrypt({
|
|
1216
|
+
userPassword: password,
|
|
1217
|
+
ownerPassword: password,
|
|
1218
|
+
userProtectionFlag: 4
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
recipe.endPDF(() => {
|
|
1222
|
+
if (this.config.debug) this.logger.info(`PDF ${pdfName}.pdf 生成完成`);
|
|
1223
|
+
});
|
|
1224
|
+
} catch (error) {
|
|
1225
|
+
throw new Error(error);
|
|
1226
|
+
} finally {
|
|
1227
|
+
await (0, import_promises2.rm)(pdfTempPath, { recursive: true, force: true });
|
|
1228
|
+
}
|
|
1229
|
+
return pdfPath;
|
|
1230
|
+
}
|
|
1231
|
+
async writeBlogPdf(blog, pdfPath) {
|
|
1232
|
+
const context = await this.puppeteer.browser.createBrowserContext();
|
|
1233
|
+
const page = await context.newPage();
|
|
1234
|
+
await page.setViewport({
|
|
1235
|
+
width: 0,
|
|
1236
|
+
height: 0,
|
|
1237
|
+
deviceScaleFactor: 1
|
|
1238
|
+
});
|
|
1239
|
+
await page.evaluate((title) => {
|
|
1240
|
+
document.title = title;
|
|
1241
|
+
}, blog.Info.title);
|
|
1242
|
+
await page.setContent(blog.Info.content, {
|
|
1243
|
+
waitUntil: "networkidle0",
|
|
1244
|
+
timeout: 60 * 1e3
|
|
1245
|
+
});
|
|
1246
|
+
const pdfBuffer = await page.pdf({
|
|
1247
|
+
format: "A4",
|
|
1248
|
+
printBackground: true,
|
|
1249
|
+
// 确保背景颜色和图片被打印
|
|
1250
|
+
margin: {
|
|
1251
|
+
top: "25mm",
|
|
1252
|
+
right: "25mm",
|
|
1253
|
+
bottom: "25mm",
|
|
1254
|
+
left: "25mm"
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
await (0, import_promises2.writeFile)(pdfPath, pdfBuffer);
|
|
1258
|
+
context.close();
|
|
1259
|
+
page.close();
|
|
1260
|
+
}
|
|
1050
1261
|
async albumToZip(album, password, level = 6) {
|
|
1051
1262
|
const id = album.getId();
|
|
1052
1263
|
const series = album.getSeries();
|
|
1053
1264
|
const zipName = sanitizeFileName(album.getName());
|
|
1054
1265
|
if (this.config.debug) this.logger.info(`开始生成ZIP ${zipName}.zip`);
|
|
1055
|
-
const path = (0,
|
|
1266
|
+
const path = (0, import_path3.join)(this.root, "album", `${id}`);
|
|
1056
1267
|
const directorys = [];
|
|
1057
1268
|
if (series.length > 1) {
|
|
1058
1269
|
for (const s of series) {
|
|
1059
1270
|
directorys.push({
|
|
1060
|
-
directory: (0,
|
|
1271
|
+
directory: (0, import_path3.join)(path, "decoded", s.id),
|
|
1061
1272
|
destpath: `第${s.sort}章`
|
|
1062
1273
|
});
|
|
1063
1274
|
}
|
|
1064
1275
|
} else {
|
|
1065
|
-
directorys.push({ directory: (0,
|
|
1276
|
+
directorys.push({ directory: (0, import_path3.join)(path, "decoded"), destpath: false });
|
|
1066
1277
|
}
|
|
1067
|
-
const zipPath = (0,
|
|
1278
|
+
const zipPath = (0, import_path3.join)(path, `${zipName}.zip`);
|
|
1068
1279
|
await archiverImage(directorys, zipPath, password, level);
|
|
1069
1280
|
if (this.config.debug) this.logger.info(`ZIP ${zipName}.zip 生成完成`);
|
|
1070
1281
|
return zipPath;
|
|
@@ -1073,10 +1284,10 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
1073
1284
|
const id = photo.getId();
|
|
1074
1285
|
zipName = sanitizeFileName(zipName);
|
|
1075
1286
|
if (this.config.debug) this.logger.info(`开始生成ZIP ${zipName}.zip`);
|
|
1076
|
-
const path = (0,
|
|
1077
|
-
const zipPath = (0,
|
|
1287
|
+
const path = (0, import_path3.join)(this.root, "photo", `${id}`);
|
|
1288
|
+
const zipPath = (0, import_path3.join)(path, `${zipName}.zip`);
|
|
1078
1289
|
await archiverImage(
|
|
1079
|
-
[{ directory: (0,
|
|
1290
|
+
[{ directory: (0, import_path3.join)(path, "decoded"), destpath: false }],
|
|
1080
1291
|
zipPath,
|
|
1081
1292
|
password,
|
|
1082
1293
|
level
|
|
@@ -1084,6 +1295,18 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
1084
1295
|
if (this.config.debug) this.logger.info(`ZIP ${zipName}.zip 生成完成`);
|
|
1085
1296
|
return zipPath;
|
|
1086
1297
|
}
|
|
1298
|
+
async blogToZip(blog, password, level = 6) {
|
|
1299
|
+
const zipName = sanitizeFileName(blog.Info.title);
|
|
1300
|
+
if (this.config.debug) this.logger.info(`开始生成ZIP ${zipName}.zip`);
|
|
1301
|
+
const path = (0, import_path3.join)(this.root, "blog", `${blog.Info.id}`);
|
|
1302
|
+
await (0, import_promises2.mkdir)(path, { recursive: true });
|
|
1303
|
+
const pdfPath = (0, import_path3.join)(path, `${zipName}.pdf`);
|
|
1304
|
+
const zipPath = (0, import_path3.join)(path, `${zipName}.zip`);
|
|
1305
|
+
await this.writeBlogPdf(blog, pdfPath);
|
|
1306
|
+
await archiverFile(pdfPath, zipPath, password, level);
|
|
1307
|
+
if (this.config.debug) this.logger.info(`ZIP ${zipName}.zip 生成完成`);
|
|
1308
|
+
return zipPath;
|
|
1309
|
+
}
|
|
1087
1310
|
/**
|
|
1088
1311
|
* 获取时间戳
|
|
1089
1312
|
* @returns 时间戳
|
|
@@ -1147,7 +1370,7 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
|
|
|
1147
1370
|
// src/processors/jmProcessor.ts
|
|
1148
1371
|
var import_koishi = require("koishi");
|
|
1149
1372
|
var import_promises3 = require("node:fs/promises");
|
|
1150
|
-
var createJmProcessor = /* @__PURE__ */ __name((processorConfig, http, config, logger) => {
|
|
1373
|
+
var createJmProcessor = /* @__PURE__ */ __name((processorConfig, http, config, logger, puppeteer) => {
|
|
1151
1374
|
const {
|
|
1152
1375
|
root,
|
|
1153
1376
|
sendMethod,
|
|
@@ -1161,51 +1384,47 @@ var createJmProcessor = /* @__PURE__ */ __name((processorConfig, http, config, l
|
|
|
1161
1384
|
return async (payload) => {
|
|
1162
1385
|
const { id, session, messageId, scope } = payload;
|
|
1163
1386
|
try {
|
|
1164
|
-
const jmClient = new JMAppClient(root, http, config, logger);
|
|
1387
|
+
const jmClient = new JMAppClient(root, http, config, logger, puppeteer);
|
|
1165
1388
|
let filePath;
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1389
|
+
switch (payload.type) {
|
|
1390
|
+
case "album": {
|
|
1391
|
+
const album = await jmClient.getAlbumById(id);
|
|
1392
|
+
await jmClient.downloadByAlbum(album);
|
|
1393
|
+
if (sendMethod === "zip") {
|
|
1394
|
+
filePath = await jmClient.albumToZip(album, password, level);
|
|
1395
|
+
} else {
|
|
1396
|
+
filePath = await jmClient.albumToPdf(album, password);
|
|
1397
|
+
}
|
|
1398
|
+
break;
|
|
1176
1399
|
}
|
|
1177
|
-
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1400
|
+
case "photo": {
|
|
1401
|
+
const photo = await jmClient.getPhotoById(id);
|
|
1402
|
+
await jmClient.downloadByPhoto(photo);
|
|
1403
|
+
const photoName = photo.getName();
|
|
1404
|
+
if (sendMethod === "zip") {
|
|
1405
|
+
filePath = await jmClient.photoToZip(
|
|
1406
|
+
photo,
|
|
1407
|
+
photoName,
|
|
1408
|
+
password,
|
|
1409
|
+
level
|
|
1410
|
+
);
|
|
1411
|
+
} else {
|
|
1412
|
+
filePath = await jmClient.photoToPdf(photo, photoName);
|
|
1413
|
+
}
|
|
1414
|
+
break;
|
|
1188
1415
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
password,
|
|
1198
|
-
level
|
|
1199
|
-
);
|
|
1200
|
-
} else {
|
|
1201
|
-
filePath = await jmClient.photoToPdf(photo, photoName);
|
|
1416
|
+
case "blog": {
|
|
1417
|
+
const blog = await jmClient.getBlogById(id);
|
|
1418
|
+
if (sendMethod === "zip") {
|
|
1419
|
+
filePath = await jmClient.blogToZip(blog, password, level);
|
|
1420
|
+
} else {
|
|
1421
|
+
filePath = await jmClient.blogToPdf(blog, password);
|
|
1422
|
+
}
|
|
1423
|
+
return;
|
|
1202
1424
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
dir = fileInfo.dir;
|
|
1207
|
-
} else {
|
|
1208
|
-
throw new Error(`未知任务类型: ${payload.type}`);
|
|
1425
|
+
// 理论上不会走到这里,但为了类型安全和健壮性,可以抛出错误
|
|
1426
|
+
default:
|
|
1427
|
+
throw new Error(`未知任务类型: ${payload.type}`);
|
|
1209
1428
|
}
|
|
1210
1429
|
if (typeof filePath === "string") {
|
|
1211
1430
|
const {
|
|
@@ -1412,6 +1631,7 @@ var Queue = class {
|
|
|
1412
1631
|
};
|
|
1413
1632
|
|
|
1414
1633
|
// src/index.ts
|
|
1634
|
+
var import_promises4 = require("fs/promises");
|
|
1415
1635
|
var name = "jmcomic";
|
|
1416
1636
|
var Config = import_koishi2.Schema.intersect([
|
|
1417
1637
|
import_koishi2.Schema.object({
|
|
@@ -1427,6 +1647,9 @@ var Config = import_koishi2.Schema.intersect([
|
|
|
1427
1647
|
}),
|
|
1428
1648
|
import_koishi2.Schema.object({})
|
|
1429
1649
|
]),
|
|
1650
|
+
import_koishi2.Schema.object({
|
|
1651
|
+
listeningJMId: import_koishi2.Schema.boolean().default(false)
|
|
1652
|
+
}),
|
|
1430
1653
|
import_koishi2.Schema.object({
|
|
1431
1654
|
retryCount: import_koishi2.Schema.number().min(1).max(5).default(5),
|
|
1432
1655
|
concurrentDownloadLimit: import_koishi2.Schema.number().min(0).max(20).default(10),
|
|
@@ -1461,18 +1684,18 @@ var Config = import_koishi2.Schema.intersect([
|
|
|
1461
1684
|
"en-US": require_en_US()._config
|
|
1462
1685
|
});
|
|
1463
1686
|
var inject = {
|
|
1464
|
-
required: ["http"],
|
|
1687
|
+
required: ["http", "puppeteer"],
|
|
1465
1688
|
optional: ["notifier", "cron"]
|
|
1466
1689
|
};
|
|
1467
1690
|
async function apply(ctx, config) {
|
|
1468
1691
|
ctx.i18n.define("en-US", require_en_US());
|
|
1469
1692
|
ctx.i18n.define("zh-CN", require_zh_CN());
|
|
1470
1693
|
const logger = ctx.logger("jmcomic");
|
|
1471
|
-
const root = (0,
|
|
1694
|
+
const root = (0, import_path4.join)(ctx.baseDir, "data", "jmcomic");
|
|
1472
1695
|
const scheduleFn = /* @__PURE__ */ __name(async () => {
|
|
1473
|
-
const albumPath = (0,
|
|
1696
|
+
const albumPath = (0, import_path4.join)(ctx.baseDir, "data", "jmcomic", "album");
|
|
1474
1697
|
await deleteFewDaysAgoFolders(albumPath, config.keepDays);
|
|
1475
|
-
const photoPath = (0,
|
|
1698
|
+
const photoPath = (0, import_path4.join)(ctx.baseDir, "data", "jmcomic", "photo");
|
|
1476
1699
|
await deleteFewDaysAgoFolders(photoPath, config.keepDays);
|
|
1477
1700
|
}, "scheduleFn");
|
|
1478
1701
|
if (config.autoDelete && ctx.cron) {
|
|
@@ -1499,7 +1722,8 @@ async function apply(ctx, config) {
|
|
|
1499
1722
|
processorConfig,
|
|
1500
1723
|
ctx.http,
|
|
1501
1724
|
config,
|
|
1502
|
-
logger
|
|
1725
|
+
logger,
|
|
1726
|
+
ctx.puppeteer
|
|
1503
1727
|
);
|
|
1504
1728
|
const queue = new Queue(
|
|
1505
1729
|
jmProcessor,
|
|
@@ -1545,6 +1769,38 @@ async function apply(ctx, config) {
|
|
|
1545
1769
|
ctx.command("jm.photo <photoId:string>").alias("本子章节").action(async ({ session }, photoId) => {
|
|
1546
1770
|
await handleAlbumOrPhoto(session, photoId, "photo");
|
|
1547
1771
|
});
|
|
1772
|
+
ctx.command("jm.blog <blogId:string>").alias("JM文库").action(async ({ session }, blogId) => {
|
|
1773
|
+
const messageId = session.messageId;
|
|
1774
|
+
if (!/^\d+$/.test(blogId)) {
|
|
1775
|
+
await session.send([
|
|
1776
|
+
import_koishi2.h.quote(messageId),
|
|
1777
|
+
import_koishi2.h.text("输入的ID不合法,请检查")
|
|
1778
|
+
]);
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
const { task, pendingAhead, queuePosition } = queue.add({
|
|
1782
|
+
type: "blog",
|
|
1783
|
+
id: blogId,
|
|
1784
|
+
session,
|
|
1785
|
+
messageId,
|
|
1786
|
+
scope: session.scope
|
|
1787
|
+
});
|
|
1788
|
+
const params = {
|
|
1789
|
+
id: blogId,
|
|
1790
|
+
ahead: pendingAhead,
|
|
1791
|
+
pos: queuePosition,
|
|
1792
|
+
status: task.status
|
|
1793
|
+
};
|
|
1794
|
+
const msg = [import_koishi2.h.quote(messageId)];
|
|
1795
|
+
if (pendingAhead === 0 && queuePosition === 1) {
|
|
1796
|
+
msg.push(import_koishi2.h.text(session.text(".queueFirst", params)));
|
|
1797
|
+
} else if (pendingAhead > 0) {
|
|
1798
|
+
msg.push(import_koishi2.h.text(session.text(".queuePosition", params)));
|
|
1799
|
+
} else {
|
|
1800
|
+
msg.push(import_koishi2.h.text(session.text(".queueProcessing", params)));
|
|
1801
|
+
}
|
|
1802
|
+
await session.send(msg);
|
|
1803
|
+
});
|
|
1548
1804
|
ctx.command("jm.album.info <albumId:string>").alias("本子信息").action(async ({ session, options }, albumId) => {
|
|
1549
1805
|
const messageId = session.messageId;
|
|
1550
1806
|
if (!/^\d+$/.test(albumId)) {
|
|
@@ -1555,7 +1811,13 @@ async function apply(ctx, config) {
|
|
|
1555
1811
|
return;
|
|
1556
1812
|
}
|
|
1557
1813
|
try {
|
|
1558
|
-
const jmClient = new JMAppClient(
|
|
1814
|
+
const jmClient = new JMAppClient(
|
|
1815
|
+
root,
|
|
1816
|
+
ctx.http,
|
|
1817
|
+
config,
|
|
1818
|
+
logger,
|
|
1819
|
+
ctx.puppeteer
|
|
1820
|
+
);
|
|
1559
1821
|
const album = await jmClient.getAlbumById(albumId);
|
|
1560
1822
|
await session.send([
|
|
1561
1823
|
import_koishi2.h.quote(messageId),
|
|
@@ -1599,7 +1861,13 @@ async function apply(ctx, config) {
|
|
|
1599
1861
|
return;
|
|
1600
1862
|
}
|
|
1601
1863
|
try {
|
|
1602
|
-
const jmClient = new JMAppClient(
|
|
1864
|
+
const jmClient = new JMAppClient(
|
|
1865
|
+
root,
|
|
1866
|
+
ctx.http,
|
|
1867
|
+
config,
|
|
1868
|
+
logger,
|
|
1869
|
+
ctx.puppeteer
|
|
1870
|
+
);
|
|
1603
1871
|
const searchResult = await jmClient.search(keyword);
|
|
1604
1872
|
const contents = searchResult.content;
|
|
1605
1873
|
const fragment = [import_koishi2.h.quote(messageId)];
|
|
@@ -1681,6 +1949,27 @@ async function apply(ctx, config) {
|
|
|
1681
1949
|
});
|
|
1682
1950
|
await session.send([import_koishi2.h.quote(messageId), ...taskInfos]);
|
|
1683
1951
|
});
|
|
1952
|
+
if (config.listeningJMId) {
|
|
1953
|
+
ctx.on("message", async (session) => {
|
|
1954
|
+
const text = session.content;
|
|
1955
|
+
const regexp = /JM\d+/gi;
|
|
1956
|
+
const numbers = text.match(regexp);
|
|
1957
|
+
if (!numbers?.length) return;
|
|
1958
|
+
const id = numbers[0];
|
|
1959
|
+
const jmClient = new JMAppClient(
|
|
1960
|
+
root,
|
|
1961
|
+
ctx.http,
|
|
1962
|
+
config,
|
|
1963
|
+
logger,
|
|
1964
|
+
ctx.puppeteer
|
|
1965
|
+
);
|
|
1966
|
+
const album = await jmClient.getAlbumById(id.replace(/JM/i, ""));
|
|
1967
|
+
const imagePath = await jmClient.downloadFirstImageByAlbum(album);
|
|
1968
|
+
const messageId = session.messageId;
|
|
1969
|
+
const imageBuffer = await (0, import_promises4.readFile)(imagePath);
|
|
1970
|
+
session.send([import_koishi2.h.quote(messageId), import_koishi2.h.image(imageBuffer, "webp")]);
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1684
1973
|
}
|
|
1685
1974
|
__name(apply, "apply");
|
|
1686
1975
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Config } from "..";
|
|
2
2
|
import { Logger, Session, HTTP } from "koishi";
|
|
3
|
+
import Puppeteer from "koishi-plugin-puppeteer";
|
|
3
4
|
export type JmTaskPayload = {
|
|
4
|
-
type: "album" | "photo";
|
|
5
|
+
type: "album" | "photo" | "blog";
|
|
5
6
|
id: string;
|
|
6
7
|
session: Session;
|
|
7
8
|
messageId: string;
|
|
@@ -17,5 +18,5 @@ interface ProcessorConfig {
|
|
|
17
18
|
cache: boolean;
|
|
18
19
|
debug: boolean;
|
|
19
20
|
}
|
|
20
|
-
export declare const createJmProcessor: (processorConfig: ProcessorConfig, http: HTTP, config: Config, logger: Logger) => (payload: JmTaskPayload) => Promise<void>;
|
|
21
|
+
export declare const createJmProcessor: (processorConfig: ProcessorConfig, http: HTTP, config: Config, logger: Logger, puppeteer: Puppeteer) => (payload: JmTaskPayload) => Promise<void>;
|
|
21
22
|
export {};
|
package/lib/utils/Image.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Directorys } from "../types";
|
|
2
|
-
export declare function decodeImage(imageBuffer: Buffer
|
|
3
|
-
export declare function saveImage(imageBuffer: Buffer
|
|
2
|
+
export declare function decodeImage(imageBuffer: Buffer, num: number, path: string): Promise<void>;
|
|
3
|
+
export declare function saveImage(imageBuffer: Buffer, path: string): Promise<void>;
|
|
4
4
|
/**
|
|
5
5
|
* 将目录压缩成 ZIP 文件,并可选附带密码
|
|
6
6
|
* @param directory 要压缩的目录路径
|
|
@@ -9,3 +9,4 @@ export declare function saveImage(imageBuffer: Buffer | ArrayBuffer, path: strin
|
|
|
9
9
|
* @param level 压缩级别,范围 0-9,默认 9
|
|
10
10
|
*/
|
|
11
11
|
export declare function archiverImage(directorys: Directorys[], outputPath: string, password?: string, level?: number): Promise<void>;
|
|
12
|
+
export declare function archiverFile(path: string, outputPath: string, password?: string, level?: number): Promise<void>;
|
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 blog xxxx
|
|
13
14
|
jm queue
|
|
14
15
|
```
|
|
15
16
|
|
|
@@ -17,6 +18,20 @@ jm queue
|
|
|
17
18
|
|
|
18
19
|
## 更新日志
|
|
19
20
|
|
|
21
|
+
<details>
|
|
22
|
+
<summary>0.2.7</summary>
|
|
23
|
+
|
|
24
|
+
支持 JM 文库
|
|
25
|
+
|
|
26
|
+
</details>
|
|
27
|
+
|
|
28
|
+
<details>
|
|
29
|
+
<summary>0.2.6</summary>
|
|
30
|
+
|
|
31
|
+
添加监听聊天内容,检测到 `JMxxx` 时发送第一张图片(不是封面)
|
|
32
|
+
|
|
33
|
+
</details>
|
|
34
|
+
|
|
20
35
|
<details>
|
|
21
36
|
<summary>0.2.5</summary>
|
|
22
37
|
|
|
@@ -28,6 +43,7 @@ jm queue
|
|
|
28
43
|
<summary>0.2.4</summary>
|
|
29
44
|
|
|
30
45
|
1. 调整 debug 日志
|
|
46
|
+
|
|
31
47
|
2. 修正文化名返回值
|
|
32
48
|
|
|
33
49
|
</details>
|
|
@@ -36,6 +52,7 @@ jm queue
|
|
|
36
52
|
<summary>0.2.3</summary>
|
|
37
53
|
|
|
38
54
|
1. 将手动路径拼接替换为 join
|
|
55
|
+
|
|
39
56
|
2. 健壮文件名序列化,尝试修复部分文件名导致无法创建文件
|
|
40
57
|
|
|
41
58
|
</details>
|
|
@@ -51,6 +68,7 @@ jm queue
|
|
|
51
68
|
<summary>0.2.1</summary>
|
|
52
69
|
|
|
53
70
|
1. 搜索分页限制
|
|
71
|
+
|
|
54
72
|
2. 搜索结果分割空行
|
|
55
73
|
|
|
56
74
|
</details>
|
|
@@ -59,6 +77,7 @@ jm queue
|
|
|
59
77
|
<summary>0.2.0</summary>
|
|
60
78
|
|
|
61
79
|
1. 简易搜索功能
|
|
80
|
+
|
|
62
81
|
2. 修复队列丢失 i18n key 的问题
|
|
63
82
|
|
|
64
83
|
</details>
|
|
@@ -67,6 +86,7 @@ jm queue
|
|
|
67
86
|
<summary>0.1.1</summary>
|
|
68
87
|
|
|
69
88
|
1. 添加队列时返回队列信息
|
|
89
|
+
|
|
70
90
|
2. 提取代码
|
|
71
91
|
|
|
72
92
|
</details>
|
|
@@ -75,43 +95,59 @@ jm queue
|
|
|
75
95
|
<summary>0.1.0</summary>
|
|
76
96
|
|
|
77
97
|
1. 队列系统
|
|
98
|
+
|
|
78
99
|
2. 下载并发与解密并发限制
|
|
100
|
+
|
|
79
101
|
3. 修改配置页面顺序、分类
|
|
102
|
+
|
|
80
103
|
4. 不再直接暴露变量,改为逐级传递
|
|
104
|
+
|
|
81
105
|
5. 统一暴露 Error 类
|
|
106
|
+
|
|
82
107
|
6. 添加域名切换条件
|
|
83
108
|
|
|
84
109
|
</details>
|
|
85
110
|
|
|
86
111
|
<details>
|
|
87
112
|
<summary>0.0.6</summary>
|
|
113
|
+
|
|
88
114
|
添加了一些错误提示
|
|
115
|
+
|
|
89
116
|
</details>
|
|
90
117
|
|
|
91
118
|
<details>
|
|
92
119
|
<summary>0.0.5</summary>
|
|
120
|
+
|
|
93
121
|
修改使用示例
|
|
122
|
+
|
|
94
123
|
</details>
|
|
95
124
|
|
|
96
125
|
<details>
|
|
97
126
|
<summary>0.0.4</summary>
|
|
98
127
|
|
|
99
128
|
1. 文件名移除前后空格
|
|
129
|
+
|
|
100
130
|
2. 新增文件发送配置,用于配置文件是以 buffer 读取后发送还是以本地地址的形式发送。docker 中使用 file 形式需要在 bot 实现端同时映射/koishi 目录
|
|
101
131
|
|
|
102
132
|
</details>
|
|
103
133
|
|
|
104
134
|
<details>
|
|
105
135
|
<summary>0.0.3</summary>
|
|
136
|
+
|
|
106
137
|
忘了给自动删除做判断
|
|
138
|
+
|
|
107
139
|
</details>
|
|
108
140
|
|
|
109
141
|
<details>
|
|
110
142
|
<summary>0.0.2</summary>
|
|
143
|
+
|
|
111
144
|
依赖从peerDependencies移动到dependencies
|
|
145
|
+
|
|
112
146
|
</details>
|
|
113
147
|
|
|
114
148
|
<details>
|
|
115
149
|
<summary>0.0.1</summary>
|
|
150
|
+
|
|
116
151
|
初版
|
|
152
|
+
|
|
117
153
|
</details>
|