@wahaha216/koishi-plugin-jmcomic 0.2.6 → 0.2.8

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.
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ import { JMBlogAbstract } from "../abstract/JMBlogAbstract";
2
+ import { IJMBlog } from "../types/JMClient";
3
+ export declare class JMAppBlog extends JMBlogAbstract {
4
+ constructor(json: IJMBlog);
5
+ static fromJson(json: IJMBlog): JMAppBlog;
6
+ }
@@ -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
- constructor(root: string, http: HTTP, config: Config, logger: Logger);
27
+ private puppeteer;
28
+ constructor(root: string, http: HTTP, config: Config, logger: Logger, puppeteer: Puppeteer);
26
29
  /**
27
30
  * 登录,未完成
28
31
  * @param username 用户名
@@ -33,6 +36,7 @@ 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>;
38
42
  downloadFirstImageByAlbum(album: JMAppAlbum): Promise<string>;
@@ -40,8 +44,11 @@ export declare class JMAppClient extends JMClientAbstract {
40
44
  decodeByPhoto(photo: JMPhotoAbstract, type?: "photo" | "album", albumId?: string, single?: boolean): Promise<void>;
41
45
  albumToPdf(album: JMAppAlbum, password?: string): Promise<string | string[]>;
42
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;
43
49
  albumToZip(album: JMAppAlbum, password?: string, level?: number): Promise<string>;
44
50
  photoToZip(photo: JMPhotoAbstract, zipName: string, password?: string, level?: number): Promise<string>;
51
+ blogToZip(blog: JMAppBlog, password?: string, level?: number): Promise<string>;
45
52
  /**
46
53
  * 获取时间戳
47
54
  * @returns 时间戳
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为仅存储" }, { listeningJMId: "监听 `JM ID` 关键词,当出现JMxxxxx的关键词时,发送第一张图片" }, { $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.", 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" }] };
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 import_path3 = require("path");
57
+ var import_path4 = require("path");
58
58
 
59
59
  // src/utils/Utils.ts
60
60
  var import_fs = require("fs");
@@ -65,10 +65,10 @@ var JM_SCRAMBLE_ID = /var scramble_id = (\d+);/;
65
65
 
66
66
  // src/utils/Const.ts
67
67
  var JM_CLIENT_URL_LIST = [
68
- "www.cdnmhwscc.vip",
69
- "www.cdnblackmyth.club",
70
- "www.cdnmhws.cc",
71
- "www.cdnuc.vip"
68
+ "www.cdnaspa.vip",
69
+ "www.cdnaspa.club",
70
+ "www.cdnplaystation6.vip",
71
+ "www.cdnplaystation6.cc"
72
72
  ];
73
73
  var JM_IMAGE_URL_LIST = [
74
74
  "cdn-msp.jmapiproxy1.cc",
@@ -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 import_path2 = require("path");
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
- constructor(root, http, config, logger) {
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
  * 登录,未完成
@@ -853,6 +933,8 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
853
933
  const photo = await this.getPhotoById(id);
854
934
  photos.push(photo);
855
935
  }
936
+ if (this.config.debug)
937
+ this.logger.info(`本子 ${id} 章节数:${photos.length}`);
856
938
  album.setPhotos(photos);
857
939
  return album;
858
940
  }
@@ -880,6 +962,26 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
880
962
  photo.setImageNames(image_ids);
881
963
  return photo;
882
964
  }
965
+ async getBlogById(id) {
966
+ if (this.config.debug) this.logger.info(`获取文库(${id})信息`);
967
+ const timestamp = this.getTimeStamp();
968
+ const { token, tokenparam } = this.getTokenAndTokenParam(timestamp);
969
+ const res = await requestWithUrlSwitch(
970
+ "/blog",
971
+ "POST",
972
+ {
973
+ params: { id },
974
+ headers: { token, tokenparam },
975
+ responseType: "json"
976
+ },
977
+ this.http,
978
+ this.config,
979
+ this.logger
980
+ );
981
+ const blog_json = this.decodeBase64(res.data, timestamp);
982
+ if (!blog_json?.info?.title) throw new AlbumNotExistError();
983
+ return JMAppBlog.fromJson(blog_json);
984
+ }
883
985
  async downloadByAlbum(album) {
884
986
  const id = album.getId();
885
987
  const path = `${this.root}/album/${id}`;
@@ -1054,11 +1156,11 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
1054
1156
  const id = photo.getId();
1055
1157
  pdfName = sanitizeFileName(pdfName);
1056
1158
  if (this.config.debug) this.logger.info(`开始生成PDF ${pdfName}.pdf`);
1057
- let path = (0, import_path2.join)(this.root, type, `${id}`);
1159
+ let path = (0, import_path3.join)(this.root, type, `${id}`);
1058
1160
  if (type === "album") {
1059
- path = (0, import_path2.join)(this.root, type, `${albumId}`);
1161
+ path = (0, import_path3.join)(this.root, type, `${albumId}`);
1060
1162
  }
1061
- const pdfPath = (0, import_path2.join)(path, `${pdfName}.pdf`);
1163
+ const pdfPath = (0, import_path3.join)(path, `${pdfName}.pdf`);
1062
1164
  let pdfDoc;
1063
1165
  try {
1064
1166
  pdfDoc = new import_muhammara.Recipe("new", pdfPath, {
@@ -1067,17 +1169,17 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
1067
1169
  } catch (error) {
1068
1170
  throw new Error(error);
1069
1171
  }
1070
- const tempPath = (0, import_path2.join)(path, `temp_${id}`);
1172
+ const tempPath = (0, import_path3.join)(path, `temp_${id}`);
1071
1173
  await (0, import_promises2.mkdir)(tempPath);
1072
1174
  for (const image of images) {
1073
- let imagePath = (0, import_path2.join)(path, "decoded", image);
1175
+ let imagePath = (0, import_path3.join)(path, "decoded", image);
1074
1176
  if (type === "album" && !single) {
1075
- imagePath = (0, import_path2.join)(path, "decoded", `${id}`, image);
1177
+ imagePath = (0, import_path3.join)(path, "decoded", `${id}`, image);
1076
1178
  }
1077
1179
  const buffer = await (0, import_promises2.readFile)(imagePath);
1078
- const ext = (0, import_path2.extname)(imagePath);
1180
+ const ext = (0, import_path3.extname)(imagePath);
1079
1181
  const jpgName = image.replace(ext, ".jpg");
1080
- const jpgPath = (0, import_path2.join)(tempPath, jpgName);
1182
+ const jpgPath = (0, import_path3.join)(tempPath, jpgName);
1081
1183
  const sharpInstance = (0, import_sharp2.default)(buffer);
1082
1184
  await sharpInstance.jpeg().toFile(jpgPath);
1083
1185
  const metadata = await sharpInstance.metadata();
@@ -1101,24 +1203,81 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
1101
1203
  }
1102
1204
  return pdfPath;
1103
1205
  }
1206
+ async blogToPdf(blog, password) {
1207
+ const pdfName = sanitizeFileName(blog.Info.title);
1208
+ if (this.config.debug) this.logger.info(`开始生成PDF ${pdfName}.pdf`);
1209
+ const path = (0, import_path3.join)(this.root, "blog", `${blog.Info.id}`);
1210
+ await (0, import_promises2.mkdir)(path, { recursive: true });
1211
+ const pdfPath = (0, import_path3.join)(path, `${pdfName}.pdf`);
1212
+ const pdfTempPath = (0, import_path3.join)(path, `temp.pdf`);
1213
+ await this.writeBlogPdf(blog, pdfTempPath);
1214
+ try {
1215
+ const recipe = new import_muhammara.Recipe(pdfTempPath, pdfPath);
1216
+ if (password) {
1217
+ recipe.encrypt({
1218
+ userPassword: password,
1219
+ ownerPassword: password,
1220
+ userProtectionFlag: 4
1221
+ });
1222
+ }
1223
+ recipe.endPDF(() => {
1224
+ if (this.config.debug) this.logger.info(`PDF ${pdfName}.pdf 生成完成`);
1225
+ });
1226
+ } catch (error) {
1227
+ throw new Error(error);
1228
+ } finally {
1229
+ await (0, import_promises2.rm)(pdfTempPath, { recursive: true, force: true });
1230
+ }
1231
+ return pdfPath;
1232
+ }
1233
+ async writeBlogPdf(blog, pdfPath) {
1234
+ const context = await this.puppeteer.browser.createBrowserContext();
1235
+ const page = await context.newPage();
1236
+ await page.setViewport({
1237
+ width: 0,
1238
+ height: 0,
1239
+ deviceScaleFactor: 1
1240
+ });
1241
+ await page.evaluate((title) => {
1242
+ document.title = title;
1243
+ }, blog.Info.title);
1244
+ await page.setContent(blog.Info.content, {
1245
+ waitUntil: "networkidle0",
1246
+ timeout: 60 * 1e3
1247
+ });
1248
+ const pdfBuffer = await page.pdf({
1249
+ format: "A4",
1250
+ printBackground: true,
1251
+ // 确保背景颜色和图片被打印
1252
+ margin: {
1253
+ top: "25mm",
1254
+ right: "25mm",
1255
+ bottom: "25mm",
1256
+ left: "25mm"
1257
+ }
1258
+ });
1259
+ await (0, import_promises2.writeFile)(pdfPath, pdfBuffer);
1260
+ context.close();
1261
+ page.close();
1262
+ }
1104
1263
  async albumToZip(album, password, level = 6) {
1105
1264
  const id = album.getId();
1106
1265
  const series = album.getSeries();
1107
1266
  const zipName = sanitizeFileName(album.getName());
1108
1267
  if (this.config.debug) this.logger.info(`开始生成ZIP ${zipName}.zip`);
1109
- const path = (0, import_path2.join)(this.root, "album", `${id}`);
1268
+ const path = (0, import_path3.join)(this.root, "album", `${id}`);
1110
1269
  const directorys = [];
1111
1270
  if (series.length > 1) {
1112
1271
  for (const s of series) {
1113
1272
  directorys.push({
1114
- directory: (0, import_path2.join)(path, "decoded", s.id),
1273
+ directory: (0, import_path3.join)(path, "decoded", s.id),
1115
1274
  destpath: `第${s.sort}章`
1116
1275
  });
1117
1276
  }
1118
1277
  } else {
1119
- directorys.push({ directory: (0, import_path2.join)(path, "decoded"), destpath: false });
1278
+ directorys.push({ directory: (0, import_path3.join)(path, "decoded"), destpath: false });
1120
1279
  }
1121
- const zipPath = (0, import_path2.join)(path, `${zipName}.zip`);
1280
+ const zipPath = (0, import_path3.join)(path, `${zipName}.zip`);
1122
1281
  await archiverImage(directorys, zipPath, password, level);
1123
1282
  if (this.config.debug) this.logger.info(`ZIP ${zipName}.zip 生成完成`);
1124
1283
  return zipPath;
@@ -1127,10 +1286,10 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
1127
1286
  const id = photo.getId();
1128
1287
  zipName = sanitizeFileName(zipName);
1129
1288
  if (this.config.debug) this.logger.info(`开始生成ZIP ${zipName}.zip`);
1130
- const path = (0, import_path2.join)(this.root, "photo", `${id}`);
1131
- const zipPath = (0, import_path2.join)(path, `${zipName}.zip`);
1289
+ const path = (0, import_path3.join)(this.root, "photo", `${id}`);
1290
+ const zipPath = (0, import_path3.join)(path, `${zipName}.zip`);
1132
1291
  await archiverImage(
1133
- [{ directory: (0, import_path2.join)(path, "decoded"), destpath: false }],
1292
+ [{ directory: (0, import_path3.join)(path, "decoded"), destpath: false }],
1134
1293
  zipPath,
1135
1294
  password,
1136
1295
  level
@@ -1138,6 +1297,18 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
1138
1297
  if (this.config.debug) this.logger.info(`ZIP ${zipName}.zip 生成完成`);
1139
1298
  return zipPath;
1140
1299
  }
1300
+ async blogToZip(blog, password, level = 6) {
1301
+ const zipName = sanitizeFileName(blog.Info.title);
1302
+ if (this.config.debug) this.logger.info(`开始生成ZIP ${zipName}.zip`);
1303
+ const path = (0, import_path3.join)(this.root, "blog", `${blog.Info.id}`);
1304
+ await (0, import_promises2.mkdir)(path, { recursive: true });
1305
+ const pdfPath = (0, import_path3.join)(path, `${zipName}.pdf`);
1306
+ const zipPath = (0, import_path3.join)(path, `${zipName}.zip`);
1307
+ await this.writeBlogPdf(blog, pdfPath);
1308
+ await archiverFile(pdfPath, zipPath, password, level);
1309
+ if (this.config.debug) this.logger.info(`ZIP ${zipName}.zip 生成完成`);
1310
+ return zipPath;
1311
+ }
1141
1312
  /**
1142
1313
  * 获取时间戳
1143
1314
  * @returns 时间戳
@@ -1201,7 +1372,7 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
1201
1372
  // src/processors/jmProcessor.ts
1202
1373
  var import_koishi = require("koishi");
1203
1374
  var import_promises3 = require("node:fs/promises");
1204
- var createJmProcessor = /* @__PURE__ */ __name((processorConfig, http, config, logger) => {
1375
+ var createJmProcessor = /* @__PURE__ */ __name((processorConfig, http, config, logger, puppeteer) => {
1205
1376
  const {
1206
1377
  root,
1207
1378
  sendMethod,
@@ -1215,51 +1386,47 @@ var createJmProcessor = /* @__PURE__ */ __name((processorConfig, http, config, l
1215
1386
  return async (payload) => {
1216
1387
  const { id, session, messageId, scope } = payload;
1217
1388
  try {
1218
- const jmClient = new JMAppClient(root, http, config, logger);
1389
+ const jmClient = new JMAppClient(root, http, config, logger, puppeteer);
1219
1390
  let filePath;
1220
- let name2;
1221
- let ext;
1222
- let dir = "";
1223
- if (payload.type === "album") {
1224
- const album = await jmClient.getAlbumById(id);
1225
- await jmClient.downloadByAlbum(album);
1226
- if (sendMethod === "zip") {
1227
- filePath = await jmClient.albumToZip(album, password, level);
1228
- } else {
1229
- filePath = await jmClient.albumToPdf(album, password);
1391
+ switch (payload.type) {
1392
+ case "album": {
1393
+ const album = await jmClient.getAlbumById(id);
1394
+ await jmClient.downloadByAlbum(album);
1395
+ if (sendMethod === "zip") {
1396
+ filePath = await jmClient.albumToZip(album, password, level);
1397
+ } else {
1398
+ filePath = await jmClient.albumToPdf(album, password);
1399
+ }
1400
+ break;
1230
1401
  }
1231
- if (typeof filePath === "string") {
1232
- const fileInfo = getFileInfo(filePath);
1233
- name2 = formatFileName(fileName, fileInfo.fileName, id);
1234
- ext = fileInfo.ext;
1235
- dir = fileInfo.dir;
1236
- } else {
1237
- const firstPath = filePath[0];
1238
- const fileInfo = getFileInfo(firstPath);
1239
- name2 = formatFileName(fileName, fileInfo.fileName, id);
1240
- ext = fileInfo.ext;
1241
- dir = fileInfo.dir;
1402
+ case "photo": {
1403
+ const photo = await jmClient.getPhotoById(id);
1404
+ await jmClient.downloadByPhoto(photo);
1405
+ const photoName = photo.getName();
1406
+ if (sendMethod === "zip") {
1407
+ filePath = await jmClient.photoToZip(
1408
+ photo,
1409
+ photoName,
1410
+ password,
1411
+ level
1412
+ );
1413
+ } else {
1414
+ filePath = await jmClient.photoToPdf(photo, photoName);
1415
+ }
1416
+ break;
1242
1417
  }
1243
- } else if (payload.type === "photo") {
1244
- const photo = await jmClient.getPhotoById(id);
1245
- await jmClient.downloadByPhoto(photo);
1246
- const photoName = photo.getName();
1247
- if (sendMethod === "zip") {
1248
- filePath = await jmClient.photoToZip(
1249
- photo,
1250
- photoName,
1251
- password,
1252
- level
1253
- );
1254
- } else {
1255
- filePath = await jmClient.photoToPdf(photo, photoName);
1418
+ case "blog": {
1419
+ const blog = await jmClient.getBlogById(id);
1420
+ if (sendMethod === "zip") {
1421
+ filePath = await jmClient.blogToZip(blog, password, level);
1422
+ } else {
1423
+ filePath = await jmClient.blogToPdf(blog, password);
1424
+ }
1425
+ return;
1256
1426
  }
1257
- const fileInfo = getFileInfo(filePath);
1258
- name2 = formatFileName(fileName, fileInfo.fileName, id);
1259
- ext = fileInfo.ext;
1260
- dir = fileInfo.dir;
1261
- } else {
1262
- throw new Error(`未知任务类型: ${payload.type}`);
1427
+ // 理论上不会走到这里,但为了类型安全和健壮性,可以抛出错误
1428
+ default:
1429
+ throw new Error(`未知任务类型: ${payload.type}`);
1263
1430
  }
1264
1431
  if (typeof filePath === "string") {
1265
1432
  const {
@@ -1519,18 +1686,18 @@ var Config = import_koishi2.Schema.intersect([
1519
1686
  "en-US": require_en_US()._config
1520
1687
  });
1521
1688
  var inject = {
1522
- required: ["http"],
1689
+ required: ["http", "puppeteer"],
1523
1690
  optional: ["notifier", "cron"]
1524
1691
  };
1525
1692
  async function apply(ctx, config) {
1526
1693
  ctx.i18n.define("en-US", require_en_US());
1527
1694
  ctx.i18n.define("zh-CN", require_zh_CN());
1528
1695
  const logger = ctx.logger("jmcomic");
1529
- const root = (0, import_path3.join)(ctx.baseDir, "data", "jmcomic");
1696
+ const root = (0, import_path4.join)(ctx.baseDir, "data", "jmcomic");
1530
1697
  const scheduleFn = /* @__PURE__ */ __name(async () => {
1531
- const albumPath = (0, import_path3.join)(ctx.baseDir, "data", "jmcomic", "album");
1698
+ const albumPath = (0, import_path4.join)(ctx.baseDir, "data", "jmcomic", "album");
1532
1699
  await deleteFewDaysAgoFolders(albumPath, config.keepDays);
1533
- const photoPath = (0, import_path3.join)(ctx.baseDir, "data", "jmcomic", "photo");
1700
+ const photoPath = (0, import_path4.join)(ctx.baseDir, "data", "jmcomic", "photo");
1534
1701
  await deleteFewDaysAgoFolders(photoPath, config.keepDays);
1535
1702
  }, "scheduleFn");
1536
1703
  if (config.autoDelete && ctx.cron) {
@@ -1557,7 +1724,8 @@ async function apply(ctx, config) {
1557
1724
  processorConfig,
1558
1725
  ctx.http,
1559
1726
  config,
1560
- logger
1727
+ logger,
1728
+ ctx.puppeteer
1561
1729
  );
1562
1730
  const queue = new Queue(
1563
1731
  jmProcessor,
@@ -1603,6 +1771,38 @@ async function apply(ctx, config) {
1603
1771
  ctx.command("jm.photo <photoId:string>").alias("本子章节").action(async ({ session }, photoId) => {
1604
1772
  await handleAlbumOrPhoto(session, photoId, "photo");
1605
1773
  });
1774
+ ctx.command("jm.blog <blogId:string>").alias("JM文库").action(async ({ session }, blogId) => {
1775
+ const messageId = session.messageId;
1776
+ if (!/^\d+$/.test(blogId)) {
1777
+ await session.send([
1778
+ import_koishi2.h.quote(messageId),
1779
+ import_koishi2.h.text("输入的ID不合法,请检查")
1780
+ ]);
1781
+ return;
1782
+ }
1783
+ const { task, pendingAhead, queuePosition } = queue.add({
1784
+ type: "blog",
1785
+ id: blogId,
1786
+ session,
1787
+ messageId,
1788
+ scope: session.scope
1789
+ });
1790
+ const params = {
1791
+ id: blogId,
1792
+ ahead: pendingAhead,
1793
+ pos: queuePosition,
1794
+ status: task.status
1795
+ };
1796
+ const msg = [import_koishi2.h.quote(messageId)];
1797
+ if (pendingAhead === 0 && queuePosition === 1) {
1798
+ msg.push(import_koishi2.h.text(session.text(".queueFirst", params)));
1799
+ } else if (pendingAhead > 0) {
1800
+ msg.push(import_koishi2.h.text(session.text(".queuePosition", params)));
1801
+ } else {
1802
+ msg.push(import_koishi2.h.text(session.text(".queueProcessing", params)));
1803
+ }
1804
+ await session.send(msg);
1805
+ });
1606
1806
  ctx.command("jm.album.info <albumId:string>").alias("本子信息").action(async ({ session, options }, albumId) => {
1607
1807
  const messageId = session.messageId;
1608
1808
  if (!/^\d+$/.test(albumId)) {
@@ -1613,7 +1813,13 @@ async function apply(ctx, config) {
1613
1813
  return;
1614
1814
  }
1615
1815
  try {
1616
- const jmClient = new JMAppClient(root, ctx.http, config, logger);
1816
+ const jmClient = new JMAppClient(
1817
+ root,
1818
+ ctx.http,
1819
+ config,
1820
+ logger,
1821
+ ctx.puppeteer
1822
+ );
1617
1823
  const album = await jmClient.getAlbumById(albumId);
1618
1824
  await session.send([
1619
1825
  import_koishi2.h.quote(messageId),
@@ -1657,7 +1863,13 @@ async function apply(ctx, config) {
1657
1863
  return;
1658
1864
  }
1659
1865
  try {
1660
- const jmClient = new JMAppClient(root, ctx.http, config, logger);
1866
+ const jmClient = new JMAppClient(
1867
+ root,
1868
+ ctx.http,
1869
+ config,
1870
+ logger,
1871
+ ctx.puppeteer
1872
+ );
1661
1873
  const searchResult = await jmClient.search(keyword);
1662
1874
  const contents = searchResult.content;
1663
1875
  const fragment = [import_koishi2.h.quote(messageId)];
@@ -1746,7 +1958,13 @@ async function apply(ctx, config) {
1746
1958
  const numbers = text.match(regexp);
1747
1959
  if (!numbers?.length) return;
1748
1960
  const id = numbers[0];
1749
- const jmClient = new JMAppClient(root, ctx.http, config, logger);
1961
+ const jmClient = new JMAppClient(
1962
+ root,
1963
+ ctx.http,
1964
+ config,
1965
+ logger,
1966
+ ctx.puppeteer
1967
+ );
1750
1968
  const album = await jmClient.getAlbumById(id.replace(/JM/i, ""));
1751
1969
  const imagePath = await jmClient.downloadFirstImageByAlbum(album);
1752
1970
  const messageId = session.messageId;
@@ -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 {};
@@ -1,6 +1,6 @@
1
1
  import { Directorys } from "../types";
2
- export declare function decodeImage(imageBuffer: Buffer | ArrayBuffer, num: number, path: string): Promise<void>;
3
- export declare function saveImage(imageBuffer: Buffer | ArrayBuffer, path: string): Promise<void>;
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wahaha216/koishi-plugin-jmcomic",
3
3
  "description": "下载JM本子,无需python。支持pdf、zip加密。",
4
- "version": "0.2.6",
4
+ "version": "0.2.8",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
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,22 @@ jm queue
17
18
 
18
19
  ## 更新日志
19
20
 
21
+ <details>
22
+ <summary>0.2.8</summary>
23
+
24
+ 1.解析时添加章节数日志
25
+
26
+ 2.替换最新的客户端域名
27
+
28
+ </details>
29
+
30
+ <details>
31
+ <summary>0.2.7</summary>
32
+
33
+ 支持 JM 文库
34
+
35
+ </details>
36
+
20
37
  <details>
21
38
  <summary>0.2.6</summary>
22
39
 
@@ -35,6 +52,7 @@ jm queue
35
52
  <summary>0.2.4</summary>
36
53
 
37
54
  1. 调整 debug 日志
55
+
38
56
  2. 修正文化名返回值
39
57
 
40
58
  </details>
@@ -43,6 +61,7 @@ jm queue
43
61
  <summary>0.2.3</summary>
44
62
 
45
63
  1. 将手动路径拼接替换为 join
64
+
46
65
  2. 健壮文件名序列化,尝试修复部分文件名导致无法创建文件
47
66
 
48
67
  </details>
@@ -58,6 +77,7 @@ jm queue
58
77
  <summary>0.2.1</summary>
59
78
 
60
79
  1. 搜索分页限制
80
+
61
81
  2. 搜索结果分割空行
62
82
 
63
83
  </details>
@@ -66,6 +86,7 @@ jm queue
66
86
  <summary>0.2.0</summary>
67
87
 
68
88
  1. 简易搜索功能
89
+
69
90
  2. 修复队列丢失 i18n key 的问题
70
91
 
71
92
  </details>
@@ -74,6 +95,7 @@ jm queue
74
95
  <summary>0.1.1</summary>
75
96
 
76
97
  1. 添加队列时返回队列信息
98
+
77
99
  2. 提取代码
78
100
 
79
101
  </details>
@@ -82,43 +104,59 @@ jm queue
82
104
  <summary>0.1.0</summary>
83
105
 
84
106
  1. 队列系统
107
+
85
108
  2. 下载并发与解密并发限制
109
+
86
110
  3. 修改配置页面顺序、分类
111
+
87
112
  4. 不再直接暴露变量,改为逐级传递
113
+
88
114
  5. 统一暴露 Error 类
115
+
89
116
  6. 添加域名切换条件
90
117
 
91
118
  </details>
92
119
 
93
120
  <details>
94
121
  <summary>0.0.6</summary>
122
+
95
123
  添加了一些错误提示
124
+
96
125
  </details>
97
126
 
98
127
  <details>
99
128
  <summary>0.0.5</summary>
129
+
100
130
  修改使用示例
131
+
101
132
  </details>
102
133
 
103
134
  <details>
104
135
  <summary>0.0.4</summary>
105
136
 
106
137
  1. 文件名移除前后空格
138
+
107
139
  2. 新增文件发送配置,用于配置文件是以 buffer 读取后发送还是以本地地址的形式发送。docker 中使用 file 形式需要在 bot 实现端同时映射/koishi 目录
108
140
 
109
141
  </details>
110
142
 
111
143
  <details>
112
144
  <summary>0.0.3</summary>
145
+
113
146
  忘了给自动删除做判断
147
+
114
148
  </details>
115
149
 
116
150
  <details>
117
151
  <summary>0.0.2</summary>
152
+
118
153
  依赖从peerDependencies移动到dependencies
154
+
119
155
  </details>
120
156
 
121
157
  <details>
122
158
  <summary>0.0.1</summary>
159
+
123
160
  初版
161
+
124
162
  </details>