@perk-net/perk-pushplus-sdk 1.0.0

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.
@@ -0,0 +1,42 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import { ClawBotInfo, ClawBotMessage, ClawBotQrCode } from '../models';
5
+ import { OpenAbstractApi } from './open-base';
6
+
7
+ /**
8
+ * 开放接口 - 微信 ClawBot(文档「八. 微信ClawBot接口」)。
9
+ */
10
+ export class ClawBotApi extends OpenAbstractApi {
11
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
12
+ super(config, http, mgr);
13
+ }
14
+
15
+ /** 1. 获取二维码。 */
16
+ getBotQrcode(): Promise<ClawBotQrCode> {
17
+ return this.executeOpen<ClawBotQrCode>('GET', '/api/open/clawBot/getBotQrcode');
18
+ }
19
+
20
+ /** 2. 扫码结果查询。 */
21
+ async getQrcodeStatus(qrcode: string): Promise<void> {
22
+ await this.executeOpen<unknown>(
23
+ 'GET',
24
+ this.appendQuery('/api/open/clawBot/getQrcodeStatus', { getQrcodeStatus: qrcode }),
25
+ );
26
+ }
27
+
28
+ /** 3. 绑定详情。 */
29
+ botInfo(): Promise<ClawBotInfo> {
30
+ return this.executeOpen<ClawBotInfo>('GET', '/api/open/clawBot/botInfo');
31
+ }
32
+
33
+ /** 4. 解绑。 */
34
+ async unbind(): Promise<void> {
35
+ await this.executeOpen<unknown>('GET', '/api/open/clawBot/unbind');
36
+ }
37
+
38
+ /** 5. 获取发送消息。 */
39
+ getMsg(): Promise<ClawBotMessage[]> {
40
+ return this.executeOpen<ClawBotMessage[]>('GET', '/api/open/clawBot/getMsg');
41
+ }
42
+ }
@@ -0,0 +1,47 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import { FriendItem, FriendQrCode, PageQuery, PageResult } from '../models';
5
+ import { OpenAbstractApi } from './open-base';
6
+
7
+ /**
8
+ * 开放接口 - 好友功能(文档「十. 好友功能接口」)。
9
+ */
10
+ export class FriendApi extends OpenAbstractApi {
11
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
12
+ super(config, http, mgr);
13
+ }
14
+
15
+ /** 1. 获取个人二维码。 */
16
+ getQrCode(options: {
17
+ appId?: string;
18
+ content?: string;
19
+ second?: number;
20
+ scanCount?: number;
21
+ }): Promise<FriendQrCode> {
22
+ const p: Record<string, unknown> = {};
23
+ if (options.appId != null) p.appId = options.appId;
24
+ if (options.content != null) p.content = options.content;
25
+ if (options.second != null) p.second = options.second;
26
+ if (options.scanCount != null) p.scanCount = options.scanCount;
27
+ return this.executeOpen<FriendQrCode>('GET', this.appendQuery('/api/open/friend/getQrCode', p));
28
+ }
29
+
30
+ /** 2. 获取好友列表。 */
31
+ list(query?: PageQuery): Promise<PageResult<FriendItem>> {
32
+ return this.executeOpen<PageResult<FriendItem>>('POST', '/api/open/friend/list', query ?? {});
33
+ }
34
+
35
+ /** 3. 删除好友。 */
36
+ async delete(friendId: number): Promise<void> {
37
+ await this.executeOpen<unknown>(
38
+ 'GET',
39
+ this.appendQuery('/api/open/friend/deleteFriend', { friendId }),
40
+ );
41
+ }
42
+
43
+ /** 4. 修改好友备注。 */
44
+ async editRemark(id: number, remark: string): Promise<void> {
45
+ await this.executeOpen<unknown>('POST', '/api/open/friend/editRemark', { id, remark });
46
+ }
47
+ }
@@ -0,0 +1,109 @@
1
+ import { ResolvedPushPlusConfig } from '../config';
2
+ import { PushPlusError } from '../exception';
3
+ import { HttpRequester } from '../http';
4
+ import { BatchSendRequest, BatchSendResult, SendRequest } from '../models';
5
+ import { RateLimitGuard } from '../rate-limit';
6
+ import { AbstractApi } from './base';
7
+
8
+ /**
9
+ * 发送消息接口。
10
+ *
11
+ * 对应 PushPlus 文档「二. 发送消息接口」与「三. 多渠道发送消息接口」。
12
+ *
13
+ * 内置本地限流守卫:当上游返回 ErrorCode.RATE_LIMITED(code=900)时,
14
+ * 后续对同一 token 的发送调用会在 SDK 内被直接短路抛 PushPlusError,
15
+ * 不再发起 HTTP,直到守卫到期自动解除。
16
+ */
17
+ export class MessageApi extends AbstractApi {
18
+ private readonly rateLimitGuard: RateLimitGuard;
19
+
20
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, rateLimitGuard: RateLimitGuard) {
21
+ super(config, http);
22
+ this.rateLimitGuard = rateLimitGuard;
23
+ }
24
+
25
+ /** 暴露限流守卫,便于运维场景手动 clear 或观察解禁时间。 */
26
+ getRateLimitGuard(): RateLimitGuard {
27
+ return this.rateLimitGuard;
28
+ }
29
+
30
+ /**
31
+ * 发送单条消息。
32
+ *
33
+ * @returns 消息流水号
34
+ */
35
+ async send(request: SendRequest): Promise<string> {
36
+ const req = this.withDefaultToken(request);
37
+ validateSend(req);
38
+ this.rateLimitGuard.check(req.token);
39
+ return this.executeWithGuard(req.token, () =>
40
+ this.executeForData<string>('POST', '/send', null, req),
41
+ );
42
+ }
43
+
44
+ /**
45
+ * 多渠道发送消息。
46
+ */
47
+ async batchSend(request: BatchSendRequest): Promise<BatchSendResult[]> {
48
+ const req = this.withDefaultBatchToken(request);
49
+ validateBatch(req);
50
+ this.rateLimitGuard.check(req.token);
51
+ return this.executeWithGuard(req.token, () =>
52
+ this.executeForData<BatchSendResult[]>('POST', '/batchSend', null, req),
53
+ );
54
+ }
55
+
56
+ /** 便捷方法:以默认渠道、默认模板发送一条简单消息。 */
57
+ sendSimple(title: string | undefined, content: string): Promise<string> {
58
+ return this.send({ title, content });
59
+ }
60
+
61
+ /* ============================== 内部辅助 ============================== */
62
+
63
+ private async executeWithGuard<T>(token: string | undefined, call: () => Promise<T>): Promise<T> {
64
+ try {
65
+ return await call();
66
+ } catch (e) {
67
+ if (e instanceof PushPlusError && e.isRateLimited()) {
68
+ this.rateLimitGuard.markBlocked(token);
69
+ }
70
+ throw e;
71
+ }
72
+ }
73
+
74
+ private withDefaultToken(req: SendRequest): SendRequest {
75
+ if (req == null) throw new PushPlusError('SendRequest 不能为空');
76
+ if (!req.token || req.token.trim().length === 0) {
77
+ return { ...req, token: this.requireToken() };
78
+ }
79
+ return req;
80
+ }
81
+
82
+ private withDefaultBatchToken(req: BatchSendRequest): BatchSendRequest {
83
+ if (req == null) throw new PushPlusError('BatchSendRequest 不能为空');
84
+ if (!req.token || req.token.trim().length === 0) {
85
+ return { ...req, token: this.requireToken() };
86
+ }
87
+ return req;
88
+ }
89
+
90
+ private requireToken(): string {
91
+ const t = this.config.token;
92
+ if (!t || t.trim().length === 0) {
93
+ throw new PushPlusError('发送消息需要 token,但 PushPlusConfig.token 为空');
94
+ }
95
+ return t;
96
+ }
97
+ }
98
+
99
+ function validateSend(req: SendRequest): void {
100
+ if (!req.content || req.content.trim().length === 0) {
101
+ throw new PushPlusError('发送消息 content 不能为空');
102
+ }
103
+ }
104
+
105
+ function validateBatch(req: BatchSendRequest): void {
106
+ if (!req.content || req.content.trim().length === 0) {
107
+ throw new PushPlusError('批量发送消息 content 不能为空');
108
+ }
109
+ }
@@ -0,0 +1,52 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import {
5
+ MessageTokenAddRequest,
6
+ MessageTokenEditRequest,
7
+ MessageTokenItem,
8
+ MessageTokenOption,
9
+ PageQuery,
10
+ PageResult,
11
+ } from '../models';
12
+ import { OpenAbstractApi } from './open-base';
13
+
14
+ /**
15
+ * 开放接口 - 消息 token(文档「四. 消息token接口」)。
16
+ */
17
+ export class MessageTokenApi extends OpenAbstractApi {
18
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
19
+ super(config, http, mgr);
20
+ }
21
+
22
+ /** 获取消息 token 列表。 */
23
+ list(query?: PageQuery): Promise<PageResult<MessageTokenItem>> {
24
+ return this.executeOpen<PageResult<MessageTokenItem>>('POST', '/api/open/token/list', query ?? {});
25
+ }
26
+
27
+ /** 新增消息 token,返回新建的 token 字符串。 */
28
+ add(req: MessageTokenAddRequest): Promise<string> {
29
+ return this.executeOpen<string>('POST', '/api/open/token/add', req);
30
+ }
31
+
32
+ /** 修改消息 token。 */
33
+ edit(req: MessageTokenEditRequest): Promise<string> {
34
+ return this.executeOpen<string>('POST', '/api/open/token/edit', req);
35
+ }
36
+
37
+ /** 删除消息 token。 */
38
+ delete(id: number): Promise<string> {
39
+ const path = this.appendQuery('/api/open/token/deleteToken', { id });
40
+ return this.executeOpen<string>('DELETE', path);
41
+ }
42
+
43
+ /**
44
+ * 消息 token 下拉选择列表。
45
+ *
46
+ * @param type 0-返回所有;1-返回未配置默认推送渠道的消息 token
47
+ */
48
+ selectList(type?: number): Promise<MessageTokenOption[]> {
49
+ const path = this.appendQuery('/api/open/token/selectTokenList', { type: type ?? 0 });
50
+ return this.executeOpen<MessageTokenOption[]>('GET', path);
51
+ }
52
+ }
@@ -0,0 +1,54 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { PushPlusError } from '../exception';
4
+ import { HttpRequester } from '../http';
5
+ import { AbstractApi, isApiSuccess } from './base';
6
+
7
+ /**
8
+ * 开放接口基类。会自动在 header 中带上 access-key,
9
+ * 并在收到 401 类业务错误时尝试重试一次(刷新 AccessKey 后重试)。
10
+ */
11
+ export abstract class OpenAbstractApi extends AbstractApi {
12
+ static readonly HEADER_ACCESS_KEY = 'access-key';
13
+ /** PushPlus AccessKey 失效相关的业务码(用于触发自动重试)。 */
14
+ private static readonly CODE_ACCESS_KEY_INVALID = 401;
15
+
16
+ protected readonly accessKeyManager: AccessKeyManager;
17
+
18
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, accessKeyManager: AccessKeyManager) {
19
+ super(config, http);
20
+ this.accessKeyManager = accessKeyManager;
21
+ }
22
+
23
+ private async headersWithAccessKey(): Promise<Record<string, string>> {
24
+ const key = await this.accessKeyManager.getAccessKey();
25
+ return { [OpenAbstractApi.HEADER_ACCESS_KEY]: key };
26
+ }
27
+
28
+ /**
29
+ * 执行带 access-key 的请求;当返回 code=401 时自动刷新 key 并重试一次。
30
+ */
31
+ protected async executeOpen<T>(method: string, path: string, body?: unknown): Promise<T> {
32
+ const headers = await this.headersWithAccessKey();
33
+ const resp = await this.execute<T>(method, path, headers, body);
34
+ if (isApiSuccess(resp)) {
35
+ return resp.data as T;
36
+ }
37
+ if (resp.code === OpenAbstractApi.CODE_ACCESS_KEY_INVALID) {
38
+ this.accessKeyManager.invalidate();
39
+ const retryHeaders = await this.headersWithAccessKey();
40
+ const retry = await this.execute<T>(method, path, retryHeaders, body);
41
+ if (isApiSuccess(retry)) {
42
+ return retry.data as T;
43
+ }
44
+ throw new PushPlusError(
45
+ `PushPlus 开放接口业务失败(重试后): code=${retry.code}, msg=${retry.msg}`,
46
+ retry.code ?? -1,
47
+ );
48
+ }
49
+ throw new PushPlusError(
50
+ `PushPlus 开放接口业务失败: code=${resp.code}, msg=${resp.msg}`,
51
+ resp.code ?? -1,
52
+ );
53
+ }
54
+ }
@@ -0,0 +1,50 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { PushPlusError } from '../exception';
4
+ import { HttpRequester } from '../http';
5
+ import { MessageItem, PageQuery, PageResult, SendMessageResult } from '../models';
6
+ import { OpenAbstractApi } from './open-base';
7
+
8
+ /**
9
+ * 开放接口 - 消息接口(文档「二. 消息接口」)。
10
+ */
11
+ export class OpenMessageApi extends OpenAbstractApi {
12
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
13
+ super(config, http, mgr);
14
+ }
15
+
16
+ /** 1. 消息列表。 */
17
+ list(query?: PageQuery): Promise<PageResult<MessageItem>> {
18
+ return this.executeOpen<PageResult<MessageItem>>('POST', '/api/open/message/list', query ?? {});
19
+ }
20
+
21
+ /** 2. 查询消息发送结果。 */
22
+ queryResult(shortCode: string): Promise<SendMessageResult> {
23
+ if (!shortCode || shortCode.trim().length === 0) {
24
+ throw new PushPlusError('shortCode 不能为空');
25
+ }
26
+ const path = this.appendQuery('/api/open/message/sendMessageResult', { shortCode });
27
+ return this.executeOpen<SendMessageResult>('GET', path);
28
+ }
29
+
30
+ /** 3. 删除消息。 */
31
+ delete(shortCode: string): Promise<string> {
32
+ if (!shortCode || shortCode.trim().length === 0) {
33
+ throw new PushPlusError('shortCode 不能为空');
34
+ }
35
+ const path = this.appendQuery('/api/open/message/deleteMessage', { shortCode });
36
+ return this.executeOpen<string>('DELETE', path);
37
+ }
38
+
39
+ /**
40
+ * 4. 消息详情(HTML 页面 URL)。
41
+ *
42
+ * 该接口直接返回 HTML 内容,SDK 仅返回访问 URL,调用方自行决定是否拉取页面内容。
43
+ */
44
+ detailUrl(shortCode: string): string {
45
+ if (!shortCode || shortCode.trim().length === 0) {
46
+ throw new PushPlusError('shortCode 不能为空');
47
+ }
48
+ return this.resolveUrl('/shortMessage/' + shortCode);
49
+ }
50
+ }
@@ -0,0 +1,42 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import { PageQuery, PageResult, PreDetail, PreItem, PreSaveRequest, PreTestRequest } from '../models';
5
+ import { OpenAbstractApi } from './open-base';
6
+
7
+ /**
8
+ * 开放接口 - 预处理信息(文档「十一. 预处理信息接口」)。注:需开通会员。
9
+ */
10
+ export class PreApi extends OpenAbstractApi {
11
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
12
+ super(config, http, mgr);
13
+ }
14
+
15
+ list(q?: PageQuery): Promise<PageResult<PreItem>> {
16
+ return this.executeOpen<PageResult<PreItem>>('POST', '/api/open/pre/list', q ?? {});
17
+ }
18
+
19
+ detail(preId: number): Promise<PreDetail> {
20
+ return this.executeOpen<PreDetail>('GET', this.appendQuery('/api/open/pre/detail', { preId }));
21
+ }
22
+
23
+ add(req: PreSaveRequest): Promise<number> {
24
+ return this.executeOpen<number>('POST', '/api/open/pre/add', req);
25
+ }
26
+
27
+ edit(req: PreSaveRequest): Promise<string> {
28
+ return this.executeOpen<string>('POST', '/api/open/pre/edit', req);
29
+ }
30
+
31
+ delete(preId: number): Promise<string> {
32
+ return this.executeOpen<string>(
33
+ 'DELETE',
34
+ this.appendQuery('/api/open/pre/delete', { preId }),
35
+ );
36
+ }
37
+
38
+ /** 测试预处理代码,返回处理后的消息。 */
39
+ test(req: PreTestRequest): Promise<string> {
40
+ return this.executeOpen<string>('POST', '/api/open/pre/test', req);
41
+ }
42
+ }
@@ -0,0 +1,99 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import {
5
+ PageQuery,
6
+ PageResult,
7
+ UserDefaultDetail,
8
+ UserDefaultItem,
9
+ UserDefaultSaveRequest,
10
+ } from '../models';
11
+ import { OpenAbstractApi } from './open-base';
12
+
13
+ /**
14
+ * 开放接口 - 功能设置(文档「九. 功能设置接口」)。
15
+ */
16
+ export class SettingApi extends OpenAbstractApi {
17
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
18
+ super(config, http, mgr);
19
+ }
20
+
21
+ /** 1. 获取默认配置列表。 */
22
+ listUserDefault(q?: PageQuery): Promise<PageResult<UserDefaultItem>> {
23
+ return this.executeOpen<PageResult<UserDefaultItem>>('POST', '/api/open/setting/listUserDefault', q ?? {});
24
+ }
25
+
26
+ /** 2. 默认配置详情。 */
27
+ detailUserDefault(id: number): Promise<UserDefaultDetail> {
28
+ return this.executeOpen<UserDefaultDetail>(
29
+ 'GET',
30
+ this.appendQuery('/api/open/setting/detailUserDefault', { id }),
31
+ );
32
+ }
33
+
34
+ /** 3. 新增默认配置。 */
35
+ async addUserDefault(req: UserDefaultSaveRequest): Promise<void> {
36
+ await this.executeOpen<unknown>('POST', '/api/open/setting/addUserDefault', req);
37
+ }
38
+
39
+ /** 4. 修改默认配置。 */
40
+ async editUserDefault(req: UserDefaultSaveRequest): Promise<void> {
41
+ await this.executeOpen<unknown>('POST', '/api/open/setting/editUserDefault', req);
42
+ }
43
+
44
+ /** 5. 删除默认配置。 */
45
+ async deleteUserDefault(id: number): Promise<void> {
46
+ await this.executeOpen<unknown>(
47
+ 'DELETE',
48
+ this.appendQuery('/api/open/setting/deleteUserDefault', { id }),
49
+ );
50
+ }
51
+
52
+ /**
53
+ * 6. 修改接收消息限制。
54
+ *
55
+ * @param recevieLimit 0-接收全部,1-不接收消息
56
+ */
57
+ async changeReceiveLimit(recevieLimit: number): Promise<void> {
58
+ await this.executeOpen<unknown>(
59
+ 'GET',
60
+ this.appendQuery('/api/open/setting/changeRecevieLimit', { recevieLimit }),
61
+ );
62
+ }
63
+
64
+ /**
65
+ * 7. 开启/关闭发送消息功能。
66
+ *
67
+ * @param isSend 0-禁用,1-启用
68
+ */
69
+ async changeIsSend(isSend: number): Promise<void> {
70
+ await this.executeOpen<unknown>(
71
+ 'GET',
72
+ this.appendQuery('/api/open/setting/changeIsSend', { isSend }),
73
+ );
74
+ }
75
+
76
+ /**
77
+ * 8. 修改打开消息方式。
78
+ *
79
+ * @param openMessageType 0:H5,1:小程序
80
+ */
81
+ async changeOpenMessageType(openMessageType: number): Promise<void> {
82
+ await this.executeOpen<unknown>(
83
+ 'GET',
84
+ this.appendQuery('/api/open/setting/changeOpenMessageType', { openMessageType }),
85
+ );
86
+ }
87
+
88
+ /**
89
+ * 9. 修改插件渠道转发。
90
+ *
91
+ * @param forward 0:否,1:是
92
+ */
93
+ async changeExtensionForward(forward: number): Promise<void> {
94
+ await this.executeOpen<unknown>(
95
+ 'GET',
96
+ this.appendQuery('/api/open/setting/extension', { forward }),
97
+ );
98
+ }
99
+ }
@@ -0,0 +1,80 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import {
5
+ PageResult,
6
+ TopicAddRequest,
7
+ TopicDetail,
8
+ TopicEditRequest,
9
+ TopicItem,
10
+ TopicListQuery,
11
+ TopicQrCode,
12
+ } from '../models';
13
+ import { OpenAbstractApi } from './open-base';
14
+
15
+ /**
16
+ * 开放接口 - 群组接口(文档「五. 群组接口」)。
17
+ */
18
+ export class TopicApi extends OpenAbstractApi {
19
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
20
+ super(config, http, mgr);
21
+ }
22
+
23
+ /** 1. 群组列表。 */
24
+ list(query?: TopicListQuery): Promise<PageResult<TopicItem>> {
25
+ return this.executeOpen<PageResult<TopicItem>>('POST', '/api/open/topic/list', query ?? {});
26
+ }
27
+
28
+ /** 2. 获取我创建的群组详情。 */
29
+ detail(topicId: number): Promise<TopicDetail> {
30
+ return this.executeOpen<TopicDetail>('GET', this.appendQuery('/api/open/topic/detail', { topicId }));
31
+ }
32
+
33
+ /** 3. 获取我加入的群详情。 */
34
+ joinDetail(topicId: number): Promise<TopicDetail> {
35
+ return this.executeOpen<TopicDetail>(
36
+ 'GET',
37
+ this.appendQuery('/api/open/topic/joinTopicDetail', { topicId }),
38
+ );
39
+ }
40
+
41
+ /** 4. 新增群组,返回新建群组编号。 */
42
+ add(req: TopicAddRequest): Promise<number> {
43
+ return this.executeOpen<number>('POST', '/api/open/topic/add', req);
44
+ }
45
+
46
+ /** 5. 修改群组。 */
47
+ edit(req: TopicEditRequest): Promise<string> {
48
+ return this.executeOpen<string>('POST', '/api/open/topic/editTopic', req);
49
+ }
50
+
51
+ /** 6. 获取群组二维码。 */
52
+ qrCode(topicId: number, second?: number, scanCount?: number): Promise<TopicQrCode> {
53
+ const params: Record<string, unknown> = { topicId };
54
+ if (second != null) params.second = second;
55
+ if (scanCount != null) params.scanCount = scanCount;
56
+ return this.executeOpen<TopicQrCode>('GET', this.appendQuery('/api/open/topic/qrCode', params));
57
+ }
58
+
59
+ /** 7. 退出群组。 */
60
+ exit(topicId: number): Promise<string> {
61
+ return this.executeOpen<string>(
62
+ 'GET',
63
+ this.appendQuery('/api/open/topic/exitTopic', { topicId }),
64
+ );
65
+ }
66
+
67
+ /** 8. 删除群组。 */
68
+ delete(topicId: number): Promise<string> {
69
+ return this.executeOpen<string>('GET', this.appendQuery('/api/open/topic/delete', { topicId }));
70
+ }
71
+
72
+ /**
73
+ * 9. 上下架积分群组。
74
+ *
75
+ * @param isOpen 1-上架,0-下架
76
+ */
77
+ setOpen(topicId: number, isOpen: number): Promise<string> {
78
+ return this.executeOpen<string>('POST', '/api/open/topic/isOpen', { topic: topicId, isOpen });
79
+ }
80
+ }
@@ -0,0 +1,30 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import { PageResult, TopicUserItem, TopicUserListQuery } from '../models';
5
+ import { OpenAbstractApi } from './open-base';
6
+
7
+ /**
8
+ * 开放接口 - 群组用户(文档「六. 群组用户接口」)。
9
+ */
10
+ export class TopicUserApi extends OpenAbstractApi {
11
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
12
+ super(config, http, mgr);
13
+ }
14
+
15
+ /** 1. 获取群组内用户。 */
16
+ subscriberList(query: TopicUserListQuery): Promise<PageResult<TopicUserItem>> {
17
+ return this.executeOpen<PageResult<TopicUserItem>>('POST', '/api/open/topicUser/subscriberList', query);
18
+ }
19
+
20
+ /** 2. 删除群组内用户。 */
21
+ deleteUser(topicRelationId: number): Promise<string> {
22
+ const path = this.appendQuery('/api/open/topicUser/deleteTopicUser', { topicRelationId });
23
+ return this.executeOpen<string>('POST', path);
24
+ }
25
+
26
+ /** 3. 修改订阅人备注。 */
27
+ async editRemark(id: number, remark: string): Promise<void> {
28
+ await this.executeOpen<unknown>('POST', '/api/open/topicUser/editRemark', { id, remark });
29
+ }
30
+ }
@@ -0,0 +1,34 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import { SendCount, UserInfo, UserLimitTime } from '../models';
5
+ import { OpenAbstractApi } from './open-base';
6
+
7
+ /**
8
+ * 开放接口 - 用户接口(文档「三. 用户接口」)。
9
+ */
10
+ export class UserApi extends OpenAbstractApi {
11
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
12
+ super(config, http, mgr);
13
+ }
14
+
15
+ /** 获取用户 token。 */
16
+ getToken(): Promise<string> {
17
+ return this.executeOpen<string>('GET', '/api/open/user/token');
18
+ }
19
+
20
+ /** 个人资料详情。 */
21
+ myInfo(): Promise<UserInfo> {
22
+ return this.executeOpen<UserInfo>('GET', '/api/open/user/myInfo');
23
+ }
24
+
25
+ /** 获取解封剩余时间。 */
26
+ getLimitTime(): Promise<UserLimitTime> {
27
+ return this.executeOpen<UserLimitTime>('GET', '/api/open/user/userLimitTime');
28
+ }
29
+
30
+ /** 查询当日消息接口请求次数。 */
31
+ getSendCount(): Promise<SendCount> {
32
+ return this.executeOpen<SendCount>('GET', '/api/open/user/sendCount');
33
+ }
34
+ }
@@ -0,0 +1,34 @@
1
+ import { AccessKeyManager } from '../access-key-manager';
2
+ import { ResolvedPushPlusConfig } from '../config';
3
+ import { HttpRequester } from '../http';
4
+ import { PageQuery, PageResult, WebhookItem, WebhookSaveRequest } from '../models';
5
+ import { OpenAbstractApi } from './open-base';
6
+
7
+ /**
8
+ * 开放接口 - webhook 渠道配置(文档「七. 渠道配置接口」 1-4)。
9
+ */
10
+ export class WebhookApi extends OpenAbstractApi {
11
+ constructor(config: ResolvedPushPlusConfig, http: HttpRequester, mgr: AccessKeyManager) {
12
+ super(config, http, mgr);
13
+ }
14
+
15
+ list(q?: PageQuery): Promise<PageResult<WebhookItem>> {
16
+ return this.executeOpen<PageResult<WebhookItem>>('POST', '/api/open/webhook/list', q ?? {});
17
+ }
18
+
19
+ detail(webhookId: number): Promise<WebhookItem> {
20
+ return this.executeOpen<WebhookItem>(
21
+ 'GET',
22
+ this.appendQuery('/api/open/webhook/detail', { webhookId }),
23
+ );
24
+ }
25
+
26
+ /** 新增 webhook,返回新 id。 */
27
+ add(req: WebhookSaveRequest): Promise<number> {
28
+ return this.executeOpen<number>('POST', '/api/open/webhook/add', req);
29
+ }
30
+
31
+ edit(req: WebhookSaveRequest): Promise<string> {
32
+ return this.executeOpen<string>('POST', '/api/open/webhook/edit', req);
33
+ }
34
+ }