@wahaha216/koishi-plugin-jmcomic 0.2.7 → 0.2.9

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.
@@ -1,4 +1,3 @@
1
- import crypto from "crypto";
2
1
  import { IJMSearchResult, IJMUser } from "../types/JMClient";
3
2
  import { JMPhotoAbstract } from "./JMPhotoAbstract";
4
3
  import { JMAlbumAbstract } from "./JMAlbumAbstract";
@@ -84,10 +83,4 @@ export declare abstract class JMClientAbstract {
84
83
  * @param single 类型为本子时,该本子是否是单章节
85
84
  */
86
85
  abstract decodeByPhoto(photo: JMPhotoAbstract, type: "photo" | "album", albumId: string, single: boolean): Promise<void>;
87
- /**
88
- * 使用MD5将字符串加密成十六进制
89
- * @param key 要计算MD5的字符串
90
- * @returns 十六进制MD5
91
- */
92
- md5Hex(key: string, inputEncoding?: crypto.Encoding): string;
93
86
  }
@@ -8,10 +8,6 @@ import { JMPhotoAbstract } from "../abstract/JMPhotoAbstract";
8
8
  import { JMAppBlog } from "./JMAppBlog";
9
9
  import Puppeteer from "koishi-plugin-puppeteer";
10
10
  export declare class JMAppClient extends JMClientAbstract {
11
- static APP_VERSION: string;
12
- static APP_TOKEN_SECRET: string;
13
- static APP_TOKEN_SECRET_2: string;
14
- static APP_DATA_SECRET: string;
15
11
  /**
16
12
  * koishi 配置项
17
13
  */
@@ -67,12 +63,4 @@ export declare class JMAppClient extends JMClientAbstract {
67
63
  * @returns 请求时所需的token和tokenparam
68
64
  */
69
65
  private getTokenAndTokenParam;
70
- /**
71
- * 解密加密字符串
72
- * @param timestamp 请求时传递的时间戳
73
- * @param base64 待解密的字符串
74
- * @param secret
75
- * @returns 解密结果,JSON
76
- */
77
- private decodeBase64;
78
66
  }
@@ -0,0 +1,3 @@
1
+ export declare class AllDomainFailedError extends Error {
2
+ constructor(message?: string);
3
+ }
@@ -1,6 +1,7 @@
1
- import { AlbumNotExistError } from "../error/albumNotExist.error";
2
- import { PhotoNotExistError } from "../error/photoNotExist.error";
3
- import { MySqlError } from "../error/mysql.error";
4
- import { EmptyBufferError } from "../error/emptybuffer.error";
5
- import { OverRetryError } from "../error/overRetry.error";
6
- export { AlbumNotExistError, PhotoNotExistError, MySqlError, EmptyBufferError, OverRetryError, };
1
+ import { AlbumNotExistError } from "./albumNotExist.error";
2
+ import { PhotoNotExistError } from "./photoNotExist.error";
3
+ import { MySqlError } from "./mysql.error";
4
+ import { EmptyBufferError } from "./emptybuffer.error";
5
+ import { OverRetryError } from "./overRetry.error";
6
+ import { AllDomainFailedError } from "./allDomainFailed.error";
7
+ export { AlbumNotExistError, PhotoNotExistError, MySqlError, EmptyBufferError, OverRetryError, AllDomainFailedError, };
package/lib/index.js CHANGED
@@ -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",
@@ -78,10 +78,41 @@ var JM_IMAGE_URL_LIST = [
78
78
  "cdn-msp.jmapinodeudzn.net",
79
79
  "cdn-msp3.jmapinodeudzn.net"
80
80
  ];
81
+ var JM_API_URL_DOMAIN_SERVER_LIST = [
82
+ "https://rup4a04-c01.tos-ap-southeast-1.bytepluses.com/newsvr-2025.txt",
83
+ "https://rup4a04-c02.tos-cn-hongkong.bytepluses.com/newsvr-2025.txt"
84
+ ];
85
+ var JM_APP_VERSION = "1.7.9";
86
+ var JM_APP_TOKEN_SECRET = "18comicAPP";
87
+ var JM_APP_TOKEN_SECRET_2 = "18comicAPPContent";
88
+ var JM_APP_DATA_SECRET = "185Hcomic3PAPP7R";
89
+ var JM_API_DOMAIN_SERVER_SECRET = "diosfjckwpqpdfjkvnqQjsik";
81
90
 
82
91
  // src/utils/Utils.ts
83
92
  var import_path = require("path");
84
93
 
94
+ // src/error/albumNotExist.error.ts
95
+ var AlbumNotExistError = class extends Error {
96
+ static {
97
+ __name(this, "AlbumNotExistError");
98
+ }
99
+ constructor(message) {
100
+ super(message);
101
+ this.name = "AlbumNotExistError";
102
+ }
103
+ };
104
+
105
+ // src/error/photoNotExist.error.ts
106
+ var PhotoNotExistError = class extends Error {
107
+ static {
108
+ __name(this, "PhotoNotExistError");
109
+ }
110
+ constructor(message) {
111
+ super(message);
112
+ this.name = "PhotoNotExistError";
113
+ }
114
+ };
115
+
85
116
  // src/error/mysql.error.ts
86
117
  var MySqlError = class extends Error {
87
118
  static {
@@ -93,6 +124,17 @@ var MySqlError = class extends Error {
93
124
  }
94
125
  };
95
126
 
127
+ // src/error/emptybuffer.error.ts
128
+ var EmptyBufferError = class extends Error {
129
+ static {
130
+ __name(this, "EmptyBufferError");
131
+ }
132
+ constructor(message) {
133
+ super(message);
134
+ this.name = "EmptyBufferError";
135
+ }
136
+ };
137
+
96
138
  // src/error/overRetry.error.ts
97
139
  var OverRetryError = class extends Error {
98
140
  static {
@@ -104,18 +146,19 @@ var OverRetryError = class extends Error {
104
146
  }
105
147
  };
106
148
 
107
- // src/error/emptybuffer.error.ts
108
- var EmptyBufferError = class extends Error {
149
+ // src/error/allDomainFailed.error.ts
150
+ var AllDomainFailedError = class extends Error {
109
151
  static {
110
- __name(this, "EmptyBufferError");
152
+ __name(this, "AllDomainFailedError");
111
153
  }
112
154
  constructor(message) {
113
155
  super(message);
114
- this.name = "EmptyBufferError";
156
+ this.name = "AllDomainFailedError";
115
157
  }
116
158
  };
117
159
 
118
160
  // src/utils/Utils.ts
161
+ var import_crypto = require("crypto");
119
162
  function fileExistsAsync(path) {
120
163
  try {
121
164
  (0, import_fs.accessSync)(path, import_fs.constants.F_OK);
@@ -203,8 +246,9 @@ async function requestWithRetry(url, method, config = {}, http, pluginsConfig, l
203
246
  }
204
247
  }
205
248
  __name(requestWithRetry, "requestWithRetry");
206
- async function requestWithUrlSwitch(url, method, config = {}, http, pluginsConfig, logger, type = "CLIENT", urlIndex = 0) {
249
+ async function requestWithUrlSwitch(url, method, config = {}, http, pluginsConfig, logger, type = "CLIENT", urlIndex = 0, isUpdate = false) {
207
250
  const list = type === "CLIENT" ? JM_CLIENT_URL_LIST : JM_IMAGE_URL_LIST;
251
+ console.log(list);
208
252
  const urlCount = list.length;
209
253
  const url_bak = url;
210
254
  if (url.startsWith("/")) {
@@ -225,12 +269,13 @@ async function requestWithUrlSwitch(url, method, config = {}, http, pluginsConfi
225
269
  }
226
270
  return res;
227
271
  } else {
228
- throw new Error("所有域名请求失败");
272
+ throw new AllDomainFailedError();
229
273
  }
230
274
  } catch (error) {
231
275
  const isMysqlError = error instanceof MySqlError;
232
276
  const isEmptyBuffer = error instanceof EmptyBufferError;
233
277
  const isOverRetryError = error instanceof OverRetryError;
278
+ const isAllDomainFailedError = error instanceof AllDomainFailedError;
234
279
  if (isMysqlError || isEmptyBuffer || isOverRetryError) {
235
280
  logger.info(`请求失败,尝试切换域名... ${urlIndex + 1}/${urlCount}`);
236
281
  return await requestWithUrlSwitch(
@@ -241,7 +286,23 @@ async function requestWithUrlSwitch(url, method, config = {}, http, pluginsConfi
241
286
  pluginsConfig,
242
287
  logger,
243
288
  type,
244
- urlIndex + 1
289
+ urlIndex + 1,
290
+ isUpdate
291
+ );
292
+ } else if (isAllDomainFailedError && !isUpdate) {
293
+ logger.info(`请求失败,尝试更新域名...`);
294
+ const res = await updateApiDomain(http, pluginsConfig, logger);
295
+ if (!res) throw new AllDomainFailedError();
296
+ return await requestWithUrlSwitch(
297
+ url_bak,
298
+ method,
299
+ config,
300
+ http,
301
+ pluginsConfig,
302
+ logger,
303
+ type,
304
+ 0,
305
+ true
245
306
  );
246
307
  }
247
308
  throw new Error(error);
@@ -278,13 +339,55 @@ function formatFileName(originName, name2, id, index) {
278
339
  return originName.replaceAll("{{name}}", name2).replaceAll("{{id}}", id).replaceAll("{{index}}", index ? `${index}` : "").trim();
279
340
  }
280
341
  __name(formatFileName, "formatFileName");
342
+ function md5Hex(key, inputEncoding = "utf-8") {
343
+ return (0, import_crypto.createHash)("md5").update(key, inputEncoding).digest("hex");
344
+ }
345
+ __name(md5Hex, "md5Hex");
346
+ function decodeBase64(base64, timestamp, secret = JM_APP_DATA_SECRET) {
347
+ const dataB64 = Buffer.from(base64, "base64");
348
+ const md5 = md5Hex(`${timestamp}${secret}`);
349
+ const key = Buffer.from(md5);
350
+ const decipher = (0, import_crypto.createDecipheriv)("aes-256-ecb", key, null);
351
+ let dataAES = decipher.update(dataB64);
352
+ let decrypted = Buffer.concat([dataAES, decipher.final()]);
353
+ const decodedString = decrypted.toString("utf-8");
354
+ return JSON.parse(decodedString);
355
+ }
356
+ __name(decodeBase64, "decodeBase64");
357
+ async function updateApiDomain(http, config, logger) {
358
+ for (const url of JM_API_URL_DOMAIN_SERVER_LIST) {
359
+ try {
360
+ const encodeText = await requestWithRetry(
361
+ url,
362
+ "GET",
363
+ { responseType: "text" },
364
+ http,
365
+ config,
366
+ logger
367
+ );
368
+ const domainJson = decodeBase64(
369
+ encodeText,
370
+ "",
371
+ JM_API_DOMAIN_SERVER_SECRET
372
+ );
373
+ JM_CLIENT_URL_LIST.splice(0, JM_CLIENT_URL_LIST.length);
374
+ JM_CLIENT_URL_LIST.push(...domainJson.Server);
375
+ console.log(domainJson);
376
+ console.log(JM_CLIENT_URL_LIST);
377
+ return true;
378
+ } catch (error) {
379
+ console.log(error);
380
+ }
381
+ }
382
+ return false;
383
+ }
384
+ __name(updateApiDomain, "updateApiDomain");
281
385
 
282
386
  // src/entity/JMAppClient.ts
283
387
  var import_crypto3 = require("crypto");
284
388
  var import_form_data = __toESM(require("form-data"));
285
389
 
286
390
  // src/abstract/JMClientAbstract.ts
287
- var import_crypto = __toESM(require("crypto"));
288
391
  var JMClientAbstract = class {
289
392
  static {
290
393
  __name(this, "JMClientAbstract");
@@ -299,14 +402,6 @@ var JMClientAbstract = class {
299
402
  getRoot() {
300
403
  return this.root;
301
404
  }
302
- /**
303
- * 使用MD5将字符串加密成十六进制
304
- * @param key 要计算MD5的字符串
305
- * @returns 十六进制MD5
306
- */
307
- md5Hex(key, inputEncoding = "utf-8") {
308
- return import_crypto.default.createHash("md5").update(key, inputEncoding).digest("hex");
309
- }
310
405
  };
311
406
 
312
407
  // src/abstract/JMPhotoAbstract.ts
@@ -751,28 +846,6 @@ var import_path3 = require("path");
751
846
  var import_sharp2 = __toESM(require("sharp"));
752
847
  var import_muhammara = require("muhammara");
753
848
 
754
- // src/error/albumNotExist.error.ts
755
- var AlbumNotExistError = class extends Error {
756
- static {
757
- __name(this, "AlbumNotExistError");
758
- }
759
- constructor(message) {
760
- super(message);
761
- this.name = "AlbumNotExistError";
762
- }
763
- };
764
-
765
- // src/error/photoNotExist.error.ts
766
- var PhotoNotExistError = class extends Error {
767
- static {
768
- __name(this, "PhotoNotExistError");
769
- }
770
- constructor(message) {
771
- super(message);
772
- this.name = "PhotoNotExistError";
773
- }
774
- };
775
-
776
849
  // src/abstract/JMBlogAbstract.ts
777
850
  var JMBlogAbstract = class {
778
851
  static {
@@ -832,14 +905,10 @@ var JMAppBlog = class _JMAppBlog extends JMBlogAbstract {
832
905
  };
833
906
 
834
907
  // src/entity/JMAppClient.ts
835
- var JMAppClient = class _JMAppClient extends JMClientAbstract {
908
+ var JMAppClient = class extends JMClientAbstract {
836
909
  static {
837
910
  __name(this, "JMAppClient");
838
911
  }
839
- static APP_VERSION = "1.7.9";
840
- static APP_TOKEN_SECRET = "18comicAPP";
841
- static APP_TOKEN_SECRET_2 = "18comicAPPContent";
842
- static APP_DATA_SECRET = "185Hcomic3PAPP7R";
843
912
  /**
844
913
  * koishi 配置项
845
914
  */
@@ -883,7 +952,7 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
883
952
  formData,
884
953
  { headers, responseType: "json" }
885
954
  );
886
- return this.decodeBase64(res.data, timestamp);
955
+ return decodeBase64(res.data, timestamp);
887
956
  }
888
957
  async search(keyword) {
889
958
  if (this.config.debug) this.logger.info(`搜索本子: ${keyword}`);
@@ -901,7 +970,7 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
901
970
  this.config,
902
971
  this.logger
903
972
  );
904
- return this.decodeBase64(searchRes.data, timestamp);
973
+ return decodeBase64(searchRes.data, timestamp);
905
974
  }
906
975
  async getAlbumById(id) {
907
976
  if (this.config.debug) this.logger.info(`获取本子(${id})信息`);
@@ -919,7 +988,7 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
919
988
  this.config,
920
989
  this.logger
921
990
  );
922
- const album_json = this.decodeBase64(res.data, timestamp);
991
+ const album_json = decodeBase64(res.data, timestamp);
923
992
  if (!album_json.name) throw new AlbumNotExistError();
924
993
  const album = JMAppAlbum.fromJson(album_json);
925
994
  const series = album.getSeries();
@@ -933,6 +1002,8 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
933
1002
  const photo = await this.getPhotoById(id);
934
1003
  photos.push(photo);
935
1004
  }
1005
+ if (this.config.debug)
1006
+ this.logger.info(`本子 ${id} 章节数:${photos.length}`);
936
1007
  album.setPhotos(photos);
937
1008
  return album;
938
1009
  }
@@ -952,7 +1023,7 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
952
1023
  this.config,
953
1024
  this.logger
954
1025
  );
955
- const photo_json = this.decodeBase64(res.data, timestamp);
1026
+ const photo_json = decodeBase64(res.data, timestamp);
956
1027
  if (!photo_json.name) throw new PhotoNotExistError();
957
1028
  const photo = JMAppPhoto.fromJson(photo_json);
958
1029
  const images = photo.getImages();
@@ -976,7 +1047,7 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
976
1047
  this.config,
977
1048
  this.logger
978
1049
  );
979
- const blog_json = this.decodeBase64(res.data, timestamp);
1050
+ const blog_json = decodeBase64(res.data, timestamp);
980
1051
  if (!blog_json?.info?.title) throw new AlbumNotExistError();
981
1052
  return JMAppBlog.fromJson(blog_json);
982
1053
  }
@@ -1323,7 +1394,7 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
1323
1394
  const timestamp = this.getTimeStamp();
1324
1395
  const { token, tokenparam } = this.getTokenAndTokenParam(
1325
1396
  timestamp,
1326
- _JMAppClient.APP_TOKEN_SECRET_2
1397
+ JM_APP_TOKEN_SECRET_2
1327
1398
  );
1328
1399
  const html = await requestWithUrlSwitch(
1329
1400
  "/chapter_view_template",
@@ -1342,29 +1413,12 @@ var JMAppClient = class _JMAppClient extends JMClientAbstract {
1342
1413
  * @param secret 密钥
1343
1414
  * @returns 请求时所需的token和tokenparam
1344
1415
  */
1345
- getTokenAndTokenParam(timestamp, secret = _JMAppClient.APP_TOKEN_SECRET, version = _JMAppClient.APP_VERSION) {
1416
+ getTokenAndTokenParam(timestamp, secret = JM_APP_TOKEN_SECRET, version = JM_APP_VERSION) {
1346
1417
  const key = `${timestamp}${secret}`;
1347
1418
  const token = (0, import_crypto3.createHash)("md5").update(key).digest("hex");
1348
1419
  const tokenparam = `${timestamp},${version}`;
1349
1420
  return { token, tokenparam };
1350
1421
  }
1351
- /**
1352
- * 解密加密字符串
1353
- * @param timestamp 请求时传递的时间戳
1354
- * @param base64 待解密的字符串
1355
- * @param secret
1356
- * @returns 解密结果,JSON
1357
- */
1358
- decodeBase64(base64, timestamp, secret = _JMAppClient.APP_DATA_SECRET) {
1359
- const dataB64 = Buffer.from(base64, "base64");
1360
- const md5 = this.md5Hex(`${timestamp}${secret}`);
1361
- const key = Buffer.from(md5);
1362
- const decipher = (0, import_crypto3.createDecipheriv)("aes-256-ecb", key, null);
1363
- let dataAES = decipher.update(dataB64);
1364
- let decrypted = Buffer.concat([dataAES, decipher.final()]);
1365
- const decodedString = decrypted.toString("utf-8");
1366
- return JSON.parse(decodedString);
1367
- }
1368
1422
  };
1369
1423
 
1370
1424
  // src/processors/jmProcessor.ts
@@ -1,3 +1,9 @@
1
1
  export declare const buildHtmlContent: (base64Images: string[]) => string;
2
2
  export declare const JM_CLIENT_URL_LIST: string[];
3
3
  export declare const JM_IMAGE_URL_LIST: string[];
4
+ export declare const JM_API_URL_DOMAIN_SERVER_LIST: string[];
5
+ export declare const JM_APP_VERSION = "1.7.9";
6
+ export declare const JM_APP_TOKEN_SECRET = "18comicAPP";
7
+ export declare const JM_APP_TOKEN_SECRET_2 = "18comicAPPContent";
8
+ export declare const JM_APP_DATA_SECRET = "185Hcomic3PAPP7R";
9
+ export declare const JM_API_DOMAIN_SERVER_SECRET = "diosfjckwpqpdfjkvnqQjsik";
@@ -1,6 +1,7 @@
1
1
  import { Config } from "..";
2
2
  import { HTTP, Logger } from "koishi";
3
3
  import { IJMResponse } from "../types/JMClient";
4
+ import { Encoding } from "crypto";
4
5
  /**
5
6
  * 文件是否存在
6
7
  * @param path 文件路径
@@ -48,7 +49,7 @@ export declare function requestWithRetry<T = IJMResponse>(url: string, method: "
48
49
  * @param urlIndex 地址下标,默认从0开始尝试
49
50
  * @returns 请求结果
50
51
  */
51
- export declare function requestWithUrlSwitch<T = IJMResponse>(url: string, method: "GET" | "POST", config: HTTP.RequestConfig, http: HTTP, pluginsConfig: Config, logger: Logger, type?: "IMAGE" | "CLIENT", urlIndex?: number): Promise<T>;
52
+ export declare function requestWithUrlSwitch<T = IJMResponse>(url: string, method: "GET" | "POST", config: HTTP.RequestConfig, http: HTTP, pluginsConfig: Config, logger: Logger, type?: "IMAGE" | "CLIENT", urlIndex?: number, isUpdate?: boolean): Promise<T>;
52
53
  /**
53
54
  * 获取文件名和扩展名
54
55
  * @param filePath 文件路径
@@ -66,3 +67,13 @@ export declare function getFileInfo(filePath: string): {
66
67
  */
67
68
  export declare function deleteFewDaysAgoFolders(path: string, days: number): Promise<void>;
68
69
  export declare function formatFileName(originName: string, name: string, id: string, index?: number): string;
70
+ export declare function md5Hex(key: string, inputEncoding?: Encoding): string;
71
+ /**
72
+ * 解密加密字符串
73
+ * @param timestamp 请求时传递的时间戳
74
+ * @param base64 待解密的字符串
75
+ * @param secret
76
+ * @returns 解密结果,JSON
77
+ */
78
+ export declare function decodeBase64<T = Record<string, unknown>>(base64: string, timestamp: number | string, secret?: string): T;
79
+ export declare function updateApiDomain(http: HTTP, config: Config, logger: Logger): Promise<boolean>;
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.7",
4
+ "version": "0.2.9",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -18,6 +18,22 @@ jm queue
18
18
 
19
19
  ## 更新日志
20
20
 
21
+ <details>
22
+ <summary>0.2.9</summary>
23
+
24
+ 添加所有域名请求失败后,自动更新域名功能
25
+
26
+ </details>
27
+
28
+ <details>
29
+ <summary>0.2.8</summary>
30
+
31
+ 1.解析时添加章节数日志
32
+
33
+ 2.替换最新的客户端域名
34
+
35
+ </details>
36
+
21
37
  <details>
22
38
  <summary>0.2.7</summary>
23
39