@perk-net/perk-pushplus-sdk 1.2.0 → 1.2.2

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.
@@ -12,6 +12,7 @@ var PerkPushPlus = (function (exports) {
12
12
  Channel2["EXTENSION"] = "extension";
13
13
  Channel2["APP"] = "app";
14
14
  Channel2["CLAWBOT"] = "clawbot";
15
+ Channel2["QQ"] = "qq";
15
16
  return Channel2;
16
17
  })(Channel || {});
17
18
  var Template = /* @__PURE__ */ ((Template2) => {
@@ -376,6 +377,26 @@ var PerkPushPlus = (function (exports) {
376
377
  }
377
378
  return parseApiResponse(resp);
378
379
  }
380
+ /**
381
+ * 执行带二进制请求体的请求并返回原始 ApiResponse(不进行 code 校验)。
382
+ * 用于 multipart 上传等场景。
383
+ */
384
+ async executeRaw(method, path, headers, body) {
385
+ const url = this.resolveUrl(path);
386
+ const resp = await callExecuteRaw(this.http, {
387
+ method,
388
+ url,
389
+ headers: headers != null ? headers : void 0,
390
+ body
391
+ });
392
+ if (!isSuccessfulHttpStatus(resp.statusCode)) {
393
+ throw new PushPlusError(
394
+ `PushPlus \u63A5\u53E3 HTTP \u8C03\u7528\u5931\u8D25: status=${resp.statusCode}, body=${resp.body}`,
395
+ resp.statusCode
396
+ );
397
+ }
398
+ return parseApiResponse(resp);
399
+ }
379
400
  /** 执行请求并直接返回 data;非 200 抛出异常。 */
380
401
  async executeForData(method, path, headers, body) {
381
402
  var _a;
@@ -506,6 +527,39 @@ var PerkPushPlus = (function (exports) {
506
527
  (_b = resp.code) != null ? _b : -1
507
528
  );
508
529
  }
530
+ /** 以 multipart 上传文件(自动携带 access-key;code=401 时刷新后重试一次)。 */
531
+ executeOpenMultipart(path, multipart) {
532
+ return this.executeOpenRaw("POST", path, multipart.body, {
533
+ "Content-Type": multipart.contentType
534
+ });
535
+ }
536
+ /**
537
+ * 执行带二进制 body 的开放接口请求;当返回 code=401 时自动刷新 key 并重试一次。
538
+ */
539
+ async executeOpenRaw(method, path, body, extraHeaders) {
540
+ var _a, _b;
541
+ const headers = { ...await this.headersWithAccessKey(), ...extraHeaders != null ? extraHeaders : {} };
542
+ const resp = await this.executeRaw(method, path, headers, body);
543
+ if (isApiSuccess(resp)) {
544
+ return resp.data;
545
+ }
546
+ if (resp.code === _OpenAbstractApi.CODE_ACCESS_KEY_INVALID) {
547
+ this.accessKeyManager.invalidate();
548
+ const retryHeaders = { ...await this.headersWithAccessKey(), ...extraHeaders != null ? extraHeaders : {} };
549
+ const retry = await this.executeRaw(method, path, retryHeaders, body);
550
+ if (isApiSuccess(retry)) {
551
+ return retry.data;
552
+ }
553
+ throw new PushPlusError(
554
+ `PushPlus \u5F00\u653E\u63A5\u53E3\u4E1A\u52A1\u5931\u8D25(\u91CD\u8BD5\u540E): code=${retry.code}, msg=${retry.msg}`,
555
+ (_a = retry.code) != null ? _a : -1
556
+ );
557
+ }
558
+ throw new PushPlusError(
559
+ `PushPlus \u5F00\u653E\u63A5\u53E3\u4E1A\u52A1\u5931\u8D25: code=${resp.code}, msg=${resp.msg}`,
560
+ (_b = resp.code) != null ? _b : -1
561
+ );
562
+ }
509
563
  };
510
564
  _OpenAbstractApi.HEADER_ACCESS_KEY = "access-key";
511
565
  /** PushPlus AccessKey 失效相关的业务码(用于触发自动重试)。 */
@@ -568,6 +622,50 @@ var PerkPushPlus = (function (exports) {
568
622
  }
569
623
  };
570
624
 
625
+ // src/multipart.ts
626
+ function buildFileMultipart(fileName, contentType, fileBytes) {
627
+ if (fileBytes == null || fileBytes.byteLength === 0) {
628
+ throw new PushPlusError("\u4E0A\u4F20\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
629
+ }
630
+ const safeName = fileName && fileName.trim() ? fileName : "file";
631
+ const mime = contentType && contentType.trim() ? contentType : "application/octet-stream";
632
+ const boundary = "----PushPlusBoundary" + randomBoundarySuffix();
633
+ const crlf = "\r\n";
634
+ const enc = new TextEncoder();
635
+ const head = enc.encode(
636
+ `--${boundary}${crlf}Content-Disposition: form-data; name="file"; filename="${escapeFileName(safeName)}"${crlf}Content-Type: ${mime}${crlf}${crlf}`
637
+ );
638
+ const tail = enc.encode(`${crlf}--${boundary}--${crlf}`);
639
+ const body = new Uint8Array(head.byteLength + fileBytes.byteLength + tail.byteLength);
640
+ body.set(head, 0);
641
+ body.set(fileBytes, head.byteLength);
642
+ body.set(tail, head.byteLength + fileBytes.byteLength);
643
+ return { contentType: `multipart/form-data; boundary=${boundary}`, body };
644
+ }
645
+ async function toFileBytes(file) {
646
+ if (file instanceof Uint8Array) {
647
+ return file;
648
+ }
649
+ if (file instanceof ArrayBuffer) {
650
+ return new Uint8Array(file);
651
+ }
652
+ if (typeof Blob !== "undefined" && file instanceof Blob) {
653
+ const ab = await file.arrayBuffer();
654
+ return new Uint8Array(ab);
655
+ }
656
+ throw new PushPlusError(`\u4E0D\u652F\u6301\u7684\u4E0A\u4F20\u6587\u4EF6\u7C7B\u578B: ${Object.prototype.toString.call(file)}`);
657
+ }
658
+ function escapeFileName(name) {
659
+ return name.replace(/"/g, "_").replace(/\r/g, " ").replace(/\n/g, " ");
660
+ }
661
+ function randomBoundarySuffix() {
662
+ let s = "";
663
+ for (let i = 0; i < 32; i++) {
664
+ s += Math.floor(Math.random() * 16).toString(16);
665
+ }
666
+ return s;
667
+ }
668
+
571
669
  // src/api/doc-api.ts
572
670
  var DocApi = class extends OpenAbstractApi {
573
671
  constructor(config, http, mgr) {
@@ -585,6 +683,19 @@ var PerkPushPlus = (function (exports) {
585
683
  create(title) {
586
684
  return this.executeOpen("POST", "/push/api/open/doc/create", { title });
587
685
  }
686
+ /**
687
+ * 导入 Word(.docx)创建文档。
688
+ *
689
+ * 标题默认取文件名;创建后默认关闭分享,需再调用 publish 才会同步到分享页。
690
+ */
691
+ async importWord(file, fileName = "document.docx") {
692
+ const bytes = await toFileBytes(file);
693
+ const name = fileName && fileName.trim() ? fileName : "document.docx";
694
+ return this.executeOpenMultipart(
695
+ "/push/api/open/doc/import",
696
+ buildFileMultipart(name, guessDocxContentType(name), bytes)
697
+ );
698
+ }
588
699
  /** 获取文档元信息与 HTML 草稿正文。 */
589
700
  content(docCode) {
590
701
  return this.executeOpen(
@@ -626,6 +737,9 @@ var PerkPushPlus = (function (exports) {
626
737
  return this.executeOpen("POST", "/push/api/open/doc/updateShare", body);
627
738
  }
628
739
  };
740
+ function guessDocxContentType(name) {
741
+ return name.toLowerCase().endsWith(".docx") ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/octet-stream";
742
+ }
629
743
 
630
744
  // src/api/excel-api.ts
631
745
  var ExcelApi = class extends OpenAbstractApi {
@@ -644,6 +758,19 @@ var PerkPushPlus = (function (exports) {
644
758
  create(title) {
645
759
  return this.executeOpen("POST", "/push/api/open/excel/create", { title });
646
760
  }
761
+ /**
762
+ * 导入 Excel(.xlsx / .xls)创建表格。
763
+ *
764
+ * 标题默认取文件名;创建后默认关闭分享,需再调用 publish 才会同步到分享页。
765
+ */
766
+ async importExcel(file, fileName = "workbook.xlsx") {
767
+ const bytes = await toFileBytes(file);
768
+ const name = fileName && fileName.trim() ? fileName : "workbook.xlsx";
769
+ return this.executeOpenMultipart(
770
+ "/push/api/open/excel/import",
771
+ buildFileMultipart(name, guessExcelContentType(name), bytes)
772
+ );
773
+ }
647
774
  /** 获取表格元信息与整表 JSON 草稿。 */
648
775
  content(docCode) {
649
776
  return this.executeOpen(
@@ -714,6 +841,16 @@ var PerkPushPlus = (function (exports) {
714
841
  throw new PushPlusError(`\u5E8F\u5217\u5316\u8868\u683C\u5185\u5BB9\u5931\u8D25: ${e.message}`, -1, { cause: e });
715
842
  }
716
843
  }
844
+ function guessExcelContentType(name) {
845
+ const lower = name.toLowerCase();
846
+ if (lower.endsWith(".xlsx")) {
847
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
848
+ }
849
+ if (lower.endsWith(".xls")) {
850
+ return "application/vnd.ms-excel";
851
+ }
852
+ return "application/octet-stream";
853
+ }
717
854
 
718
855
  // src/api/form-api.ts
719
856
  var FormApi = class extends OpenAbstractApi {
@@ -809,6 +946,40 @@ var PerkPushPlus = (function (exports) {
809
946
  async editRemark(id, remark) {
810
947
  await this.executeOpen("POST", "/api/open/friend/editRemark", { id, remark });
811
948
  }
949
+ /**
950
+ * 5. 将好友加入黑名单。
951
+ *
952
+ * 加入后将解除双方好友关系,对方无法再添加你。不能将自己加入黑名单,仅可将已有好友加入黑名单。
953
+ *
954
+ * @param friendId 好友 id(好友列表中的 friendId 字段)
955
+ */
956
+ async addBlacklist(friendId) {
957
+ await this.executeOpen(
958
+ "POST",
959
+ this.appendQuery("/api/open/friend/addBlacklist", { friendId })
960
+ );
961
+ }
962
+ /** 6. 好友黑名单列表。 */
963
+ blacklistList(query) {
964
+ return this.executeOpen(
965
+ "POST",
966
+ "/api/open/friend/blacklistList",
967
+ query != null ? query : {}
968
+ );
969
+ }
970
+ /**
971
+ * 7. 解除好友黑名单。
972
+ *
973
+ * 解除后不会自动恢复好友关系,需重新扫码添加。
974
+ *
975
+ * @param id 黑名单记录 ID(黑名单列表中的 id 字段)
976
+ */
977
+ async removeBlacklist(id) {
978
+ await this.executeOpen(
979
+ "POST",
980
+ this.appendQuery("/api/open/friend/removeBlacklist", { id })
981
+ );
982
+ }
812
983
  };
813
984
 
814
985
  // src/api/image-api.ts
@@ -857,7 +1028,7 @@ var PerkPushPlus = (function (exports) {
857
1028
  }
858
1029
  const fileName = options.fileName || "file";
859
1030
  const contentType = options.contentType || guessContentTypeByName(fileName) || "application/octet-stream";
860
- const boundary = "----PushPlusBoundary" + randomBoundarySuffix();
1031
+ const boundary = "----PushPlusBoundary" + randomBoundarySuffix2();
861
1032
  const body = buildMultipartBody(boundary, uploadToken, fileName, contentType, bytes);
862
1033
  const resp = await callExecuteRaw(this.http, {
863
1034
  method: "POST",
@@ -943,7 +1114,7 @@ var PerkPushPlus = (function (exports) {
943
1114
  const crlf = "\r\n";
944
1115
  const enc = new TextEncoder();
945
1116
  const head = enc.encode(
946
- `--${boundary}${crlf}Content-Disposition: form-data; name="token"${crlf}${crlf}${uploadToken}${crlf}--${boundary}${crlf}Content-Disposition: form-data; name="file"; filename="${escapeFileName(fileName)}"${crlf}Content-Type: ${contentType}${crlf}${crlf}`
1117
+ `--${boundary}${crlf}Content-Disposition: form-data; name="token"${crlf}${crlf}${uploadToken}${crlf}--${boundary}${crlf}Content-Disposition: form-data; name="file"; filename="${escapeFileName2(fileName)}"${crlf}Content-Type: ${contentType}${crlf}${crlf}`
947
1118
  );
948
1119
  const tail = enc.encode(`${crlf}--${boundary}--${crlf}`);
949
1120
  const out = new Uint8Array(head.byteLength + fileBytes.byteLength + tail.byteLength);
@@ -952,7 +1123,7 @@ var PerkPushPlus = (function (exports) {
952
1123
  out.set(tail, head.byteLength + fileBytes.byteLength);
953
1124
  return out;
954
1125
  }
955
- function escapeFileName(name) {
1126
+ function escapeFileName2(name) {
956
1127
  return name.replace(/"/g, "_").replace(/\r/g, " ").replace(/\n/g, " ");
957
1128
  }
958
1129
  function guessContentTypeByName(name) {
@@ -965,7 +1136,7 @@ var PerkPushPlus = (function (exports) {
965
1136
  if (lower.endsWith(".svg")) return "image/svg+xml";
966
1137
  return null;
967
1138
  }
968
- function randomBoundarySuffix() {
1139
+ function randomBoundarySuffix2() {
969
1140
  let s = "";
970
1141
  for (let i = 0; i < 32; i++) {
971
1142
  s += Math.floor(Math.random() * 16).toString(16);
@@ -1157,6 +1328,55 @@ var PerkPushPlus = (function (exports) {
1157
1328
  }
1158
1329
  };
1159
1330
 
1331
+ // src/api/qqbot-api.ts
1332
+ var SEND_TYPE_QQ_GROUP = 2;
1333
+ var QqBotApi = class extends OpenAbstractApi {
1334
+ constructor(config, http, mgr) {
1335
+ super(config, http, mgr);
1336
+ }
1337
+ /** 1. 获取绑定链接与绑定码;refresh 为 true 时旧绑定码失效并重新生成。 */
1338
+ getBindLink(refresh = false) {
1339
+ const path = refresh ? this.appendQuery("/api/open/qqBot/getBindLink", { refresh: true }) : "/api/open/qqBot/getBindLink";
1340
+ return this.executeOpen("GET", path);
1341
+ }
1342
+ /** 2. 查询绑定状态。 */
1343
+ botInfo() {
1344
+ return this.executeOpen("GET", "/api/open/qqBot/botInfo");
1345
+ }
1346
+ /** 3. 解绑 QQ 机器人。 */
1347
+ async unbind() {
1348
+ await this.executeOpen("GET", "/api/open/qqBot/unbind");
1349
+ }
1350
+ /** 4. 获取机器人已加入的 QQ 群列表。 */
1351
+ async groupList() {
1352
+ var _a;
1353
+ return (_a = await this.executeOpen("GET", "/api/open/qqBot/groupList")) != null ? _a : [];
1354
+ }
1355
+ /** 5. 获取 QQ 机器人渠道配置列表。 */
1356
+ list(q) {
1357
+ return this.executeOpen("POST", "/api/open/qqBot/list", q != null ? q : {});
1358
+ }
1359
+ /** 6. 新增渠道配置,用于把消息发送到指定 QQ 群;发给自己无需创建配置。 */
1360
+ async add(req) {
1361
+ await this.executeOpen("POST", "/api/open/qqBot/add", withDefaultSendType(req));
1362
+ }
1363
+ /** 7. 修改渠道配置;配置编码不可修改。 */
1364
+ async edit(req) {
1365
+ await this.executeOpen("POST", "/api/open/qqBot/edit", withDefaultSendType(req));
1366
+ }
1367
+ /** 8. 删除渠道配置。 */
1368
+ async delete(id) {
1369
+ await this.executeOpen(
1370
+ "DELETE",
1371
+ this.appendQuery("/api/open/qqBot/delete", { id })
1372
+ );
1373
+ }
1374
+ };
1375
+ function withDefaultSendType(req) {
1376
+ var _a;
1377
+ return { ...req, sendType: (_a = req.sendType) != null ? _a : SEND_TYPE_QQ_GROUP };
1378
+ }
1379
+
1160
1380
  // src/api/setting-api.ts
1161
1381
  var SettingApi = class extends OpenAbstractApi {
1162
1382
  constructor(config, http, mgr) {
@@ -1308,6 +1528,42 @@ var PerkPushPlus = (function (exports) {
1308
1528
  async editRemark(id, remark) {
1309
1529
  await this.executeOpen("POST", "/api/open/topicUser/editRemark", { id, remark });
1310
1530
  }
1531
+ /**
1532
+ * 4. 将订阅人加入黑名单。
1533
+ *
1534
+ * 加入后将移出群组,对方无法再加入该群组。积分群组不支持黑名单。不能将自己加入黑名单。
1535
+ *
1536
+ * @param topicRelationId 用户编号(订阅人列表中的 id 字段)
1537
+ */
1538
+ async addBlacklist(topicRelationId) {
1539
+ const path = this.appendQuery("/api/open/topicUser/addBlacklist", { topicRelationId });
1540
+ await this.executeOpen("POST", path);
1541
+ }
1542
+ /**
1543
+ * 5. 订阅人黑名单列表。
1544
+ *
1545
+ * `query.params.topicId` 必填。
1546
+ */
1547
+ blacklistList(query) {
1548
+ return this.executeOpen(
1549
+ "POST",
1550
+ "/api/open/topicUser/blacklistList",
1551
+ query
1552
+ );
1553
+ }
1554
+ /**
1555
+ * 6. 解除订阅人黑名单。
1556
+ *
1557
+ * 解除后不会自动恢复群组订阅,对方可重新加入该群组。
1558
+ *
1559
+ * @param id 黑名单记录 ID(黑名单列表中的 id 字段)
1560
+ */
1561
+ async removeBlacklist(id) {
1562
+ await this.executeOpen(
1563
+ "POST",
1564
+ this.appendQuery("/api/open/topicUser/removeBlacklist", { id })
1565
+ );
1566
+ }
1311
1567
  };
1312
1568
 
1313
1569
  // src/api/user-api.ts
@@ -1372,7 +1628,7 @@ var PerkPushPlus = (function (exports) {
1372
1628
  logRequest: (_g = cfg.logRequest) != null ? _g : false,
1373
1629
  rateLimitGuardEnabled: (_h = cfg.rateLimitGuardEnabled) != null ? _h : true,
1374
1630
  rateLimitCooldownMs: (_i = cfg.rateLimitCooldownMs) != null ? _i : 0,
1375
- userAgent: (_j = cfg.userAgent) != null ? _j : `@perk-net/perk-pushplus-sdk/1.2.0`
1631
+ userAgent: (_j = cfg.userAgent) != null ? _j : `@perk-net/perk-pushplus-sdk/1.2.1`
1376
1632
  };
1377
1633
  }
1378
1634
 
@@ -1464,6 +1720,7 @@ var PerkPushPlus = (function (exports) {
1464
1720
  this.webhook = new WebhookApi(this.config, this.httpRequester, this.accessKeyManager);
1465
1721
  this.channel = new ChannelApi(this.config, this.httpRequester, this.accessKeyManager);
1466
1722
  this.clawBot = new ClawBotApi(this.config, this.httpRequester, this.accessKeyManager);
1723
+ this.qqBot = new QqBotApi(this.config, this.httpRequester, this.accessKeyManager);
1467
1724
  this.setting = new SettingApi(this.config, this.httpRequester, this.accessKeyManager);
1468
1725
  this.pre = new PreApi(this.config, this.httpRequester, this.accessKeyManager);
1469
1726
  this.image = new ImageApi(this.config, this.httpRequester, this.accessKeyManager);
@@ -1731,6 +1988,7 @@ var PerkPushPlus = (function (exports) {
1731
1988
  exports.PushPlusClientBuilder = PushPlusClientBuilder;
1732
1989
  exports.PushPlusError = PushPlusError;
1733
1990
  exports.PushPlusException = PushPlusException;
1991
+ exports.QqBotApi = QqBotApi;
1734
1992
  exports.RateLimitGuard = RateLimitGuard;
1735
1993
  exports.SendRequestBuilder = SendRequestBuilder;
1736
1994
  exports.SendStatus = SendStatus;