@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.
- package/README.md +60 -5
- package/dist/index.cjs +263 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +220 -15
- package/dist/index.d.ts +220 -15
- package/dist/index.global.js +263 -5
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +263 -6
- 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/qqbot-api.ts +75 -0
- package/src/api/topic-user-api.ts +40 -1
- package/src/client.ts +3 -0
- package/src/config.ts +1 -1
- package/src/enums.ts +2 -0
- package/src/index.ts +10 -0
- package/src/models.ts +123 -13
- package/src/multipart.ts +62 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- **同时支持 Node.js 与浏览器**:Node.js 18+ 使用内置 `fetch`,浏览器使用原生 `fetch`,无运行时依赖。
|
|
6
6
|
- **三种产物**:CommonJS (`.cjs`) + ESModule (`.js`) + 浏览器 IIFE (`.global.js`),可通过 npm / `<script>` 直接加载。
|
|
7
7
|
- **完整 TypeScript 类型**:所有请求 / 响应 / 枚举 / 回调全部带类型声明。
|
|
8
|
-
- **全部开放接口**:用户、消息、消息 token、群组、群组用户、好友、webhook、渠道、ClawBot
|
|
8
|
+
- **全部开放接口**:用户、消息、消息 token、群组、群组用户、好友、webhook、渠道、ClawBot、QQ 机器人、功能设置、预处理、图片服务(含一键上传到 PushPlus 图床)、push 表单、push 文档、push 表格。
|
|
9
9
|
- **AccessKey 自动管理**:缓存 + 过期前自动刷新;`code=401` 自动刷新并重试一次。
|
|
10
10
|
- **本地限流守卫**:命中 `code=900` 后按 token 短路同 token 后续发送,避免被服务端长期封禁。
|
|
11
11
|
- **Builder 链式 API**:与 Java/Python SDK 风格保持一致。
|
|
@@ -90,7 +90,7 @@ await client.send({
|
|
|
90
90
|
template: Template.MARKDOWN,
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
// push
|
|
93
|
+
// push 表单 / 文档 / 表格:template 为 form/doc/excel 时需传 pushId(对应编码)
|
|
94
94
|
await client.send(
|
|
95
95
|
sendRequest()
|
|
96
96
|
.title('表单通知')
|
|
@@ -99,6 +99,22 @@ await client.send(
|
|
|
99
99
|
.pushId('表单编码')
|
|
100
100
|
.build(),
|
|
101
101
|
);
|
|
102
|
+
await client.send(
|
|
103
|
+
sendRequest()
|
|
104
|
+
.title('本周工作同步')
|
|
105
|
+
.content('请查收')
|
|
106
|
+
.template(Template.DOC)
|
|
107
|
+
.pushId('文档编码')
|
|
108
|
+
.build(),
|
|
109
|
+
);
|
|
110
|
+
await client.send(
|
|
111
|
+
sendRequest()
|
|
112
|
+
.title('销售日报')
|
|
113
|
+
.content('请查收')
|
|
114
|
+
.template(Template.EXCEL)
|
|
115
|
+
.pushId('表格编码')
|
|
116
|
+
.build(),
|
|
117
|
+
);
|
|
102
118
|
```
|
|
103
119
|
|
|
104
120
|
### 3. 多渠道发送
|
|
@@ -151,10 +167,18 @@ const qr = await client.topic.qrCode(123, 86400, -1);
|
|
|
151
167
|
|
|
152
168
|
// 群组用户
|
|
153
169
|
await client.topicUser.editRemark(456, '老张');
|
|
170
|
+
await client.topicUser.addBlacklist(10);
|
|
171
|
+
const topicBlacklist = await client.topicUser.blacklistList({
|
|
172
|
+
current: 1,
|
|
173
|
+
pageSize: 20,
|
|
174
|
+
params: { topicId: 1 },
|
|
175
|
+
});
|
|
154
176
|
|
|
155
177
|
// 好友
|
|
156
178
|
const myQr = await client.friend.getQrCode({ content: 'welcome' });
|
|
157
179
|
const friends = await client.friend.list({ current: 1, pageSize: 20 });
|
|
180
|
+
await client.friend.addBlacklist(friends.list[0].friendId);
|
|
181
|
+
const friendBlacklist = await client.friend.blacklistList({ current: 1, pageSize: 20 });
|
|
158
182
|
|
|
159
183
|
// webhook 渠道
|
|
160
184
|
import { WebhookType } from '@perk-net/perk-pushplus-sdk';
|
|
@@ -171,6 +195,19 @@ const mps = await client.channel.mpList();
|
|
|
171
195
|
// ClawBot
|
|
172
196
|
const botQr = await client.clawBot.getBotQrcode();
|
|
173
197
|
|
|
198
|
+
// QQ 机器人:绑定 -> 认领群 -> 建配置 -> 发到群
|
|
199
|
+
const link = await client.qqBot.getBindLink(); // link.url 生成二维码,或私聊发送 link.bindCode
|
|
200
|
+
const bind = await client.qqBot.botInfo(); // bind.isBind === 1 表示已绑定
|
|
201
|
+
const qqGroups = await client.qqBot.groupList();
|
|
202
|
+
await client.qqBot.add({ qqName: '运维告警群', qqCode: 'ops-group', qqGroupId: qqGroups[0].id });
|
|
203
|
+
await client.send({
|
|
204
|
+
title: '服务告警',
|
|
205
|
+
content: '订单服务响应超时',
|
|
206
|
+
channel: Channel.QQ,
|
|
207
|
+
option: 'ops-group', // 不传 option 则发给自己
|
|
208
|
+
template: Template.TXT,
|
|
209
|
+
});
|
|
210
|
+
|
|
174
211
|
// 设置
|
|
175
212
|
await client.setting.changeIsSend(1); // 启用发送
|
|
176
213
|
await client.setting.changeOpenMessageType(0);
|
|
@@ -195,20 +232,38 @@ await client.form.save({
|
|
|
195
232
|
});
|
|
196
233
|
const published = await client.form.publish(form.id!);
|
|
197
234
|
console.log(published.fillUrl);
|
|
235
|
+
await client.send({
|
|
236
|
+
title: published.title,
|
|
237
|
+
content: '请花1分钟完成填写',
|
|
238
|
+
template: Template.FORM,
|
|
239
|
+
pushId: published.formCode,
|
|
240
|
+
});
|
|
198
241
|
|
|
199
242
|
// push 文档
|
|
200
|
-
|
|
201
|
-
await client.doc.
|
|
243
|
+
import { readFile } from 'node:fs/promises';
|
|
244
|
+
const doc = await client.doc.importWord(await readFile('本周工作同步.docx'), '本周工作同步.docx');
|
|
202
245
|
await client.doc.updateShare(doc.docCode!, 1, 0);
|
|
203
246
|
await client.doc.publish(doc.docCode!);
|
|
247
|
+
await client.send({
|
|
248
|
+
title: doc.title,
|
|
249
|
+
content: '请查收',
|
|
250
|
+
template: Template.DOC,
|
|
251
|
+
pushId: doc.docCode,
|
|
252
|
+
});
|
|
204
253
|
|
|
205
254
|
// push 表格
|
|
206
|
-
const sheet = await client.excel.
|
|
255
|
+
const sheet = await client.excel.importExcel(await readFile('销售日报.xlsx'), '销售日报.xlsx');
|
|
207
256
|
await client.excel.writeCells(sheet.docCode!, 'A1', [
|
|
208
257
|
['日期', '销售额'],
|
|
209
258
|
['2026-08-13', 12800],
|
|
210
259
|
], 'Sheet1');
|
|
211
260
|
await client.excel.publish(sheet.docCode!);
|
|
261
|
+
await client.send({
|
|
262
|
+
title: sheet.title,
|
|
263
|
+
content: '请查收',
|
|
264
|
+
template: Template.EXCEL,
|
|
265
|
+
pushId: sheet.docCode,
|
|
266
|
+
});
|
|
212
267
|
```
|
|
213
268
|
|
|
214
269
|
### 图片服务
|
package/dist/index.cjs
CHANGED
|
@@ -11,6 +11,7 @@ var Channel = /* @__PURE__ */ ((Channel2) => {
|
|
|
11
11
|
Channel2["EXTENSION"] = "extension";
|
|
12
12
|
Channel2["APP"] = "app";
|
|
13
13
|
Channel2["CLAWBOT"] = "clawbot";
|
|
14
|
+
Channel2["QQ"] = "qq";
|
|
14
15
|
return Channel2;
|
|
15
16
|
})(Channel || {});
|
|
16
17
|
var Template = /* @__PURE__ */ ((Template2) => {
|
|
@@ -375,6 +376,26 @@ var AbstractApi = class {
|
|
|
375
376
|
}
|
|
376
377
|
return parseApiResponse(resp);
|
|
377
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
|
+
}
|
|
378
399
|
/** 执行请求并直接返回 data;非 200 抛出异常。 */
|
|
379
400
|
async executeForData(method, path, headers, body) {
|
|
380
401
|
var _a;
|
|
@@ -505,6 +526,39 @@ var _OpenAbstractApi = class _OpenAbstractApi extends AbstractApi {
|
|
|
505
526
|
(_b = resp.code) != null ? _b : -1
|
|
506
527
|
);
|
|
507
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
|
+
}
|
|
508
562
|
};
|
|
509
563
|
_OpenAbstractApi.HEADER_ACCESS_KEY = "access-key";
|
|
510
564
|
/** PushPlus AccessKey 失效相关的业务码(用于触发自动重试)。 */
|
|
@@ -567,6 +621,50 @@ var ClawBotApi = class extends OpenAbstractApi {
|
|
|
567
621
|
}
|
|
568
622
|
};
|
|
569
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
|
+
|
|
570
668
|
// src/api/doc-api.ts
|
|
571
669
|
var DocApi = class extends OpenAbstractApi {
|
|
572
670
|
constructor(config, http, mgr) {
|
|
@@ -584,6 +682,19 @@ var DocApi = class extends OpenAbstractApi {
|
|
|
584
682
|
create(title) {
|
|
585
683
|
return this.executeOpen("POST", "/push/api/open/doc/create", { title });
|
|
586
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
|
+
}
|
|
587
698
|
/** 获取文档元信息与 HTML 草稿正文。 */
|
|
588
699
|
content(docCode) {
|
|
589
700
|
return this.executeOpen(
|
|
@@ -625,6 +736,9 @@ var DocApi = class extends OpenAbstractApi {
|
|
|
625
736
|
return this.executeOpen("POST", "/push/api/open/doc/updateShare", body);
|
|
626
737
|
}
|
|
627
738
|
};
|
|
739
|
+
function guessDocxContentType(name) {
|
|
740
|
+
return name.toLowerCase().endsWith(".docx") ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/octet-stream";
|
|
741
|
+
}
|
|
628
742
|
|
|
629
743
|
// src/api/excel-api.ts
|
|
630
744
|
var ExcelApi = class extends OpenAbstractApi {
|
|
@@ -643,6 +757,19 @@ var ExcelApi = class extends OpenAbstractApi {
|
|
|
643
757
|
create(title) {
|
|
644
758
|
return this.executeOpen("POST", "/push/api/open/excel/create", { title });
|
|
645
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
|
+
}
|
|
646
773
|
/** 获取表格元信息与整表 JSON 草稿。 */
|
|
647
774
|
content(docCode) {
|
|
648
775
|
return this.executeOpen(
|
|
@@ -713,6 +840,16 @@ function stringifyJsonContent(content) {
|
|
|
713
840
|
throw new PushPlusError(`\u5E8F\u5217\u5316\u8868\u683C\u5185\u5BB9\u5931\u8D25: ${e.message}`, -1, { cause: e });
|
|
714
841
|
}
|
|
715
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
|
+
}
|
|
716
853
|
|
|
717
854
|
// src/api/form-api.ts
|
|
718
855
|
var FormApi = class extends OpenAbstractApi {
|
|
@@ -808,6 +945,40 @@ var FriendApi = class extends OpenAbstractApi {
|
|
|
808
945
|
async editRemark(id, remark) {
|
|
809
946
|
await this.executeOpen("POST", "/api/open/friend/editRemark", { id, remark });
|
|
810
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
|
+
}
|
|
811
982
|
};
|
|
812
983
|
|
|
813
984
|
// src/api/image-api.ts
|
|
@@ -856,7 +1027,7 @@ var ImageApi = class extends OpenAbstractApi {
|
|
|
856
1027
|
}
|
|
857
1028
|
const fileName = options.fileName || "file";
|
|
858
1029
|
const contentType = options.contentType || guessContentTypeByName(fileName) || "application/octet-stream";
|
|
859
|
-
const boundary = "----PushPlusBoundary" +
|
|
1030
|
+
const boundary = "----PushPlusBoundary" + randomBoundarySuffix2();
|
|
860
1031
|
const body = buildMultipartBody(boundary, uploadToken, fileName, contentType, bytes);
|
|
861
1032
|
const resp = await callExecuteRaw(this.http, {
|
|
862
1033
|
method: "POST",
|
|
@@ -942,7 +1113,7 @@ function buildMultipartBody(boundary, uploadToken, fileName, contentType, fileBy
|
|
|
942
1113
|
const crlf = "\r\n";
|
|
943
1114
|
const enc = new TextEncoder();
|
|
944
1115
|
const head = enc.encode(
|
|
945
|
-
`--${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}`
|
|
946
1117
|
);
|
|
947
1118
|
const tail = enc.encode(`${crlf}--${boundary}--${crlf}`);
|
|
948
1119
|
const out = new Uint8Array(head.byteLength + fileBytes.byteLength + tail.byteLength);
|
|
@@ -951,7 +1122,7 @@ function buildMultipartBody(boundary, uploadToken, fileName, contentType, fileBy
|
|
|
951
1122
|
out.set(tail, head.byteLength + fileBytes.byteLength);
|
|
952
1123
|
return out;
|
|
953
1124
|
}
|
|
954
|
-
function
|
|
1125
|
+
function escapeFileName2(name) {
|
|
955
1126
|
return name.replace(/"/g, "_").replace(/\r/g, " ").replace(/\n/g, " ");
|
|
956
1127
|
}
|
|
957
1128
|
function guessContentTypeByName(name) {
|
|
@@ -964,7 +1135,7 @@ function guessContentTypeByName(name) {
|
|
|
964
1135
|
if (lower.endsWith(".svg")) return "image/svg+xml";
|
|
965
1136
|
return null;
|
|
966
1137
|
}
|
|
967
|
-
function
|
|
1138
|
+
function randomBoundarySuffix2() {
|
|
968
1139
|
let s = "";
|
|
969
1140
|
for (let i = 0; i < 32; i++) {
|
|
970
1141
|
s += Math.floor(Math.random() * 16).toString(16);
|
|
@@ -1156,6 +1327,55 @@ var PreApi = class extends OpenAbstractApi {
|
|
|
1156
1327
|
}
|
|
1157
1328
|
};
|
|
1158
1329
|
|
|
1330
|
+
// src/api/qqbot-api.ts
|
|
1331
|
+
var SEND_TYPE_QQ_GROUP = 2;
|
|
1332
|
+
var QqBotApi = class extends OpenAbstractApi {
|
|
1333
|
+
constructor(config, http, mgr) {
|
|
1334
|
+
super(config, http, mgr);
|
|
1335
|
+
}
|
|
1336
|
+
/** 1. 获取绑定链接与绑定码;refresh 为 true 时旧绑定码失效并重新生成。 */
|
|
1337
|
+
getBindLink(refresh = false) {
|
|
1338
|
+
const path = refresh ? this.appendQuery("/api/open/qqBot/getBindLink", { refresh: true }) : "/api/open/qqBot/getBindLink";
|
|
1339
|
+
return this.executeOpen("GET", path);
|
|
1340
|
+
}
|
|
1341
|
+
/** 2. 查询绑定状态。 */
|
|
1342
|
+
botInfo() {
|
|
1343
|
+
return this.executeOpen("GET", "/api/open/qqBot/botInfo");
|
|
1344
|
+
}
|
|
1345
|
+
/** 3. 解绑 QQ 机器人。 */
|
|
1346
|
+
async unbind() {
|
|
1347
|
+
await this.executeOpen("GET", "/api/open/qqBot/unbind");
|
|
1348
|
+
}
|
|
1349
|
+
/** 4. 获取机器人已加入的 QQ 群列表。 */
|
|
1350
|
+
async groupList() {
|
|
1351
|
+
var _a;
|
|
1352
|
+
return (_a = await this.executeOpen("GET", "/api/open/qqBot/groupList")) != null ? _a : [];
|
|
1353
|
+
}
|
|
1354
|
+
/** 5. 获取 QQ 机器人渠道配置列表。 */
|
|
1355
|
+
list(q) {
|
|
1356
|
+
return this.executeOpen("POST", "/api/open/qqBot/list", q != null ? q : {});
|
|
1357
|
+
}
|
|
1358
|
+
/** 6. 新增渠道配置,用于把消息发送到指定 QQ 群;发给自己无需创建配置。 */
|
|
1359
|
+
async add(req) {
|
|
1360
|
+
await this.executeOpen("POST", "/api/open/qqBot/add", withDefaultSendType(req));
|
|
1361
|
+
}
|
|
1362
|
+
/** 7. 修改渠道配置;配置编码不可修改。 */
|
|
1363
|
+
async edit(req) {
|
|
1364
|
+
await this.executeOpen("POST", "/api/open/qqBot/edit", withDefaultSendType(req));
|
|
1365
|
+
}
|
|
1366
|
+
/** 8. 删除渠道配置。 */
|
|
1367
|
+
async delete(id) {
|
|
1368
|
+
await this.executeOpen(
|
|
1369
|
+
"DELETE",
|
|
1370
|
+
this.appendQuery("/api/open/qqBot/delete", { id })
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
function withDefaultSendType(req) {
|
|
1375
|
+
var _a;
|
|
1376
|
+
return { ...req, sendType: (_a = req.sendType) != null ? _a : SEND_TYPE_QQ_GROUP };
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1159
1379
|
// src/api/setting-api.ts
|
|
1160
1380
|
var SettingApi = class extends OpenAbstractApi {
|
|
1161
1381
|
constructor(config, http, mgr) {
|
|
@@ -1307,6 +1527,42 @@ var TopicUserApi = class extends OpenAbstractApi {
|
|
|
1307
1527
|
async editRemark(id, remark) {
|
|
1308
1528
|
await this.executeOpen("POST", "/api/open/topicUser/editRemark", { id, remark });
|
|
1309
1529
|
}
|
|
1530
|
+
/**
|
|
1531
|
+
* 4. 将订阅人加入黑名单。
|
|
1532
|
+
*
|
|
1533
|
+
* 加入后将移出群组,对方无法再加入该群组。积分群组不支持黑名单。不能将自己加入黑名单。
|
|
1534
|
+
*
|
|
1535
|
+
* @param topicRelationId 用户编号(订阅人列表中的 id 字段)
|
|
1536
|
+
*/
|
|
1537
|
+
async addBlacklist(topicRelationId) {
|
|
1538
|
+
const path = this.appendQuery("/api/open/topicUser/addBlacklist", { topicRelationId });
|
|
1539
|
+
await this.executeOpen("POST", path);
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* 5. 订阅人黑名单列表。
|
|
1543
|
+
*
|
|
1544
|
+
* `query.params.topicId` 必填。
|
|
1545
|
+
*/
|
|
1546
|
+
blacklistList(query) {
|
|
1547
|
+
return this.executeOpen(
|
|
1548
|
+
"POST",
|
|
1549
|
+
"/api/open/topicUser/blacklistList",
|
|
1550
|
+
query
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* 6. 解除订阅人黑名单。
|
|
1555
|
+
*
|
|
1556
|
+
* 解除后不会自动恢复群组订阅,对方可重新加入该群组。
|
|
1557
|
+
*
|
|
1558
|
+
* @param id 黑名单记录 ID(黑名单列表中的 id 字段)
|
|
1559
|
+
*/
|
|
1560
|
+
async removeBlacklist(id) {
|
|
1561
|
+
await this.executeOpen(
|
|
1562
|
+
"POST",
|
|
1563
|
+
this.appendQuery("/api/open/topicUser/removeBlacklist", { id })
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1310
1566
|
};
|
|
1311
1567
|
|
|
1312
1568
|
// src/api/user-api.ts
|
|
@@ -1371,7 +1627,7 @@ function resolveConfig(input) {
|
|
|
1371
1627
|
logRequest: (_g = cfg.logRequest) != null ? _g : false,
|
|
1372
1628
|
rateLimitGuardEnabled: (_h = cfg.rateLimitGuardEnabled) != null ? _h : true,
|
|
1373
1629
|
rateLimitCooldownMs: (_i = cfg.rateLimitCooldownMs) != null ? _i : 0,
|
|
1374
|
-
userAgent: (_j = cfg.userAgent) != null ? _j : `@perk-net/perk-pushplus-sdk/1.2.
|
|
1630
|
+
userAgent: (_j = cfg.userAgent) != null ? _j : `@perk-net/perk-pushplus-sdk/1.2.1`
|
|
1375
1631
|
};
|
|
1376
1632
|
}
|
|
1377
1633
|
|
|
@@ -1463,6 +1719,7 @@ var PushPlusClient = class _PushPlusClient {
|
|
|
1463
1719
|
this.webhook = new WebhookApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1464
1720
|
this.channel = new ChannelApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1465
1721
|
this.clawBot = new ClawBotApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1722
|
+
this.qqBot = new QqBotApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1466
1723
|
this.setting = new SettingApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1467
1724
|
this.pre = new PreApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1468
1725
|
this.image = new ImageApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
@@ -1730,6 +1987,7 @@ exports.PushPlusClient = PushPlusClient;
|
|
|
1730
1987
|
exports.PushPlusClientBuilder = PushPlusClientBuilder;
|
|
1731
1988
|
exports.PushPlusError = PushPlusError;
|
|
1732
1989
|
exports.PushPlusException = PushPlusException;
|
|
1990
|
+
exports.QqBotApi = QqBotApi;
|
|
1733
1991
|
exports.RateLimitGuard = RateLimitGuard;
|
|
1734
1992
|
exports.SendRequestBuilder = SendRequestBuilder;
|
|
1735
1993
|
exports.SendStatus = SendStatus;
|