@sovovs/bycli 2.1.49 → 2.1.50
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/cli-manifest.json
CHANGED
|
@@ -29550,6 +29550,51 @@
|
|
|
29550
29550
|
"sourceFile": "weixin/home-overview.js",
|
|
29551
29551
|
"navigateBefore": false
|
|
29552
29552
|
},
|
|
29553
|
+
{
|
|
29554
|
+
"site": "weixin",
|
|
29555
|
+
"name": "open-platform-authorizer-info",
|
|
29556
|
+
"description": "Get an authorized Official Account profile through a Weixin third-party platform",
|
|
29557
|
+
"access": "read",
|
|
29558
|
+
"domain": "api.weixin.qq.com",
|
|
29559
|
+
"strategy": "local",
|
|
29560
|
+
"browser": false,
|
|
29561
|
+
"args": [
|
|
29562
|
+
{
|
|
29563
|
+
"name": "authorizerAppid",
|
|
29564
|
+
"type": "str",
|
|
29565
|
+
"required": true,
|
|
29566
|
+
"positional": true,
|
|
29567
|
+
"help": "Authorized Official Account AppID"
|
|
29568
|
+
},
|
|
29569
|
+
{
|
|
29570
|
+
"name": "component-appid",
|
|
29571
|
+
"type": "str",
|
|
29572
|
+
"required": false,
|
|
29573
|
+
"help": "Third-party platform AppID; falls back to WECHAT_COMPONENT_APPID"
|
|
29574
|
+
},
|
|
29575
|
+
{
|
|
29576
|
+
"name": "component-appsecret",
|
|
29577
|
+
"type": "str",
|
|
29578
|
+
"required": false,
|
|
29579
|
+
"help": "Third-party platform AppSecret; prefer WECHAT_COMPONENT_APPSECRET"
|
|
29580
|
+
},
|
|
29581
|
+
{
|
|
29582
|
+
"name": "component-verify-ticket",
|
|
29583
|
+
"type": "str",
|
|
29584
|
+
"required": false,
|
|
29585
|
+
"help": "Latest pushed verify ticket; prefer WECHAT_COMPONENT_VERIFY_TICKET"
|
|
29586
|
+
}
|
|
29587
|
+
],
|
|
29588
|
+
"columns": [
|
|
29589
|
+
"appid",
|
|
29590
|
+
"nickname",
|
|
29591
|
+
"username",
|
|
29592
|
+
"principal_name"
|
|
29593
|
+
],
|
|
29594
|
+
"type": "js",
|
|
29595
|
+
"modulePath": "weixin/open-platform-authorizer-info.js",
|
|
29596
|
+
"sourceFile": "weixin/open-platform-authorizer-info.js"
|
|
29597
|
+
},
|
|
29553
29598
|
{
|
|
29554
29599
|
"site": "weixin",
|
|
29555
29600
|
"name": "published",
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { CommandExecutionError } from '@sovovs/bycli/errors';
|
|
2
|
+
|
|
3
|
+
const API_BASE = 'https://api.weixin.qq.com/cgi-bin/component';
|
|
4
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
5
|
+
|
|
6
|
+
function text(value) {
|
|
7
|
+
const normalized = String(value ?? '').trim();
|
|
8
|
+
return normalized || null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function redact(message, secrets = []) {
|
|
12
|
+
let redacted = String(message ?? '');
|
|
13
|
+
for (const secret of secrets) {
|
|
14
|
+
const value = text(secret);
|
|
15
|
+
if (!value) continue;
|
|
16
|
+
redacted = redacted.replaceAll(value, '[REDACTED]');
|
|
17
|
+
redacted = redacted.replaceAll(encodeURIComponent(value), '[REDACTED]');
|
|
18
|
+
}
|
|
19
|
+
redacted = redacted.replace(/([?&](?:component_access_token|access_token)=)[^&\s]*/giu, '$1[REDACTED]');
|
|
20
|
+
return redacted;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function postJson(path, body, { query = {}, fetchImpl = globalThis.fetch, secrets = [] } = {}) {
|
|
24
|
+
if (typeof fetchImpl !== 'function') throw new CommandExecutionError('Weixin Open Platform requires fetch support');
|
|
25
|
+
const url = new URL(`${API_BASE}/${path}`);
|
|
26
|
+
for (const [key, value] of Object.entries(query)) url.searchParams.set(key, value);
|
|
27
|
+
let response;
|
|
28
|
+
try {
|
|
29
|
+
response = await fetchImpl(url.toString(), {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
|
32
|
+
body: JSON.stringify(body),
|
|
33
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
34
|
+
});
|
|
35
|
+
} catch {
|
|
36
|
+
throw new CommandExecutionError(`Weixin Open Platform ${path} request failed`);
|
|
37
|
+
}
|
|
38
|
+
let payload;
|
|
39
|
+
try {
|
|
40
|
+
payload = await response.json();
|
|
41
|
+
} catch {
|
|
42
|
+
throw new CommandExecutionError(`Weixin Open Platform ${path} returned invalid JSON`);
|
|
43
|
+
}
|
|
44
|
+
if (!response.ok) throw new CommandExecutionError(`Weixin Open Platform ${path} returned HTTP ${response.status}`);
|
|
45
|
+
if (payload?.errcode != null && payload.errcode !== 0) {
|
|
46
|
+
const message = redact(payload.errmsg || 'unknown error', secrets);
|
|
47
|
+
throw new CommandExecutionError(`Weixin Open Platform ${path} failed (${payload.errcode}): ${message}`);
|
|
48
|
+
}
|
|
49
|
+
return payload;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function getComponentAccessToken({
|
|
53
|
+
componentAppid,
|
|
54
|
+
componentAppsecret,
|
|
55
|
+
componentVerifyTicket,
|
|
56
|
+
fetchImpl = globalThis.fetch,
|
|
57
|
+
} = {}) {
|
|
58
|
+
const payload = await postJson('api_component_token', {
|
|
59
|
+
component_appid: componentAppid,
|
|
60
|
+
component_appsecret: componentAppsecret,
|
|
61
|
+
component_verify_ticket: componentVerifyTicket,
|
|
62
|
+
}, {
|
|
63
|
+
fetchImpl,
|
|
64
|
+
secrets: [componentAppsecret, componentVerifyTicket],
|
|
65
|
+
});
|
|
66
|
+
const accessToken = text(payload?.component_access_token);
|
|
67
|
+
if (!accessToken) {
|
|
68
|
+
throw new CommandExecutionError('Weixin Open Platform api_component_token returned no component access token');
|
|
69
|
+
}
|
|
70
|
+
return accessToken;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function normalizeAuthorizerProfile(payload, requestedAuthorizerAppid) {
|
|
74
|
+
const requested = text(requestedAuthorizerAppid);
|
|
75
|
+
const authorizerInfo = payload?.authorizer_info;
|
|
76
|
+
if (!requested || !authorizerInfo || typeof authorizerInfo !== 'object' || Array.isArray(authorizerInfo)) {
|
|
77
|
+
throw new CommandExecutionError('Weixin Open Platform returned an incomplete account profile');
|
|
78
|
+
}
|
|
79
|
+
const responseAppid = text(payload?.authorization_info?.authorizer_appid)
|
|
80
|
+
?? text(payload?.authorization_info?.authorization_appid);
|
|
81
|
+
if (responseAppid && responseAppid !== requested) {
|
|
82
|
+
throw new CommandExecutionError('Weixin Open Platform authorizer AppID did not match the request');
|
|
83
|
+
}
|
|
84
|
+
const nickname = text(authorizerInfo.nick_name);
|
|
85
|
+
const username = text(authorizerInfo.user_name);
|
|
86
|
+
const principalName = text(authorizerInfo.principal_name);
|
|
87
|
+
if (!nickname || !username || !principalName) {
|
|
88
|
+
throw new CommandExecutionError('Weixin Open Platform returned an incomplete account profile');
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
appid: requested,
|
|
92
|
+
nickname,
|
|
93
|
+
username,
|
|
94
|
+
principal_name: principalName,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function fetchAuthorizerProfile({
|
|
99
|
+
componentAppid,
|
|
100
|
+
componentAccessToken,
|
|
101
|
+
authorizerAppid,
|
|
102
|
+
fetchImpl = globalThis.fetch,
|
|
103
|
+
} = {}) {
|
|
104
|
+
const payload = await postJson('api_get_authorizer_info', {
|
|
105
|
+
component_appid: componentAppid,
|
|
106
|
+
authorizer_appid: authorizerAppid,
|
|
107
|
+
}, {
|
|
108
|
+
query: { component_access_token: componentAccessToken },
|
|
109
|
+
fetchImpl,
|
|
110
|
+
secrets: [componentAccessToken],
|
|
111
|
+
});
|
|
112
|
+
return normalizeAuthorizerProfile(payload, authorizerAppid);
|
|
113
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ArgumentError } from '@sovovs/bycli/errors';
|
|
2
|
+
import { cli, Strategy } from '@sovovs/bycli/registry';
|
|
3
|
+
import { fetchAuthorizerProfile, getComponentAccessToken } from './_wechat/open-platform-api.js';
|
|
4
|
+
|
|
5
|
+
function text(value) {
|
|
6
|
+
const normalized = String(value ?? '').trim();
|
|
7
|
+
return normalized || null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function readOpenPlatformCredentials(args = {}, env = process.env) {
|
|
11
|
+
const componentAppid = text(args['component-appid']) ?? text(env.WECHAT_COMPONENT_APPID);
|
|
12
|
+
const componentAppsecret = text(args['component-appsecret']) ?? text(env.WECHAT_COMPONENT_APPSECRET);
|
|
13
|
+
const componentVerifyTicket = text(args['component-verify-ticket']) ?? text(env.WECHAT_COMPONENT_VERIFY_TICKET);
|
|
14
|
+
if (!componentAppid || !componentAppsecret || !componentVerifyTicket) {
|
|
15
|
+
throw new ArgumentError('Weixin Open Platform requires component AppID, AppSecret, and verify ticket');
|
|
16
|
+
}
|
|
17
|
+
return { componentAppid, componentAppsecret, componentVerifyTicket };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const openPlatformAuthorizerInfoCommand = cli({
|
|
21
|
+
site: 'weixin',
|
|
22
|
+
name: 'open-platform-authorizer-info',
|
|
23
|
+
access: 'read',
|
|
24
|
+
domain: 'api.weixin.qq.com',
|
|
25
|
+
description: 'Get an authorized Official Account profile through a Weixin third-party platform',
|
|
26
|
+
strategy: Strategy.LOCAL,
|
|
27
|
+
browser: false,
|
|
28
|
+
args: [
|
|
29
|
+
{
|
|
30
|
+
name: 'authorizerAppid',
|
|
31
|
+
positional: true,
|
|
32
|
+
required: true,
|
|
33
|
+
help: 'Authorized Official Account AppID',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'component-appid',
|
|
37
|
+
help: 'Third-party platform AppID; falls back to WECHAT_COMPONENT_APPID',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'component-appsecret',
|
|
41
|
+
help: 'Third-party platform AppSecret; prefer WECHAT_COMPONENT_APPSECRET',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'component-verify-ticket',
|
|
45
|
+
help: 'Latest pushed verify ticket; prefer WECHAT_COMPONENT_VERIFY_TICKET',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
columns: ['appid', 'nickname', 'username', 'principal_name'],
|
|
49
|
+
func: async args => {
|
|
50
|
+
const authorizerAppid = text(args.authorizerAppid);
|
|
51
|
+
if (!authorizerAppid) throw new ArgumentError('authorizerAppid is required');
|
|
52
|
+
const credentials = readOpenPlatformCredentials(args);
|
|
53
|
+
const componentAccessToken = await getComponentAccessToken(credentials);
|
|
54
|
+
const profile = await fetchAuthorizerProfile({
|
|
55
|
+
componentAppid: credentials.componentAppid,
|
|
56
|
+
componentAccessToken,
|
|
57
|
+
authorizerAppid,
|
|
58
|
+
});
|
|
59
|
+
return [profile];
|
|
60
|
+
},
|
|
61
|
+
});
|