@perk-net/perk-pushplus-sdk 1.0.0 → 1.1.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.
- package/README.md +52 -0
- package/dist/index.cjs +233 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +158 -2
- package/dist/index.d.ts +158 -2
- package/dist/index.global.js +233 -5
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +232 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/image-api.ts +241 -0
- package/src/client.ts +3 -0
- package/src/config.ts +1 -1
- package/src/enums.ts +2 -0
- package/src/http.ts +101 -6
- package/src/index.ts +11 -0
- package/src/models.ts +68 -0
package/dist/index.global.js
CHANGED
|
@@ -23,6 +23,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
23
23
|
Template2["JENKINS"] = "jenkins";
|
|
24
24
|
Template2["ROUTE"] = "route";
|
|
25
25
|
Template2["PAY"] = "pay";
|
|
26
|
+
Template2["FORM"] = "form";
|
|
26
27
|
return Template2;
|
|
27
28
|
})(Template || {});
|
|
28
29
|
var SendStatus = /* @__PURE__ */ ((SendStatus2) => {
|
|
@@ -186,6 +187,25 @@ var PerkPushPlus = (function (exports) {
|
|
|
186
187
|
};
|
|
187
188
|
|
|
188
189
|
// src/http.ts
|
|
190
|
+
async function callExecuteRaw(requester, options) {
|
|
191
|
+
if (typeof requester.executeRaw === "function") {
|
|
192
|
+
return requester.executeRaw(options);
|
|
193
|
+
}
|
|
194
|
+
const { method, url, headers, body } = options;
|
|
195
|
+
let text = null;
|
|
196
|
+
if (body != null) {
|
|
197
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) {
|
|
198
|
+
text = await body.text();
|
|
199
|
+
} else if (body instanceof Uint8Array) {
|
|
200
|
+
text = new TextDecoder("utf-8").decode(body);
|
|
201
|
+
} else if (body instanceof ArrayBuffer) {
|
|
202
|
+
text = new TextDecoder("utf-8").decode(new Uint8Array(body));
|
|
203
|
+
} else {
|
|
204
|
+
text = String(body);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return requester.execute({ method, url, headers, body: text });
|
|
208
|
+
}
|
|
189
209
|
var FetchHttpRequester = class {
|
|
190
210
|
constructor(config, fetchImpl) {
|
|
191
211
|
this.readTimeoutMs = config.readTimeoutMs;
|
|
@@ -201,7 +221,29 @@ var PerkPushPlus = (function (exports) {
|
|
|
201
221
|
}
|
|
202
222
|
async execute(options) {
|
|
203
223
|
var _a, _b;
|
|
204
|
-
|
|
224
|
+
return this.doExecute({
|
|
225
|
+
method: options.method,
|
|
226
|
+
url: options.url,
|
|
227
|
+
headers: options.headers,
|
|
228
|
+
body: (_a = options.body) != null ? _a : null,
|
|
229
|
+
bodyForLog: (_b = options.body) != null ? _b : null,
|
|
230
|
+
defaultContentType: "application/json;charset=UTF-8"
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async executeRaw(options) {
|
|
234
|
+
var _a;
|
|
235
|
+
return this.doExecute({
|
|
236
|
+
method: options.method,
|
|
237
|
+
url: options.url,
|
|
238
|
+
headers: options.headers,
|
|
239
|
+
body: (_a = options.body) != null ? _a : null,
|
|
240
|
+
bodyForLog: null,
|
|
241
|
+
defaultContentType: "application/octet-stream"
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
async doExecute(args) {
|
|
245
|
+
var _a, _b;
|
|
246
|
+
const { method, url, headers, body, bodyForLog, defaultContentType } = args;
|
|
205
247
|
const finalHeaders = {};
|
|
206
248
|
let hasContentType = false;
|
|
207
249
|
if (headers) {
|
|
@@ -211,14 +253,19 @@ var PerkPushPlus = (function (exports) {
|
|
|
211
253
|
if (k.toLowerCase() === "content-type") hasContentType = true;
|
|
212
254
|
}
|
|
213
255
|
}
|
|
214
|
-
if (body != null && !hasContentType) {
|
|
215
|
-
finalHeaders["Content-Type"] =
|
|
256
|
+
if (body != null && !hasContentType && defaultContentType) {
|
|
257
|
+
finalHeaders["Content-Type"] = defaultContentType;
|
|
216
258
|
}
|
|
217
259
|
if (typeof window === "undefined" && !finalHeaders["User-Agent"] && !finalHeaders["user-agent"]) {
|
|
218
260
|
finalHeaders["User-Agent"] = this.userAgent;
|
|
219
261
|
}
|
|
220
262
|
if (this.logRequest) {
|
|
221
|
-
|
|
263
|
+
if (bodyForLog != null) {
|
|
264
|
+
console.debug("[pushplus] >>>", method, url, "body=", bodyForLog);
|
|
265
|
+
} else {
|
|
266
|
+
const len = bodyLength(body);
|
|
267
|
+
console.debug("[pushplus] >>>", method, url, "bodyBytes=", len);
|
|
268
|
+
}
|
|
222
269
|
}
|
|
223
270
|
const controller = new AbortController();
|
|
224
271
|
const timer = this.readTimeoutMs > 0 ? setTimeout(() => controller.abort(), this.readTimeoutMs) : null;
|
|
@@ -250,6 +297,14 @@ var PerkPushPlus = (function (exports) {
|
|
|
250
297
|
}
|
|
251
298
|
}
|
|
252
299
|
};
|
|
300
|
+
function bodyLength(body) {
|
|
301
|
+
if (body == null) return 0;
|
|
302
|
+
if (typeof body === "string") return body.length;
|
|
303
|
+
if (body instanceof Uint8Array) return body.byteLength;
|
|
304
|
+
if (body instanceof ArrayBuffer) return body.byteLength;
|
|
305
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) return body.size;
|
|
306
|
+
return -1;
|
|
307
|
+
}
|
|
253
308
|
function isSuccessfulHttpStatus(status) {
|
|
254
309
|
return status >= 200 && status < 300;
|
|
255
310
|
}
|
|
@@ -521,6 +576,168 @@ var PerkPushPlus = (function (exports) {
|
|
|
521
576
|
}
|
|
522
577
|
};
|
|
523
578
|
|
|
579
|
+
// src/api/image-api.ts
|
|
580
|
+
var ImageApi = class extends OpenAbstractApi {
|
|
581
|
+
constructor(config, http, mgr) {
|
|
582
|
+
super(config, http, mgr);
|
|
583
|
+
}
|
|
584
|
+
/** 1. 获取上传凭证。 */
|
|
585
|
+
getUploadToken() {
|
|
586
|
+
return this.executeOpen("GET", "/api/open/userImage/uploadToken");
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* 2. 上传图片到七牛云。
|
|
590
|
+
*
|
|
591
|
+
* 使用「获取上传凭证」返回的 `uploadUrl` 与 `uploadToken`,
|
|
592
|
+
* 按七牛云表单上传规范以 `multipart/form-data` 提交。该请求
|
|
593
|
+
* **不会** 携带 PushPlus 的 `access-key` 头。
|
|
594
|
+
*/
|
|
595
|
+
async upload(token, file, options) {
|
|
596
|
+
if (token == null) {
|
|
597
|
+
throw new PushPlusError("\u4E0A\u4F20\u51ED\u8BC1 token \u4E0D\u80FD\u4E3A null");
|
|
598
|
+
}
|
|
599
|
+
if (!token.uploadToken) {
|
|
600
|
+
throw new PushPlusError("\u4E0A\u4F20\u51ED\u8BC1 uploadToken \u4E0D\u80FD\u4E3A\u7A7A");
|
|
601
|
+
}
|
|
602
|
+
const uploadUrl = token.uploadUrl || token.uploadHost;
|
|
603
|
+
if (!uploadUrl) {
|
|
604
|
+
throw new PushPlusError("\u4E0A\u4F20\u51ED\u8BC1\u672A\u8FD4\u56DE uploadUrl/uploadHost");
|
|
605
|
+
}
|
|
606
|
+
return this.uploadToQiniu(uploadUrl, token.uploadToken, file, options);
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* 2. 上传图片到七牛云(低层方法)。直接指定上传地址与 token。
|
|
610
|
+
*/
|
|
611
|
+
async uploadToQiniu(uploadUrl, uploadToken, file, options) {
|
|
612
|
+
var _a, _b;
|
|
613
|
+
if (!uploadUrl) {
|
|
614
|
+
throw new PushPlusError("uploadUrl \u4E0D\u80FD\u4E3A\u7A7A");
|
|
615
|
+
}
|
|
616
|
+
if (!uploadToken) {
|
|
617
|
+
throw new PushPlusError("uploadToken \u4E0D\u80FD\u4E3A\u7A7A");
|
|
618
|
+
}
|
|
619
|
+
const bytes = await toUint8Array(file);
|
|
620
|
+
if (bytes.byteLength === 0) {
|
|
621
|
+
throw new PushPlusError("\u4E0A\u4F20\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
|
|
622
|
+
}
|
|
623
|
+
const fileName = options.fileName || "file";
|
|
624
|
+
const contentType = options.contentType || guessContentTypeByName(fileName) || "application/octet-stream";
|
|
625
|
+
const boundary = "----PushPlusBoundary" + randomBoundarySuffix();
|
|
626
|
+
const body = buildMultipartBody(boundary, uploadToken, fileName, contentType, bytes);
|
|
627
|
+
const resp = await callExecuteRaw(this.http, {
|
|
628
|
+
method: "POST",
|
|
629
|
+
url: uploadUrl,
|
|
630
|
+
headers: { "Content-Type": `multipart/form-data; boundary=${boundary}` },
|
|
631
|
+
body
|
|
632
|
+
});
|
|
633
|
+
if (!isSuccessfulHttpStatus(resp.statusCode)) {
|
|
634
|
+
throw new PushPlusError(
|
|
635
|
+
`\u4E0A\u4F20\u56FE\u7247\u5230\u4E03\u725B\u4E91\u5931\u8D25: status=${resp.statusCode}, body=${resp.body}`,
|
|
636
|
+
resp.statusCode
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
let result;
|
|
640
|
+
try {
|
|
641
|
+
result = JSON.parse(resp.body);
|
|
642
|
+
} catch (e) {
|
|
643
|
+
throw new PushPlusError(
|
|
644
|
+
`\u89E3\u6790\u4E03\u725B\u4E91\u54CD\u5E94\u5931\u8D25: ${e.message}, payload=${resp.body}`,
|
|
645
|
+
-1,
|
|
646
|
+
{ cause: e }
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
if (result == null || typeof result !== "object") {
|
|
650
|
+
throw new PushPlusError(`\u4E03\u725B\u4E91\u8FD4\u56DE\u975E JSON \u5BF9\u8C61: ${resp.body}`);
|
|
651
|
+
}
|
|
652
|
+
if (result.errno !== 0) {
|
|
653
|
+
throw new PushPlusError(
|
|
654
|
+
`\u4E03\u725B\u4E91\u4E0A\u4F20\u5931\u8D25: errno=${result.errno}, msg=${(_a = result.msg) != null ? _a : ""}`,
|
|
655
|
+
(_b = result.errno) != null ? _b : -1
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* 便捷方法:自动获取上传凭证后上传字节数组 / Blob / ArrayBuffer。
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* ```ts
|
|
665
|
+
* await client.image.uploadBytes(buffer, { fileName: 'a.png' });
|
|
666
|
+
* await client.image.uploadBytes(blob, { fileName: 'b.jpg', contentType: 'image/jpeg' });
|
|
667
|
+
* ```
|
|
668
|
+
*/
|
|
669
|
+
async uploadBytes(file, options) {
|
|
670
|
+
const token = await this.getUploadToken();
|
|
671
|
+
return this.upload(token, file, options);
|
|
672
|
+
}
|
|
673
|
+
/** 3. 图片列表。 */
|
|
674
|
+
list(query) {
|
|
675
|
+
return this.executeOpen(
|
|
676
|
+
"POST",
|
|
677
|
+
"/api/open/userImage/list",
|
|
678
|
+
query != null ? query : {}
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* 4. 主动删除图片;未删除的图片默认 30 天后由系统自动清理。
|
|
683
|
+
*/
|
|
684
|
+
async delete(id) {
|
|
685
|
+
await this.executeOpen(
|
|
686
|
+
"DELETE",
|
|
687
|
+
this.appendQuery("/api/open/userImage/delete", { id })
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
async function toUint8Array(file) {
|
|
692
|
+
if (file == null) {
|
|
693
|
+
throw new PushPlusError("\u4E0A\u4F20\u6587\u4EF6\u4E0D\u80FD\u4E3A null");
|
|
694
|
+
}
|
|
695
|
+
if (file instanceof Uint8Array) {
|
|
696
|
+
return file;
|
|
697
|
+
}
|
|
698
|
+
if (file instanceof ArrayBuffer) {
|
|
699
|
+
return new Uint8Array(file);
|
|
700
|
+
}
|
|
701
|
+
if (typeof Blob !== "undefined" && file instanceof Blob) {
|
|
702
|
+
const ab = await file.arrayBuffer();
|
|
703
|
+
return new Uint8Array(ab);
|
|
704
|
+
}
|
|
705
|
+
throw new PushPlusError(`\u4E0D\u652F\u6301\u7684\u4E0A\u4F20\u6587\u4EF6\u7C7B\u578B: ${Object.prototype.toString.call(file)}`);
|
|
706
|
+
}
|
|
707
|
+
function buildMultipartBody(boundary, uploadToken, fileName, contentType, fileBytes) {
|
|
708
|
+
const crlf = "\r\n";
|
|
709
|
+
const enc = new TextEncoder();
|
|
710
|
+
const head = enc.encode(
|
|
711
|
+
`--${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}`
|
|
712
|
+
);
|
|
713
|
+
const tail = enc.encode(`${crlf}--${boundary}--${crlf}`);
|
|
714
|
+
const out = new Uint8Array(head.byteLength + fileBytes.byteLength + tail.byteLength);
|
|
715
|
+
out.set(head, 0);
|
|
716
|
+
out.set(fileBytes, head.byteLength);
|
|
717
|
+
out.set(tail, head.byteLength + fileBytes.byteLength);
|
|
718
|
+
return out;
|
|
719
|
+
}
|
|
720
|
+
function escapeFileName(name) {
|
|
721
|
+
return name.replace(/"/g, "_").replace(/\r/g, " ").replace(/\n/g, " ");
|
|
722
|
+
}
|
|
723
|
+
function guessContentTypeByName(name) {
|
|
724
|
+
const lower = name.toLowerCase();
|
|
725
|
+
if (lower.endsWith(".png")) return "image/png";
|
|
726
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
727
|
+
if (lower.endsWith(".gif")) return "image/gif";
|
|
728
|
+
if (lower.endsWith(".webp")) return "image/webp";
|
|
729
|
+
if (lower.endsWith(".bmp")) return "image/bmp";
|
|
730
|
+
if (lower.endsWith(".svg")) return "image/svg+xml";
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
function randomBoundarySuffix() {
|
|
734
|
+
let s = "";
|
|
735
|
+
for (let i = 0; i < 32; i++) {
|
|
736
|
+
s += Math.floor(Math.random() * 16).toString(16);
|
|
737
|
+
}
|
|
738
|
+
return s;
|
|
739
|
+
}
|
|
740
|
+
|
|
524
741
|
// src/api/message-api.ts
|
|
525
742
|
var MessageApi = class extends AbstractApi {
|
|
526
743
|
constructor(config, http, rateLimitGuard) {
|
|
@@ -920,7 +1137,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
920
1137
|
logRequest: (_g = cfg.logRequest) != null ? _g : false,
|
|
921
1138
|
rateLimitGuardEnabled: (_h = cfg.rateLimitGuardEnabled) != null ? _h : true,
|
|
922
1139
|
rateLimitCooldownMs: (_i = cfg.rateLimitCooldownMs) != null ? _i : 0,
|
|
923
|
-
userAgent: (_j = cfg.userAgent) != null ? _j : `@perk-net/perk-pushplus-sdk/1.
|
|
1140
|
+
userAgent: (_j = cfg.userAgent) != null ? _j : `@perk-net/perk-pushplus-sdk/1.1.0`
|
|
924
1141
|
};
|
|
925
1142
|
}
|
|
926
1143
|
|
|
@@ -1014,6 +1231,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
1014
1231
|
this.clawBot = new ClawBotApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1015
1232
|
this.setting = new SettingApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1016
1233
|
this.pre = new PreApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1234
|
+
this.image = new ImageApi(this.config, this.httpRequester, this.accessKeyManager);
|
|
1017
1235
|
}
|
|
1018
1236
|
/** 与 Java SDK 风格一致的 Builder 入口。 */
|
|
1019
1237
|
static builder() {
|
|
@@ -1160,6 +1378,10 @@ var PerkPushPlus = (function (exports) {
|
|
|
1160
1378
|
this.req.pre = v;
|
|
1161
1379
|
return this;
|
|
1162
1380
|
}
|
|
1381
|
+
pushId(v) {
|
|
1382
|
+
this.req.pushId = v;
|
|
1383
|
+
return this;
|
|
1384
|
+
}
|
|
1163
1385
|
build() {
|
|
1164
1386
|
return { ...this.req };
|
|
1165
1387
|
}
|
|
@@ -1209,6 +1431,10 @@ var PerkPushPlus = (function (exports) {
|
|
|
1209
1431
|
this.req.pre = v;
|
|
1210
1432
|
return this;
|
|
1211
1433
|
}
|
|
1434
|
+
pushId(v) {
|
|
1435
|
+
this.req.pushId = v;
|
|
1436
|
+
return this;
|
|
1437
|
+
}
|
|
1212
1438
|
/** 追加一个 channel。 */
|
|
1213
1439
|
channel(ch) {
|
|
1214
1440
|
this.channelList.push(typeof ch === "string" ? ch : ch);
|
|
@@ -1252,6 +1478,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
1252
1478
|
exports.ErrorCode = ErrorCode;
|
|
1253
1479
|
exports.FetchHttpRequester = FetchHttpRequester;
|
|
1254
1480
|
exports.FriendApi = FriendApi;
|
|
1481
|
+
exports.ImageApi = ImageApi;
|
|
1255
1482
|
exports.MessageApi = MessageApi;
|
|
1256
1483
|
exports.MessageTokenApi = MessageTokenApi;
|
|
1257
1484
|
exports.OpenAbstractApi = OpenAbstractApi;
|
|
@@ -1274,6 +1501,7 @@ var PerkPushPlus = (function (exports) {
|
|
|
1274
1501
|
exports.WebhookType = WebhookType;
|
|
1275
1502
|
exports.WebhookTypeDescription = WebhookTypeDescription;
|
|
1276
1503
|
exports.batchSendRequest = batchSendRequest;
|
|
1504
|
+
exports.callExecuteRaw = callExecuteRaw;
|
|
1277
1505
|
exports.errorCodeFromValue = errorCodeFromValue;
|
|
1278
1506
|
exports.isRateLimitedCode = isRateLimitedCode;
|
|
1279
1507
|
exports.isSuccessfulHttpStatus = isSuccessfulHttpStatus;
|