@perk-net/perk-pushplus-sdk 1.2.0 → 1.2.1
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/README.md +46 -4
- package/dist/index.cjs +211 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -14
- package/dist/index.d.ts +124 -14
- package/dist/index.global.js +211 -5
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +211 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/base.ts +27 -1
- package/src/api/doc-api.ts +24 -0
- package/src/api/excel-api.ts +29 -0
- package/src/api/form-api.ts +3 -0
- package/src/api/friend-api.ts +38 -1
- package/src/api/open-base.ts +40 -0
- package/src/api/topic-user-api.ts +40 -1
- package/src/config.ts +1 -1
- package/src/index.ts +3 -0
- package/src/models.ts +46 -13
- package/src/multipart.ts +62 -0
package/dist/index.global.js
CHANGED
|
@@ -376,6 +376,26 @@ var PerkPushPlus = (function (exports) {
|
|
|
376
376
|
}
|
|
377
377
|
return parseApiResponse(resp);
|
|
378
378
|
}
|
|
379
|
+
/**
|
|
380
|
+
* 执行带二进制请求体的请求并返回原始 ApiResponse(不进行 code 校验)。
|
|
381
|
+
* 用于 multipart 上传等场景。
|
|
382
|
+
*/
|
|
383
|
+
async executeRaw(method, path, headers, body) {
|
|
384
|
+
const url = this.resolveUrl(path);
|
|
385
|
+
const resp = await callExecuteRaw(this.http, {
|
|
386
|
+
method,
|
|
387
|
+
url,
|
|
388
|
+
headers: headers != null ? headers : void 0,
|
|
389
|
+
body
|
|
390
|
+
});
|
|
391
|
+
if (!isSuccessfulHttpStatus(resp.statusCode)) {
|
|
392
|
+
throw new PushPlusError(
|
|
393
|
+
`PushPlus \u63A5\u53E3 HTTP \u8C03\u7528\u5931\u8D25: status=${resp.statusCode}, body=${resp.body}`,
|
|
394
|
+
resp.statusCode
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return parseApiResponse(resp);
|
|
398
|
+
}
|
|
379
399
|
/** 执行请求并直接返回 data;非 200 抛出异常。 */
|
|
380
400
|
async executeForData(method, path, headers, body) {
|
|
381
401
|
var _a;
|
|
@@ -506,6 +526,39 @@ var PerkPushPlus = (function (exports) {
|
|
|
506
526
|
(_b = resp.code) != null ? _b : -1
|
|
507
527
|
);
|
|
508
528
|
}
|
|
529
|
+
/** 以 multipart 上传文件(自动携带 access-key;code=401 时刷新后重试一次)。 */
|
|
530
|
+
executeOpenMultipart(path, multipart) {
|
|
531
|
+
return this.executeOpenRaw("POST", path, multipart.body, {
|
|
532
|
+
"Content-Type": multipart.contentType
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* 执行带二进制 body 的开放接口请求;当返回 code=401 时自动刷新 key 并重试一次。
|
|
537
|
+
*/
|
|
538
|
+
async executeOpenRaw(method, path, body, extraHeaders) {
|
|
539
|
+
var _a, _b;
|
|
540
|
+
const headers = { ...await this.headersWithAccessKey(), ...extraHeaders != null ? extraHeaders : {} };
|
|
541
|
+
const resp = await this.executeRaw(method, path, headers, body);
|
|
542
|
+
if (isApiSuccess(resp)) {
|
|
543
|
+
return resp.data;
|
|
544
|
+
}
|
|
545
|
+
if (resp.code === _OpenAbstractApi.CODE_ACCESS_KEY_INVALID) {
|
|
546
|
+
this.accessKeyManager.invalidate();
|
|
547
|
+
const retryHeaders = { ...await this.headersWithAccessKey(), ...extraHeaders != null ? extraHeaders : {} };
|
|
548
|
+
const retry = await this.executeRaw(method, path, retryHeaders, body);
|
|
549
|
+
if (isApiSuccess(retry)) {
|
|
550
|
+
return retry.data;
|
|
551
|
+
}
|
|
552
|
+
throw new PushPlusError(
|
|
553
|
+
`PushPlus \u5F00\u653E\u63A5\u53E3\u4E1A\u52A1\u5931\u8D25(\u91CD\u8BD5\u540E): code=${retry.code}, msg=${retry.msg}`,
|
|
554
|
+
(_a = retry.code) != null ? _a : -1
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
throw new PushPlusError(
|
|
558
|
+
`PushPlus \u5F00\u653E\u63A5\u53E3\u4E1A\u52A1\u5931\u8D25: code=${resp.code}, msg=${resp.msg}`,
|
|
559
|
+
(_b = resp.code) != null ? _b : -1
|
|
560
|
+
);
|
|
561
|
+
}
|
|
509
562
|
};
|
|
510
563
|
_OpenAbstractApi.HEADER_ACCESS_KEY = "access-key";
|
|
511
564
|
/** PushPlus AccessKey 失效相关的业务码(用于触发自动重试)。 */
|
|
@@ -568,6 +621,50 @@ var PerkPushPlus = (function (exports) {
|
|
|
568
621
|
}
|
|
569
622
|
};
|
|
570
623
|
|
|
624
|
+
// src/multipart.ts
|
|
625
|
+
function buildFileMultipart(fileName, contentType, fileBytes) {
|
|
626
|
+
if (fileBytes == null || fileBytes.byteLength === 0) {
|
|
627
|
+
throw new PushPlusError("\u4E0A\u4F20\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
|
|
628
|
+
}
|
|
629
|
+
const safeName = fileName && fileName.trim() ? fileName : "file";
|
|
630
|
+
const mime = contentType && contentType.trim() ? contentType : "application/octet-stream";
|
|
631
|
+
const boundary = "----PushPlusBoundary" + randomBoundarySuffix();
|
|
632
|
+
const crlf = "\r\n";
|
|
633
|
+
const enc = new TextEncoder();
|
|
634
|
+
const head = enc.encode(
|
|
635
|
+
`--${boundary}${crlf}Content-Disposition: form-data; name="file"; filename="${escapeFileName(safeName)}"${crlf}Content-Type: ${mime}${crlf}${crlf}`
|
|
636
|
+
);
|
|
637
|
+
const tail = enc.encode(`${crlf}--${boundary}--${crlf}`);
|
|
638
|
+
const body = new Uint8Array(head.byteLength + fileBytes.byteLength + tail.byteLength);
|
|
639
|
+
body.set(head, 0);
|
|
640
|
+
body.set(fileBytes, head.byteLength);
|
|
641
|
+
body.set(tail, head.byteLength + fileBytes.byteLength);
|
|
642
|
+
return { contentType: `multipart/form-data; boundary=${boundary}`, body };
|
|
643
|
+
}
|
|
644
|
+
async function toFileBytes(file) {
|
|
645
|
+
if (file instanceof Uint8Array) {
|
|
646
|
+
return file;
|
|
647
|
+
}
|
|
648
|
+
if (file instanceof ArrayBuffer) {
|
|
649
|
+
return new Uint8Array(file);
|
|
650
|
+
}
|
|
651
|
+
if (typeof Blob !== "undefined" && file instanceof Blob) {
|
|
652
|
+
const ab = await file.arrayBuffer();
|
|
653
|
+
return new Uint8Array(ab);
|
|
654
|
+
}
|
|
655
|
+
throw new PushPlusError(`\u4E0D\u652F\u6301\u7684\u4E0A\u4F20\u6587\u4EF6\u7C7B\u578B: ${Object.prototype.toString.call(file)}`);
|
|
656
|
+
}
|
|
657
|
+
function escapeFileName(name) {
|
|
658
|
+
return name.replace(/"/g, "_").replace(/\r/g, " ").replace(/\n/g, " ");
|
|
659
|
+
}
|
|
660
|
+
function randomBoundarySuffix() {
|
|
661
|
+
let s = "";
|
|
662
|
+
for (let i = 0; i < 32; i++) {
|
|
663
|
+
s += Math.floor(Math.random() * 16).toString(16);
|
|
664
|
+
}
|
|
665
|
+
return s;
|
|
666
|
+
}
|
|
667
|
+
|
|
571
668
|
// src/api/doc-api.ts
|
|
572
669
|
var DocApi = class extends OpenAbstractApi {
|
|
573
670
|
constructor(config, http, mgr) {
|
|
@@ -585,6 +682,19 @@ var PerkPushPlus = (function (exports) {
|
|
|
585
682
|
create(title) {
|
|
586
683
|
return this.executeOpen("POST", "/push/api/open/doc/create", { title });
|
|
587
684
|
}
|
|
685
|
+
/**
|
|
686
|
+
* 导入 Word(.docx)创建文档。
|
|
687
|
+
*
|
|
688
|
+
* 标题默认取文件名;创建后默认关闭分享,需再调用 publish 才会同步到分享页。
|
|
689
|
+
*/
|
|
690
|
+
async importWord(file, fileName = "document.docx") {
|
|
691
|
+
const bytes = await toFileBytes(file);
|
|
692
|
+
const name = fileName && fileName.trim() ? fileName : "document.docx";
|
|
693
|
+
return this.executeOpenMultipart(
|
|
694
|
+
"/push/api/open/doc/import",
|
|
695
|
+
buildFileMultipart(name, guessDocxContentType(name), bytes)
|
|
696
|
+
);
|
|
697
|
+
}
|
|
588
698
|
/** 获取文档元信息与 HTML 草稿正文。 */
|
|
589
699
|
content(docCode) {
|
|
590
700
|
return this.executeOpen(
|
|
@@ -626,6 +736,9 @@ var PerkPushPlus = (function (exports) {
|
|
|
626
736
|
return this.executeOpen("POST", "/push/api/open/doc/updateShare", body);
|
|
627
737
|
}
|
|
628
738
|
};
|
|
739
|
+
function guessDocxContentType(name) {
|
|
740
|
+
return name.toLowerCase().endsWith(".docx") ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/octet-stream";
|
|
741
|
+
}
|
|
629
742
|
|
|
630
743
|
// src/api/excel-api.ts
|
|
631
744
|
var ExcelApi = class extends OpenAbstractApi {
|
|
@@ -644,6 +757,19 @@ var PerkPushPlus = (function (exports) {
|
|
|
644
757
|
create(title) {
|
|
645
758
|
return this.executeOpen("POST", "/push/api/open/excel/create", { title });
|
|
646
759
|
}
|
|
760
|
+
/**
|
|
761
|
+
* 导入 Excel(.xlsx / .xls)创建表格。
|
|
762
|
+
*
|
|
763
|
+
* 标题默认取文件名;创建后默认关闭分享,需再调用 publish 才会同步到分享页。
|
|
764
|
+
*/
|
|
765
|
+
async importExcel(file, fileName = "workbook.xlsx") {
|
|
766
|
+
const bytes = await toFileBytes(file);
|
|
767
|
+
const name = fileName && fileName.trim() ? fileName : "workbook.xlsx";
|
|
768
|
+
return this.executeOpenMultipart(
|
|
769
|
+
"/push/api/open/excel/import",
|
|
770
|
+
buildFileMultipart(name, guessExcelContentType(name), bytes)
|
|
771
|
+
);
|
|
772
|
+
}
|
|
647
773
|
/** 获取表格元信息与整表 JSON 草稿。 */
|
|
648
774
|
content(docCode) {
|
|
649
775
|
return this.executeOpen(
|
|
@@ -714,6 +840,16 @@ var PerkPushPlus = (function (exports) {
|
|
|
714
840
|
throw new PushPlusError(`\u5E8F\u5217\u5316\u8868\u683C\u5185\u5BB9\u5931\u8D25: ${e.message}`, -1, { cause: e });
|
|
715
841
|
}
|
|
716
842
|
}
|
|
843
|
+
function guessExcelContentType(name) {
|
|
844
|
+
const lower = name.toLowerCase();
|
|
845
|
+
if (lower.endsWith(".xlsx")) {
|
|
846
|
+
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
847
|
+
}
|
|
848
|
+
if (lower.endsWith(".xls")) {
|
|
849
|
+
return "application/vnd.ms-excel";
|
|
850
|
+
}
|
|
851
|
+
return "application/octet-stream";
|
|
852
|
+
}
|
|
717
853
|
|
|
718
854
|
// src/api/form-api.ts
|
|
719
855
|
var FormApi = class extends OpenAbstractApi {
|
|
@@ -809,6 +945,40 @@ var PerkPushPlus = (function (exports) {
|
|
|
809
945
|
async editRemark(id, remark) {
|
|
810
946
|
await this.executeOpen("POST", "/api/open/friend/editRemark", { id, remark });
|
|
811
947
|
}
|
|
948
|
+
/**
|
|
949
|
+
* 5. 将好友加入黑名单。
|
|
950
|
+
*
|
|
951
|
+
* 加入后将解除双方好友关系,对方无法再添加你。不能将自己加入黑名单,仅可将已有好友加入黑名单。
|
|
952
|
+
*
|
|
953
|
+
* @param friendId 好友 id(好友列表中的 friendId 字段)
|
|
954
|
+
*/
|
|
955
|
+
async addBlacklist(friendId) {
|
|
956
|
+
await this.executeOpen(
|
|
957
|
+
"POST",
|
|
958
|
+
this.appendQuery("/api/open/friend/addBlacklist", { friendId })
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
/** 6. 好友黑名单列表。 */
|
|
962
|
+
blacklistList(query) {
|
|
963
|
+
return this.executeOpen(
|
|
964
|
+
"POST",
|
|
965
|
+
"/api/open/friend/blacklistList",
|
|
966
|
+
query != null ? query : {}
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* 7. 解除好友黑名单。
|
|
971
|
+
*
|
|
972
|
+
* 解除后不会自动恢复好友关系,需重新扫码添加。
|
|
973
|
+
*
|
|
974
|
+
* @param id 黑名单记录 ID(黑名单列表中的 id 字段)
|
|
975
|
+
*/
|
|
976
|
+
async removeBlacklist(id) {
|
|
977
|
+
await this.executeOpen(
|
|
978
|
+
"POST",
|
|
979
|
+
this.appendQuery("/api/open/friend/removeBlacklist", { id })
|
|
980
|
+
);
|
|
981
|
+
}
|
|
812
982
|
};
|
|
813
983
|
|
|
814
984
|
// src/api/image-api.ts
|
|
@@ -857,7 +1027,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
857
1027
|
}
|
|
858
1028
|
const fileName = options.fileName || "file";
|
|
859
1029
|
const contentType = options.contentType || guessContentTypeByName(fileName) || "application/octet-stream";
|
|
860
|
-
const boundary = "----PushPlusBoundary" +
|
|
1030
|
+
const boundary = "----PushPlusBoundary" + randomBoundarySuffix2();
|
|
861
1031
|
const body = buildMultipartBody(boundary, uploadToken, fileName, contentType, bytes);
|
|
862
1032
|
const resp = await callExecuteRaw(this.http, {
|
|
863
1033
|
method: "POST",
|
|
@@ -943,7 +1113,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
943
1113
|
const crlf = "\r\n";
|
|
944
1114
|
const enc = new TextEncoder();
|
|
945
1115
|
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="${
|
|
1116
|
+
`--${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
1117
|
);
|
|
948
1118
|
const tail = enc.encode(`${crlf}--${boundary}--${crlf}`);
|
|
949
1119
|
const out = new Uint8Array(head.byteLength + fileBytes.byteLength + tail.byteLength);
|
|
@@ -952,7 +1122,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
952
1122
|
out.set(tail, head.byteLength + fileBytes.byteLength);
|
|
953
1123
|
return out;
|
|
954
1124
|
}
|
|
955
|
-
function
|
|
1125
|
+
function escapeFileName2(name) {
|
|
956
1126
|
return name.replace(/"/g, "_").replace(/\r/g, " ").replace(/\n/g, " ");
|
|
957
1127
|
}
|
|
958
1128
|
function guessContentTypeByName(name) {
|
|
@@ -965,7 +1135,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
965
1135
|
if (lower.endsWith(".svg")) return "image/svg+xml";
|
|
966
1136
|
return null;
|
|
967
1137
|
}
|
|
968
|
-
function
|
|
1138
|
+
function randomBoundarySuffix2() {
|
|
969
1139
|
let s = "";
|
|
970
1140
|
for (let i = 0; i < 32; i++) {
|
|
971
1141
|
s += Math.floor(Math.random() * 16).toString(16);
|
|
@@ -1308,6 +1478,42 @@ var PerkPushPlus = (function (exports) {
|
|
|
1308
1478
|
async editRemark(id, remark) {
|
|
1309
1479
|
await this.executeOpen("POST", "/api/open/topicUser/editRemark", { id, remark });
|
|
1310
1480
|
}
|
|
1481
|
+
/**
|
|
1482
|
+
* 4. 将订阅人加入黑名单。
|
|
1483
|
+
*
|
|
1484
|
+
* 加入后将移出群组,对方无法再加入该群组。积分群组不支持黑名单。不能将自己加入黑名单。
|
|
1485
|
+
*
|
|
1486
|
+
* @param topicRelationId 用户编号(订阅人列表中的 id 字段)
|
|
1487
|
+
*/
|
|
1488
|
+
async addBlacklist(topicRelationId) {
|
|
1489
|
+
const path = this.appendQuery("/api/open/topicUser/addBlacklist", { topicRelationId });
|
|
1490
|
+
await this.executeOpen("POST", path);
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* 5. 订阅人黑名单列表。
|
|
1494
|
+
*
|
|
1495
|
+
* `query.params.topicId` 必填。
|
|
1496
|
+
*/
|
|
1497
|
+
blacklistList(query) {
|
|
1498
|
+
return this.executeOpen(
|
|
1499
|
+
"POST",
|
|
1500
|
+
"/api/open/topicUser/blacklistList",
|
|
1501
|
+
query
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* 6. 解除订阅人黑名单。
|
|
1506
|
+
*
|
|
1507
|
+
* 解除后不会自动恢复群组订阅,对方可重新加入该群组。
|
|
1508
|
+
*
|
|
1509
|
+
* @param id 黑名单记录 ID(黑名单列表中的 id 字段)
|
|
1510
|
+
*/
|
|
1511
|
+
async removeBlacklist(id) {
|
|
1512
|
+
await this.executeOpen(
|
|
1513
|
+
"POST",
|
|
1514
|
+
this.appendQuery("/api/open/topicUser/removeBlacklist", { id })
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1311
1517
|
};
|
|
1312
1518
|
|
|
1313
1519
|
// src/api/user-api.ts
|
|
@@ -1372,7 +1578,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
1372
1578
|
logRequest: (_g = cfg.logRequest) != null ? _g : false,
|
|
1373
1579
|
rateLimitGuardEnabled: (_h = cfg.rateLimitGuardEnabled) != null ? _h : true,
|
|
1374
1580
|
rateLimitCooldownMs: (_i = cfg.rateLimitCooldownMs) != null ? _i : 0,
|
|
1375
|
-
userAgent: (_j = cfg.userAgent) != null ? _j : `@perk-net/perk-pushplus-sdk/1.2.
|
|
1581
|
+
userAgent: (_j = cfg.userAgent) != null ? _j : `@perk-net/perk-pushplus-sdk/1.2.1`
|
|
1376
1582
|
};
|
|
1377
1583
|
}
|
|
1378
1584
|
|