@perk-net/perk-pushplus-sdk 1.1.1 → 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 +71 -2
- package/dist/index.cjs +454 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +406 -1
- package/dist/index.d.ts +406 -1
- package/dist/index.global.js +454 -5
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +448 -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 +101 -0
- package/src/api/excel-api.ts +143 -0
- package/src/api/form-api.ts +95 -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/client.ts +9 -0
- package/src/config.ts +1 -1
- package/src/enums.ts +37 -0
- package/src/index.ts +27 -0
- package/src/models.ts +215 -0
- package/src/multipart.ts +62 -0
package/package.json
CHANGED
package/src/api/base.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ResolvedPushPlusConfig } from '../config';
|
|
2
2
|
import { PushPlusError } from '../exception';
|
|
3
|
-
import { HttpRequester, HttpResponse, isSuccessfulHttpStatus } from '../http';
|
|
3
|
+
import { HttpRequester, HttpResponse, callExecuteRaw, isSuccessfulHttpStatus } from '../http';
|
|
4
4
|
import { ApiResponse } from '../models';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -62,6 +62,32 @@ export abstract class AbstractApi {
|
|
|
62
62
|
return parseApiResponse<T>(resp);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* 执行带二进制请求体的请求并返回原始 ApiResponse(不进行 code 校验)。
|
|
67
|
+
* 用于 multipart 上传等场景。
|
|
68
|
+
*/
|
|
69
|
+
protected async executeRaw<T>(
|
|
70
|
+
method: string,
|
|
71
|
+
path: string,
|
|
72
|
+
headers: Record<string, string> | undefined | null,
|
|
73
|
+
body: Uint8Array,
|
|
74
|
+
): Promise<ApiResponse<T>> {
|
|
75
|
+
const url = this.resolveUrl(path);
|
|
76
|
+
const resp = await callExecuteRaw(this.http, {
|
|
77
|
+
method,
|
|
78
|
+
url,
|
|
79
|
+
headers: headers ?? undefined,
|
|
80
|
+
body,
|
|
81
|
+
});
|
|
82
|
+
if (!isSuccessfulHttpStatus(resp.statusCode)) {
|
|
83
|
+
throw new PushPlusError(
|
|
84
|
+
`PushPlus 接口 HTTP 调用失败: status=${resp.statusCode}, body=${resp.body}`,
|
|
85
|
+
resp.statusCode,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return parseApiResponse<T>(resp);
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
/** 执行请求并直接返回 data;非 200 抛出异常。 */
|
|
66
92
|
protected async executeForData<T>(
|
|
67
93
|
method: string,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { AccessKeyManager } from '../access-key-manager';
|
|
2
|
+
import { ResolvedPushPlusConfig } from '../config';
|
|
3
|
+
import { HttpRequester } from '../http';
|
|
4
|
+
import { FileInput, buildFileMultipart, toFileBytes } from '../multipart';
|
|
5
|
+
import { DocContent, DocListItem, DocListQuery, DocVo, PageResult } from '../models';
|
|
6
|
+
import { OpenAbstractApi } from './open-base';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 开放接口 - push 文档。
|
|
10
|
+
*
|
|
11
|
+
* 文档:https://www.pushplus.plus/doc/ecosystem/doc/
|
|
12
|
+
* 基础路径:`/push/api/open/doc`
|
|
13
|
+
*
|
|
14
|
+
* 文档开放接口不单独提供推送接口。发布后请通过 `client.send` 推送分享页:
|
|
15
|
+
* `template=doc`,`pushId=docCode`。
|
|
16
|
+
*/
|
|
17
|
+
export class DocApi extends OpenAbstractApi {
|
|
18
|
+
constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
|
|
19
|
+
super(config, http, mgr);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 我的文档分页。 */
|
|
23
|
+
list(query?: DocListQuery): Promise<PageResult<DocListItem>> {
|
|
24
|
+
return this.executeOpen<PageResult<DocListItem>>(
|
|
25
|
+
'POST',
|
|
26
|
+
'/push/api/open/doc/list',
|
|
27
|
+
query ?? {},
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 创建空白文档。 */
|
|
32
|
+
create(title: string): Promise<DocVo> {
|
|
33
|
+
return this.executeOpen<DocVo>('POST', '/push/api/open/doc/create', { title });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 导入 Word(.docx)创建文档。
|
|
38
|
+
*
|
|
39
|
+
* 标题默认取文件名;创建后默认关闭分享,需再调用 publish 才会同步到分享页。
|
|
40
|
+
*/
|
|
41
|
+
async importWord(file: FileInput, fileName = 'document.docx'): Promise<DocVo> {
|
|
42
|
+
const bytes = await toFileBytes(file);
|
|
43
|
+
const name = fileName && fileName.trim() ? fileName : 'document.docx';
|
|
44
|
+
return this.executeOpenMultipart<DocVo>(
|
|
45
|
+
'/push/api/open/doc/import',
|
|
46
|
+
buildFileMultipart(name, guessDocxContentType(name), bytes),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 获取文档元信息与 HTML 草稿正文。 */
|
|
51
|
+
content(docCode: string): Promise<DocContent> {
|
|
52
|
+
return this.executeOpen<DocContent>(
|
|
53
|
+
'GET',
|
|
54
|
+
this.appendQuery('/push/api/open/doc/content', { docCode }),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 保存 HTML 草稿(不影响分享页,需再 publish)。 */
|
|
59
|
+
saveContent(docCode: string, content: string): Promise<DocVo> {
|
|
60
|
+
return this.executeOpen<DocVo>('POST', '/push/api/open/doc/saveContent', { docCode, content });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 将草稿同步为分享页快照。 */
|
|
64
|
+
publish(docCode: string): Promise<DocVo> {
|
|
65
|
+
return this.executeOpen<DocVo>(
|
|
66
|
+
'POST',
|
|
67
|
+
this.appendQuery('/push/api/open/doc/publish', { docCode }),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** 重命名。 */
|
|
72
|
+
async rename(docCode: string, title: string): Promise<void> {
|
|
73
|
+
await this.executeOpen<unknown>('POST', '/push/api/open/doc/rename', { docCode, title });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** 删除文档。 */
|
|
77
|
+
async delete(docCode: string): Promise<void> {
|
|
78
|
+
await this.executeOpen<unknown>(
|
|
79
|
+
'POST',
|
|
80
|
+
this.appendQuery('/push/api/open/doc/delete', { docCode }),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 更新分享设置。
|
|
86
|
+
*
|
|
87
|
+
* @param sharePerm 0 关闭 / 1 开启(仅可查看)
|
|
88
|
+
* @param shareLogin 0 免登录 / 1 需登录;不传则沿用原值
|
|
89
|
+
*/
|
|
90
|
+
updateShare(docCode: string, sharePerm: number, shareLogin?: number): Promise<DocVo> {
|
|
91
|
+
const body: Record<string, unknown> = { docCode, sharePerm };
|
|
92
|
+
if (shareLogin != null) body.shareLogin = shareLogin;
|
|
93
|
+
return this.executeOpen<DocVo>('POST', '/push/api/open/doc/updateShare', body);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function guessDocxContentType(name: string): string {
|
|
98
|
+
return name.toLowerCase().endsWith('.docx')
|
|
99
|
+
? 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
100
|
+
: 'application/octet-stream';
|
|
101
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { AccessKeyManager } from '../access-key-manager';
|
|
2
|
+
import { ResolvedPushPlusConfig } from '../config';
|
|
3
|
+
import { PushPlusError } from '../exception';
|
|
4
|
+
import { HttpRequester } from '../http';
|
|
5
|
+
import { FileInput, buildFileMultipart, toFileBytes } from '../multipart';
|
|
6
|
+
import { DocListItem, DocListQuery, ExcelContent, ExcelVo, PageResult } from '../models';
|
|
7
|
+
import { OpenAbstractApi } from './open-base';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 开放接口 - push 表格。
|
|
11
|
+
*
|
|
12
|
+
* 文档:https://www.pushplus.plus/doc/ecosystem/sheet/
|
|
13
|
+
* 基础路径:`/push/api/open/excel`
|
|
14
|
+
*
|
|
15
|
+
* 表格开放接口不单独提供推送接口。发布后请通过 `client.send` 推送分享页:
|
|
16
|
+
* `template=excel`,`pushId=docCode`。
|
|
17
|
+
*/
|
|
18
|
+
export class ExcelApi extends OpenAbstractApi {
|
|
19
|
+
constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
|
|
20
|
+
super(config, http, mgr);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** 我的表格分页。 */
|
|
24
|
+
list(query?: DocListQuery): Promise<PageResult<DocListItem>> {
|
|
25
|
+
return this.executeOpen<PageResult<DocListItem>>(
|
|
26
|
+
'POST',
|
|
27
|
+
'/push/api/open/excel/list',
|
|
28
|
+
query ?? {},
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 创建空白表格。 */
|
|
33
|
+
create(title: string): Promise<ExcelVo> {
|
|
34
|
+
return this.executeOpen<ExcelVo>('POST', '/push/api/open/excel/create', { title });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 导入 Excel(.xlsx / .xls)创建表格。
|
|
39
|
+
*
|
|
40
|
+
* 标题默认取文件名;创建后默认关闭分享,需再调用 publish 才会同步到分享页。
|
|
41
|
+
*/
|
|
42
|
+
async importExcel(file: FileInput, fileName = 'workbook.xlsx'): Promise<ExcelVo> {
|
|
43
|
+
const bytes = await toFileBytes(file);
|
|
44
|
+
const name = fileName && fileName.trim() ? fileName : 'workbook.xlsx';
|
|
45
|
+
return this.executeOpenMultipart<ExcelVo>(
|
|
46
|
+
'/push/api/open/excel/import',
|
|
47
|
+
buildFileMultipart(name, guessExcelContentType(name), bytes),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 获取表格元信息与整表 JSON 草稿。 */
|
|
52
|
+
content(docCode: string): Promise<ExcelContent> {
|
|
53
|
+
return this.executeOpen<ExcelContent>(
|
|
54
|
+
'GET',
|
|
55
|
+
this.appendQuery('/push/api/open/excel/content', { docCode }),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 整表覆盖保存草稿。
|
|
61
|
+
*
|
|
62
|
+
* `content` 可为 JSON 字符串,或工作簿对象(SDK 会序列化)。
|
|
63
|
+
*/
|
|
64
|
+
saveContent(docCode: string, content: string | object): Promise<ExcelVo> {
|
|
65
|
+
return this.executeOpen<ExcelVo>('POST', '/push/api/open/excel/saveContent', {
|
|
66
|
+
docCode,
|
|
67
|
+
content: stringifyJsonContent(content),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 从指定起始单元格起,按二维数组向右向下写入(草稿)。
|
|
73
|
+
*
|
|
74
|
+
* @param range 起始单元格,如 A1
|
|
75
|
+
* @param values 外层为行、内层为列
|
|
76
|
+
* @param sheetName 工作表名称;不传则写入活动表 / 第一张表
|
|
77
|
+
*/
|
|
78
|
+
writeCells(
|
|
79
|
+
docCode: string,
|
|
80
|
+
range: string,
|
|
81
|
+
values: unknown[][],
|
|
82
|
+
sheetName?: string,
|
|
83
|
+
): Promise<ExcelVo> {
|
|
84
|
+
const body: Record<string, unknown> = { docCode, range, values };
|
|
85
|
+
if (sheetName != null) body.sheetName = sheetName;
|
|
86
|
+
return this.executeOpen<ExcelVo>('POST', '/push/api/open/excel/writeCells', body);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 将草稿同步为分享页快照。 */
|
|
90
|
+
publish(docCode: string): Promise<ExcelVo> {
|
|
91
|
+
return this.executeOpen<ExcelVo>(
|
|
92
|
+
'POST',
|
|
93
|
+
this.appendQuery('/push/api/open/excel/publish', { docCode }),
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** 重命名。 */
|
|
98
|
+
async rename(docCode: string, title: string): Promise<void> {
|
|
99
|
+
await this.executeOpen<unknown>('POST', '/push/api/open/excel/rename', { docCode, title });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** 删除表格。 */
|
|
103
|
+
async delete(docCode: string): Promise<void> {
|
|
104
|
+
await this.executeOpen<unknown>(
|
|
105
|
+
'POST',
|
|
106
|
+
this.appendQuery('/push/api/open/excel/delete', { docCode }),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 更新分享设置。
|
|
112
|
+
*
|
|
113
|
+
* @param sharePerm 0 关闭 / 1 开启(仅可查看)
|
|
114
|
+
* @param shareLogin 0 免登录 / 1 需登录;不传则沿用原值
|
|
115
|
+
*/
|
|
116
|
+
updateShare(docCode: string, sharePerm: number, shareLogin?: number): Promise<ExcelVo> {
|
|
117
|
+
const body: Record<string, unknown> = { docCode, sharePerm };
|
|
118
|
+
if (shareLogin != null) body.shareLogin = shareLogin;
|
|
119
|
+
return this.executeOpen<ExcelVo>('POST', '/push/api/open/excel/updateShare', body);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function stringifyJsonContent(content: string | object): string {
|
|
124
|
+
if (typeof content === 'string') {
|
|
125
|
+
return content;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
return JSON.stringify(content);
|
|
129
|
+
} catch (e) {
|
|
130
|
+
throw new PushPlusError(`序列化表格内容失败: ${(e as Error).message}`, -1, { cause: e });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function guessExcelContentType(name: string): string {
|
|
135
|
+
const lower = name.toLowerCase();
|
|
136
|
+
if (lower.endsWith('.xlsx')) {
|
|
137
|
+
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
138
|
+
}
|
|
139
|
+
if (lower.endsWith('.xls')) {
|
|
140
|
+
return 'application/vnd.ms-excel';
|
|
141
|
+
}
|
|
142
|
+
return 'application/octet-stream';
|
|
143
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { AccessKeyManager } from '../access-key-manager';
|
|
2
|
+
import { ResolvedPushPlusConfig } from '../config';
|
|
3
|
+
import { HttpRequester } from '../http';
|
|
4
|
+
import {
|
|
5
|
+
FormDetail,
|
|
6
|
+
FormListItem,
|
|
7
|
+
FormListQuery,
|
|
8
|
+
FormPublishDiff,
|
|
9
|
+
FormPublishResult,
|
|
10
|
+
FormSaveRequest,
|
|
11
|
+
PageResult,
|
|
12
|
+
} from '../models';
|
|
13
|
+
import { OpenAbstractApi } from './open-base';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 开放接口 - push 表单。
|
|
17
|
+
*
|
|
18
|
+
* 文档:https://www.pushplus.plus/doc/ecosystem/form/
|
|
19
|
+
* 基础路径:`/push/api/open/form`
|
|
20
|
+
*
|
|
21
|
+
* 表单开放接口不单独提供推送接口。发布后请通过 `client.send` 推送填写页:
|
|
22
|
+
* `template=form`,`pushId=formCode`。
|
|
23
|
+
*/
|
|
24
|
+
export class FormApi extends OpenAbstractApi {
|
|
25
|
+
constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
|
|
26
|
+
super(config, http, mgr);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 我的表单分页。 */
|
|
30
|
+
list(query?: FormListQuery): Promise<PageResult<FormListItem>> {
|
|
31
|
+
return this.executeOpen<PageResult<FormListItem>>(
|
|
32
|
+
'POST',
|
|
33
|
+
'/push/api/open/form/list',
|
|
34
|
+
query ?? {},
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 创建空白表单(草稿)。 */
|
|
39
|
+
create(title: string): Promise<FormListItem> {
|
|
40
|
+
return this.executeOpen<FormListItem>('POST', '/push/api/open/form/create', { title });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 基于已有表单复制一份新草稿。 */
|
|
44
|
+
copy(id: number): Promise<FormListItem> {
|
|
45
|
+
return this.executeOpen<FormListItem>(
|
|
46
|
+
'POST',
|
|
47
|
+
this.appendQuery('/push/api/open/form/copy', { id }),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 保存表单设计(仅更新草稿;已发布需再调用 publish)。 */
|
|
52
|
+
async save(req: FormSaveRequest): Promise<void> {
|
|
53
|
+
await this.executeOpen<unknown>('POST', '/push/api/open/form/save', req);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** 表单详情(含草稿题目、主题、设置)。 */
|
|
57
|
+
detail(id: number): Promise<FormDetail> {
|
|
58
|
+
return this.executeOpen<FormDetail>(
|
|
59
|
+
'GET',
|
|
60
|
+
this.appendQuery('/push/api/open/form/detail', { id }),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** 草稿与发布快照的题目差异。 */
|
|
65
|
+
publishDiff(id: number): Promise<FormPublishDiff> {
|
|
66
|
+
return this.executeOpen<FormPublishDiff>(
|
|
67
|
+
'GET',
|
|
68
|
+
this.appendQuery('/push/api/open/form/publishDiff', { id }),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 发布表单,开始收集。 */
|
|
73
|
+
publish(id: number): Promise<FormPublishResult> {
|
|
74
|
+
return this.executeOpen<FormPublishResult>(
|
|
75
|
+
'POST',
|
|
76
|
+
this.appendQuery('/push/api/open/form/publish', { id }),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** 停止收集。 */
|
|
81
|
+
async stop(id: number): Promise<void> {
|
|
82
|
+
await this.executeOpen<unknown>(
|
|
83
|
+
'POST',
|
|
84
|
+
this.appendQuery('/push/api/open/form/stop', { id }),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 删除表单(不可恢复)。 */
|
|
89
|
+
async delete(id: number): Promise<void> {
|
|
90
|
+
await this.executeOpen<unknown>(
|
|
91
|
+
'POST',
|
|
92
|
+
this.appendQuery('/push/api/open/form/delete', { id }),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/api/friend-api.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AccessKeyManager } from '../access-key-manager';
|
|
2
2
|
import { ResolvedPushPlusConfig } from '../config';
|
|
3
3
|
import { HttpRequester } from '../http';
|
|
4
|
-
import { FriendItem, FriendQrCode, PageQuery, PageResult } from '../models';
|
|
4
|
+
import { FriendBlacklistItem, FriendItem, FriendQrCode, PageQuery, PageResult } from '../models';
|
|
5
5
|
import { OpenAbstractApi } from './open-base';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -44,4 +44,41 @@ export class FriendApi extends OpenAbstractApi {
|
|
|
44
44
|
async editRemark(id: number, remark: string): Promise<void> {
|
|
45
45
|
await this.executeOpen<unknown>('POST', '/api/open/friend/editRemark', { id, remark });
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 5. 将好友加入黑名单。
|
|
50
|
+
*
|
|
51
|
+
* 加入后将解除双方好友关系,对方无法再添加你。不能将自己加入黑名单,仅可将已有好友加入黑名单。
|
|
52
|
+
*
|
|
53
|
+
* @param friendId 好友 id(好友列表中的 friendId 字段)
|
|
54
|
+
*/
|
|
55
|
+
async addBlacklist(friendId: number): Promise<void> {
|
|
56
|
+
await this.executeOpen<unknown>(
|
|
57
|
+
'POST',
|
|
58
|
+
this.appendQuery('/api/open/friend/addBlacklist', { friendId }),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** 6. 好友黑名单列表。 */
|
|
63
|
+
blacklistList(query?: PageQuery): Promise<PageResult<FriendBlacklistItem>> {
|
|
64
|
+
return this.executeOpen<PageResult<FriendBlacklistItem>>(
|
|
65
|
+
'POST',
|
|
66
|
+
'/api/open/friend/blacklistList',
|
|
67
|
+
query ?? {},
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 7. 解除好友黑名单。
|
|
73
|
+
*
|
|
74
|
+
* 解除后不会自动恢复好友关系,需重新扫码添加。
|
|
75
|
+
*
|
|
76
|
+
* @param id 黑名单记录 ID(黑名单列表中的 id 字段)
|
|
77
|
+
*/
|
|
78
|
+
async removeBlacklist(id: number): Promise<void> {
|
|
79
|
+
await this.executeOpen<unknown>(
|
|
80
|
+
'POST',
|
|
81
|
+
this.appendQuery('/api/open/friend/removeBlacklist', { id }),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
47
84
|
}
|
package/src/api/open-base.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { AccessKeyManager } from '../access-key-manager';
|
|
|
2
2
|
import { ResolvedPushPlusConfig } from '../config';
|
|
3
3
|
import { PushPlusError } from '../exception';
|
|
4
4
|
import { HttpRequester } from '../http';
|
|
5
|
+
import { FileMultipart } from '../multipart';
|
|
5
6
|
import { AbstractApi, isApiSuccess } from './base';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -51,4 +52,43 @@ export abstract class OpenAbstractApi extends AbstractApi {
|
|
|
51
52
|
resp.code ?? -1,
|
|
52
53
|
);
|
|
53
54
|
}
|
|
55
|
+
|
|
56
|
+
/** 以 multipart 上传文件(自动携带 access-key;code=401 时刷新后重试一次)。 */
|
|
57
|
+
protected executeOpenMultipart<T>(path: string, multipart: FileMultipart): Promise<T> {
|
|
58
|
+
return this.executeOpenRaw<T>('POST', path, multipart.body, {
|
|
59
|
+
'Content-Type': multipart.contentType,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 执行带二进制 body 的开放接口请求;当返回 code=401 时自动刷新 key 并重试一次。
|
|
65
|
+
*/
|
|
66
|
+
protected async executeOpenRaw<T>(
|
|
67
|
+
method: string,
|
|
68
|
+
path: string,
|
|
69
|
+
body: Uint8Array,
|
|
70
|
+
extraHeaders?: Record<string, string>,
|
|
71
|
+
): Promise<T> {
|
|
72
|
+
const headers = { ...(await this.headersWithAccessKey()), ...(extraHeaders ?? {}) };
|
|
73
|
+
const resp = await this.executeRaw<T>(method, path, headers, body);
|
|
74
|
+
if (isApiSuccess(resp)) {
|
|
75
|
+
return resp.data as T;
|
|
76
|
+
}
|
|
77
|
+
if (resp.code === OpenAbstractApi.CODE_ACCESS_KEY_INVALID) {
|
|
78
|
+
this.accessKeyManager.invalidate();
|
|
79
|
+
const retryHeaders = { ...(await this.headersWithAccessKey()), ...(extraHeaders ?? {}) };
|
|
80
|
+
const retry = await this.executeRaw<T>(method, path, retryHeaders, body);
|
|
81
|
+
if (isApiSuccess(retry)) {
|
|
82
|
+
return retry.data as T;
|
|
83
|
+
}
|
|
84
|
+
throw new PushPlusError(
|
|
85
|
+
`PushPlus 开放接口业务失败(重试后): code=${retry.code}, msg=${retry.msg}`,
|
|
86
|
+
retry.code ?? -1,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
throw new PushPlusError(
|
|
90
|
+
`PushPlus 开放接口业务失败: code=${resp.code}, msg=${resp.msg}`,
|
|
91
|
+
resp.code ?? -1,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
54
94
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AccessKeyManager } from '../access-key-manager';
|
|
2
2
|
import { ResolvedPushPlusConfig } from '../config';
|
|
3
3
|
import { HttpRequester } from '../http';
|
|
4
|
-
import { PageResult, TopicUserItem, TopicUserListQuery } from '../models';
|
|
4
|
+
import { PageResult, TopicUserBlacklistItem, TopicUserItem, TopicUserListQuery } from '../models';
|
|
5
5
|
import { OpenAbstractApi } from './open-base';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -27,4 +27,43 @@ export class TopicUserApi extends OpenAbstractApi {
|
|
|
27
27
|
async editRemark(id: number, remark: string): Promise<void> {
|
|
28
28
|
await this.executeOpen<unknown>('POST', '/api/open/topicUser/editRemark', { id, remark });
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 4. 将订阅人加入黑名单。
|
|
33
|
+
*
|
|
34
|
+
* 加入后将移出群组,对方无法再加入该群组。积分群组不支持黑名单。不能将自己加入黑名单。
|
|
35
|
+
*
|
|
36
|
+
* @param topicRelationId 用户编号(订阅人列表中的 id 字段)
|
|
37
|
+
*/
|
|
38
|
+
async addBlacklist(topicRelationId: number): Promise<void> {
|
|
39
|
+
const path = this.appendQuery('/api/open/topicUser/addBlacklist', { topicRelationId });
|
|
40
|
+
await this.executeOpen<unknown>('POST', path);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 5. 订阅人黑名单列表。
|
|
45
|
+
*
|
|
46
|
+
* `query.params.topicId` 必填。
|
|
47
|
+
*/
|
|
48
|
+
blacklistList(query: TopicUserListQuery): Promise<PageResult<TopicUserBlacklistItem>> {
|
|
49
|
+
return this.executeOpen<PageResult<TopicUserBlacklistItem>>(
|
|
50
|
+
'POST',
|
|
51
|
+
'/api/open/topicUser/blacklistList',
|
|
52
|
+
query,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 6. 解除订阅人黑名单。
|
|
58
|
+
*
|
|
59
|
+
* 解除后不会自动恢复群组订阅,对方可重新加入该群组。
|
|
60
|
+
*
|
|
61
|
+
* @param id 黑名单记录 ID(黑名单列表中的 id 字段)
|
|
62
|
+
*/
|
|
63
|
+
async removeBlacklist(id: number): Promise<void> {
|
|
64
|
+
await this.executeOpen<unknown>(
|
|
65
|
+
'POST',
|
|
66
|
+
this.appendQuery('/api/open/topicUser/removeBlacklist', { id }),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
30
69
|
}
|
package/src/client.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { AccessKeyManager } from './access-key-manager';
|
|
|
2
2
|
import { AccessKeyApi } from './api/access-key-api';
|
|
3
3
|
import { ChannelApi } from './api/channel-api';
|
|
4
4
|
import { ClawBotApi } from './api/clawbot-api';
|
|
5
|
+
import { DocApi } from './api/doc-api';
|
|
6
|
+
import { ExcelApi } from './api/excel-api';
|
|
7
|
+
import { FormApi } from './api/form-api';
|
|
5
8
|
import { FriendApi } from './api/friend-api';
|
|
6
9
|
import { ImageApi } from './api/image-api';
|
|
7
10
|
import { MessageApi } from './api/message-api';
|
|
@@ -67,6 +70,9 @@ export class PushPlusClient {
|
|
|
67
70
|
readonly setting: SettingApi;
|
|
68
71
|
readonly pre: PreApi;
|
|
69
72
|
readonly image: ImageApi;
|
|
73
|
+
readonly form: FormApi;
|
|
74
|
+
readonly doc: DocApi;
|
|
75
|
+
readonly excel: ExcelApi;
|
|
70
76
|
|
|
71
77
|
constructor(options: PushPlusClientOptions = {}) {
|
|
72
78
|
this.config = resolveConfig(options);
|
|
@@ -89,6 +95,9 @@ export class PushPlusClient {
|
|
|
89
95
|
this.setting = new SettingApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
90
96
|
this.pre = new PreApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
91
97
|
this.image = new ImageApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
98
|
+
this.form = new FormApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
99
|
+
this.doc = new DocApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
100
|
+
this.excel = new ExcelApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
92
101
|
}
|
|
93
102
|
|
|
94
103
|
/** 与 Java SDK 风格一致的 Builder 入口。 */
|
package/src/config.ts
CHANGED
|
@@ -87,6 +87,6 @@ export function resolveConfig(input: PushPlusConfig | undefined | null): Resolve
|
|
|
87
87
|
logRequest: cfg.logRequest ?? false,
|
|
88
88
|
rateLimitGuardEnabled: cfg.rateLimitGuardEnabled ?? true,
|
|
89
89
|
rateLimitCooldownMs: cfg.rateLimitCooldownMs ?? 0,
|
|
90
|
-
userAgent: cfg.userAgent ?? `@perk-net/perk-pushplus-sdk/1.
|
|
90
|
+
userAgent: cfg.userAgent ?? `@perk-net/perk-pushplus-sdk/1.2.1`,
|
|
91
91
|
};
|
|
92
92
|
}
|
package/src/enums.ts
CHANGED
|
@@ -118,6 +118,43 @@ export const WebhookTypeDescription: Record<number, string> = {
|
|
|
118
118
|
[WebhookType.CUSTOM]: '自定义',
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* push 表单状态。
|
|
123
|
+
*
|
|
124
|
+
* 0-草稿,1-收集中,2-已停止。
|
|
125
|
+
*/
|
|
126
|
+
export enum FormStatus {
|
|
127
|
+
DRAFT = 0,
|
|
128
|
+
COLLECTING = 1,
|
|
129
|
+
STOPPED = 2,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const FormStatusDescription: Record<FormStatus, string> = {
|
|
133
|
+
[FormStatus.DRAFT]: '草稿',
|
|
134
|
+
[FormStatus.COLLECTING]: '收集中',
|
|
135
|
+
[FormStatus.STOPPED]: '已停止',
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* push 文档 / 表格分享权限。
|
|
140
|
+
*
|
|
141
|
+
* 0-关闭分享,1-开启分享(仅可查看)。
|
|
142
|
+
*/
|
|
143
|
+
export enum SharePerm {
|
|
144
|
+
CLOSED = 0,
|
|
145
|
+
VIEW = 1,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* push 文档 / 表格打开分享页是否需要登录。
|
|
150
|
+
*
|
|
151
|
+
* 0-免登录,1-需登录。
|
|
152
|
+
*/
|
|
153
|
+
export enum ShareLogin {
|
|
154
|
+
ANONYMOUS = 0,
|
|
155
|
+
REQUIRED = 1,
|
|
156
|
+
};
|
|
157
|
+
|
|
121
158
|
/**
|
|
122
159
|
* PushPlus 接口业务返回码语义。
|
|
123
160
|
*
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,10 @@ export {
|
|
|
25
25
|
CallbackEvent,
|
|
26
26
|
WebhookType,
|
|
27
27
|
WebhookTypeDescription,
|
|
28
|
+
FormStatus,
|
|
29
|
+
FormStatusDescription,
|
|
30
|
+
SharePerm,
|
|
31
|
+
ShareLogin,
|
|
28
32
|
ErrorCode,
|
|
29
33
|
errorCodeFromValue,
|
|
30
34
|
isRateLimitedCode,
|
|
@@ -75,10 +79,12 @@ export type {
|
|
|
75
79
|
TopicItem,
|
|
76
80
|
TopicUserItem,
|
|
77
81
|
TopicUserListQuery,
|
|
82
|
+
TopicUserBlacklistItem,
|
|
78
83
|
WebhookItem,
|
|
79
84
|
WebhookSaveRequest,
|
|
80
85
|
FriendItem,
|
|
81
86
|
FriendQrCode,
|
|
87
|
+
FriendBlacklistItem,
|
|
82
88
|
ClawBotInfo,
|
|
83
89
|
ClawBotMessage,
|
|
84
90
|
ClawBotQrCode,
|
|
@@ -96,6 +102,23 @@ export type {
|
|
|
96
102
|
ImageUploadToken,
|
|
97
103
|
ImageUploadResult,
|
|
98
104
|
ImageItem,
|
|
105
|
+
FormListQuery,
|
|
106
|
+
FormCover,
|
|
107
|
+
FormTheme,
|
|
108
|
+
FormSettings,
|
|
109
|
+
FormItem,
|
|
110
|
+
FormListItem,
|
|
111
|
+
FormSaveRequest,
|
|
112
|
+
FormDetail,
|
|
113
|
+
FormPublishDiff,
|
|
114
|
+
FormPublishResult,
|
|
115
|
+
DocListQuery,
|
|
116
|
+
DocListItem,
|
|
117
|
+
DocVo,
|
|
118
|
+
DocContent,
|
|
119
|
+
ExcelVo,
|
|
120
|
+
ExcelContent,
|
|
121
|
+
ExcelWriteCellsRequest,
|
|
99
122
|
} from './models';
|
|
100
123
|
|
|
101
124
|
export {
|
|
@@ -126,3 +149,7 @@ export {
|
|
|
126
149
|
type ImageFileInput,
|
|
127
150
|
type ImageUploadOptions,
|
|
128
151
|
} from './api/image-api';
|
|
152
|
+
export { FormApi } from './api/form-api';
|
|
153
|
+
export { DocApi } from './api/doc-api';
|
|
154
|
+
export { ExcelApi } from './api/excel-api';
|
|
155
|
+
export { type FileInput } from './multipart';
|