koishi-plugin-rocom 1.0.4 → 1.0.5
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/lib/client.js +1 -1
- package/lib/commands/account.js +41 -3
- package/lib/commands/admin.js +27 -5
- package/lib/commands/merchant.js +43 -28
- package/lib/index.js +145 -3815
- package/lib/render-templates/yuanxing-shangren/index.html +29 -29
- package/lib/subscription-send.d.ts +2 -1
- package/lib/subscription-send.js +13 -0
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -126,7 +126,7 @@ class RocomClient {
|
|
|
126
126
|
stack: err?.stack,
|
|
127
127
|
} : undefined,
|
|
128
128
|
};
|
|
129
|
-
|
|
129
|
+
logger.warn(`${method} ${path} detailed failure\n${this.stringifyForLog(details)}`);
|
|
130
130
|
}
|
|
131
131
|
async get(ctx, path, headers, params, options) {
|
|
132
132
|
try {
|
package/lib/commands/account.js
CHANGED
|
@@ -161,9 +161,26 @@ function register(deps) {
|
|
|
161
161
|
.action(async ({ session }, index) => {
|
|
162
162
|
if (!index)
|
|
163
163
|
return '用法:洛克.切换 <序号>';
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
const userId = session.userId;
|
|
165
|
+
if (!userMgr.switchPrimary(userId, index))
|
|
166
|
+
return '序号无效。';
|
|
167
|
+
const newPrimary = userMgr.getPrimaryBinding(userId);
|
|
168
|
+
if (newPrimary?.binding_id) {
|
|
169
|
+
const token = await (0, role_token_1.getRoleToken)(ctx, userId);
|
|
170
|
+
if (token && token.bindingId !== newPrimary.binding_id) {
|
|
171
|
+
const res = await client.refreshBinding(ctx, newPrimary.binding_id, userId);
|
|
172
|
+
if (res?.framework_token) {
|
|
173
|
+
await (0, role_token_1.upsertRoleToken)(ctx, {
|
|
174
|
+
userId,
|
|
175
|
+
fwt: res.framework_token,
|
|
176
|
+
bindingId: newPrimary.binding_id,
|
|
177
|
+
roleId: newPrimary.role_id,
|
|
178
|
+
loginType: newPrimary.login_type,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return `成功切换到序号 ${index} 账号:${newPrimary?.nickname || '未知'}`;
|
|
167
184
|
});
|
|
168
185
|
ctx.command('洛克').subcommand('.解绑 <index:number>', '解绑账号')
|
|
169
186
|
.alias('洛克解绑')
|
|
@@ -184,6 +201,27 @@ function register(deps) {
|
|
|
184
201
|
if (userMgr.getUserBindings(session.userId).length === 0) {
|
|
185
202
|
await (0, role_token_1.removeRoleToken)(ctx, session.userId);
|
|
186
203
|
}
|
|
204
|
+
else {
|
|
205
|
+
const token = await (0, role_token_1.getRoleToken)(ctx, session.userId);
|
|
206
|
+
if (token?.bindingId === removed.binding_id) {
|
|
207
|
+
const newPrimary = userMgr.getPrimaryBinding(session.userId);
|
|
208
|
+
if (newPrimary?.binding_id) {
|
|
209
|
+
const res = await client.refreshBinding(ctx, newPrimary.binding_id, session.userId);
|
|
210
|
+
if (res?.framework_token) {
|
|
211
|
+
await (0, role_token_1.upsertRoleToken)(ctx, {
|
|
212
|
+
userId: session.userId,
|
|
213
|
+
fwt: res.framework_token,
|
|
214
|
+
bindingId: newPrimary.binding_id,
|
|
215
|
+
roleId: newPrimary.role_id,
|
|
216
|
+
loginType: newPrimary.login_type,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
await (0, role_token_1.removeRoleToken)(ctx, session.userId);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
187
225
|
return `已解绑账号:${removed.nickname}`;
|
|
188
226
|
});
|
|
189
227
|
ctx.command('洛克').subcommand('.刷新', '刷新当前主账号凭证')
|
package/lib/commands/admin.js
CHANGED
|
@@ -53,19 +53,33 @@ function register(deps) {
|
|
|
53
53
|
const allUsers = userMgr.getAllUsersBindings();
|
|
54
54
|
let totalInvalid = 0;
|
|
55
55
|
let totalValid = 0;
|
|
56
|
+
let totalSkipped = 0;
|
|
56
57
|
for (const [userId, bindings] of Object.entries(allUsers)) {
|
|
57
58
|
const token = await (0, role_token_1.getRoleToken)(ctx, userId);
|
|
58
59
|
if (!token?.fwt) {
|
|
59
60
|
logger.warn(`用户 ${userId} 没有可用的 fwt,跳过失效检测`);
|
|
61
|
+
totalSkipped += bindings.length;
|
|
60
62
|
continue;
|
|
61
63
|
}
|
|
62
|
-
const fwToken = token.fwt;
|
|
63
64
|
const valid = [];
|
|
64
65
|
for (const binding of bindings) {
|
|
65
|
-
const
|
|
66
|
+
const refreshRes = binding.binding_id
|
|
67
|
+
? await client.refreshBinding(ctx, binding.binding_id, userId)
|
|
68
|
+
: null;
|
|
69
|
+
const fwt = refreshRes?.framework_token || token.fwt;
|
|
70
|
+
const roleRes = await client.getRole(ctx, fwt, undefined, userId);
|
|
66
71
|
if (roleRes?.role) {
|
|
67
72
|
valid.push(binding);
|
|
68
73
|
totalValid++;
|
|
74
|
+
if (refreshRes?.framework_token) {
|
|
75
|
+
await (0, role_token_1.upsertRoleToken)(ctx, {
|
|
76
|
+
userId,
|
|
77
|
+
fwt: refreshRes.framework_token,
|
|
78
|
+
bindingId: binding.binding_id,
|
|
79
|
+
roleId: binding.role_id,
|
|
80
|
+
loginType: binding.login_type,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
69
83
|
continue;
|
|
70
84
|
}
|
|
71
85
|
if (binding.binding_id) {
|
|
@@ -83,16 +97,24 @@ function register(deps) {
|
|
|
83
97
|
await (0, role_token_1.removeRoleToken)(ctx, userId);
|
|
84
98
|
}
|
|
85
99
|
}
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
const parts = [`清理完成:移除 ${totalInvalid} 个无效绑定,剩余 ${totalValid} 个有效绑定。`];
|
|
101
|
+
if (totalSkipped)
|
|
102
|
+
parts.push(`跳过 ${totalSkipped} 个(无可用凭证)。`);
|
|
103
|
+
return totalInvalid > 0 || totalSkipped > 0
|
|
104
|
+
? parts.join('')
|
|
88
105
|
: `所有绑定均有效,无需清理。共 ${totalValid} 个有效绑定。`;
|
|
89
106
|
});
|
|
90
107
|
if (config.autoRefreshEnabled) {
|
|
108
|
+
let lastRefreshKey = '';
|
|
91
109
|
ctx.setInterval(async () => {
|
|
92
110
|
const now = new Date();
|
|
93
111
|
const timeStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
|
|
94
112
|
if (!config.autoRefreshTime.includes(timeStr))
|
|
95
113
|
return;
|
|
114
|
+
const refreshKey = `${now.toDateString()}-${timeStr}`;
|
|
115
|
+
if (refreshKey === lastRefreshKey)
|
|
116
|
+
return;
|
|
117
|
+
lastRefreshKey = refreshKey;
|
|
96
118
|
const allUsers = userMgr.getAllUsersBindings();
|
|
97
119
|
for (const [userId] of Object.entries(allUsers)) {
|
|
98
120
|
const binding = userMgr.getPrimaryBinding(userId);
|
|
@@ -114,6 +136,6 @@ function register(deps) {
|
|
|
114
136
|
logger.warn(`自动刷新用户 ${userId} 失败: ${e}`);
|
|
115
137
|
}
|
|
116
138
|
}
|
|
117
|
-
},
|
|
139
|
+
}, 30000);
|
|
118
140
|
}
|
|
119
141
|
}
|
package/lib/commands/merchant.js
CHANGED
|
@@ -28,17 +28,25 @@ function formatProductWindow(product) {
|
|
|
28
28
|
const end = normalizeTimestamp(product?.end_time);
|
|
29
29
|
if (!start && !end)
|
|
30
30
|
return '';
|
|
31
|
+
const formatDate = (timestamp) => {
|
|
32
|
+
const date = new Date(timestamp);
|
|
33
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
34
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
35
|
+
return `${month}-${day}`;
|
|
36
|
+
};
|
|
31
37
|
const formatTime = (timestamp) => {
|
|
32
38
|
const date = new Date(timestamp);
|
|
33
39
|
const hour = String(date.getHours()).padStart(2, '0');
|
|
34
40
|
const minute = String(date.getMinutes()).padStart(2, '0');
|
|
35
41
|
return `${hour}:${minute}`;
|
|
36
42
|
};
|
|
37
|
-
if (start && end)
|
|
38
|
-
|
|
43
|
+
if (start && end) {
|
|
44
|
+
const datePart = formatDate(start);
|
|
45
|
+
return `${datePart} ${formatTime(start)} – ${formatTime(end)}`;
|
|
46
|
+
}
|
|
39
47
|
if (start)
|
|
40
|
-
return `${formatTime(start)}+`;
|
|
41
|
-
return `${formatTime(end)}-`;
|
|
48
|
+
return `${formatDate(start)} ${formatTime(start)}+`;
|
|
49
|
+
return `${formatDate(end)} ${formatTime(end)}-`;
|
|
42
50
|
}
|
|
43
51
|
function getMerchantActivity(res) {
|
|
44
52
|
const activities = res?.merchantActivities || res?.merchant_activities || [];
|
|
@@ -83,6 +91,7 @@ function getCurrentMerchantRound() {
|
|
|
83
91
|
].join('-');
|
|
84
92
|
return {
|
|
85
93
|
current: currentRound,
|
|
94
|
+
total: rounds.length,
|
|
86
95
|
countdown: `${hours}\u5c0f\u65f6${mins}\u5206\u949f`,
|
|
87
96
|
is_open: currentRound !== null,
|
|
88
97
|
round_id: `${datePart}-${currentRound || 'closed'}`,
|
|
@@ -114,14 +123,37 @@ function isBotAdmin(session, adminUserIds) {
|
|
|
114
123
|
function sameStringArray(left, right) {
|
|
115
124
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
116
125
|
}
|
|
126
|
+
function buildMerchantRenderPayload(res) {
|
|
127
|
+
const products = getActiveProducts(res);
|
|
128
|
+
const roundInfo = getCurrentMerchantRound();
|
|
129
|
+
const activity = getMerchantActivity(res);
|
|
130
|
+
const data = {
|
|
131
|
+
background: '',
|
|
132
|
+
title: activity.name || TEXT.merchant,
|
|
133
|
+
subtitle: activity.start_date || '\u6bcf\u65e5 08:00 / 12:00 / 16:00 / 20:00 \u5237\u65b0',
|
|
134
|
+
titleIcon: true,
|
|
135
|
+
product_count: products.length,
|
|
136
|
+
round_info: roundInfo,
|
|
137
|
+
products: products.map((p) => ({
|
|
138
|
+
name: p.name || TEXT.unknown,
|
|
139
|
+
image: p.icon_url || '',
|
|
140
|
+
time_label: formatProductWindow(p),
|
|
141
|
+
})),
|
|
142
|
+
};
|
|
143
|
+
const fallback = products.length
|
|
144
|
+
? `\u8fdc\u884c\u5546\u4eba\u5f53\u524d\u5546\u54c1\uff1a${products.map((p) => p.name || TEXT.unknown).join('\u3001')}\n\u8f6e\u6b21\uff1a${roundInfo.current || TEXT.notOpen}\n\u5269\u4f59\uff1a${roundInfo.countdown}`
|
|
145
|
+
: '\u5f53\u524d\u8fdc\u884c\u5546\u4eba\u6682\u65e0\u5546\u54c1\u3002';
|
|
146
|
+
return { products, roundInfo, data, fallback };
|
|
147
|
+
}
|
|
117
148
|
async function checkMerchantSubscriptions(deps) {
|
|
118
|
-
const { ctx, client, merchantSubMgr } = deps;
|
|
149
|
+
const { ctx, client, merchantSubMgr, renderer, config } = deps;
|
|
119
150
|
const res = await client.getMerchantInfo(ctx, true);
|
|
120
151
|
if (!res)
|
|
121
152
|
return { subscriptions: 0, matched: 0, pushed: 0 };
|
|
122
|
-
const products =
|
|
153
|
+
const { products, roundInfo, data, fallback } = buildMerchantRenderPayload(res);
|
|
123
154
|
const productNames = products.map((p) => p.name || '').filter(Boolean);
|
|
124
|
-
const
|
|
155
|
+
const rendered = await renderer.renderHtml(ctx, 'yuanxing-shangren', data);
|
|
156
|
+
const renderedPng = rendered ? (0, send_image_1.compressPngImage)(rendered, config) : null;
|
|
125
157
|
const subs = merchantSubMgr.getAll();
|
|
126
158
|
let matchedCount = 0;
|
|
127
159
|
let pushedCount = 0;
|
|
@@ -139,12 +171,13 @@ async function checkMerchantSubscriptions(deps) {
|
|
|
139
171
|
logger.warn(`\u63a8\u9001\u5931\u8d25 ${key}: \u65e0\u6cd5\u786e\u5b9a\u5e73\u53f0\u6216\u9891\u9053`);
|
|
140
172
|
continue;
|
|
141
173
|
}
|
|
142
|
-
const
|
|
174
|
+
const fallbackText = `${msg}\n${fallback}`;
|
|
175
|
+
const sent = await (0, subscription_send_1.sendScheduledImageWithFallback)(ctx, {
|
|
143
176
|
platform,
|
|
144
177
|
channelId,
|
|
145
178
|
guildId: sub.group_id || '',
|
|
146
179
|
userId: sub.user_id || '',
|
|
147
|
-
}, sub.mention_all
|
|
180
|
+
}, renderedPng, fallbackText, !!sub.mention_all);
|
|
148
181
|
if (!sent)
|
|
149
182
|
continue;
|
|
150
183
|
pushedCount++;
|
|
@@ -163,25 +196,7 @@ function register(deps) {
|
|
|
163
196
|
const res = await client.getMerchantInfo(ctx, true);
|
|
164
197
|
if (!res)
|
|
165
198
|
return '\u83b7\u53d6\u8fdc\u884c\u5546\u4eba\u6570\u636e\u5931\u8d25\u3002';
|
|
166
|
-
const
|
|
167
|
-
const roundInfo = getCurrentMerchantRound();
|
|
168
|
-
const activity = getMerchantActivity(res);
|
|
169
|
-
const data = {
|
|
170
|
-
background: '',
|
|
171
|
-
title: activity.name || TEXT.merchant,
|
|
172
|
-
subtitle: activity.start_date || '\u6bcf\u65e5 08:00 / 12:00 / 16:00 / 20:00 \u5237\u65b0',
|
|
173
|
-
titleIcon: '',
|
|
174
|
-
product_count: products.length,
|
|
175
|
-
round_info: roundInfo,
|
|
176
|
-
products: products.map((p) => ({
|
|
177
|
-
name: p.name || TEXT.unknown,
|
|
178
|
-
image: p.icon_url || '',
|
|
179
|
-
time_label: formatProductWindow(p),
|
|
180
|
-
})),
|
|
181
|
-
};
|
|
182
|
-
const fallback = products.length
|
|
183
|
-
? `\u8fdc\u884c\u5546\u4eba\u5f53\u524d\u5546\u54c1\uff1a${products.map((p) => p.name || TEXT.unknown).join('\u3001')}\n\u8f6e\u6b21\uff1a${roundInfo.current || TEXT.notOpen}\n\u5269\u4f59\uff1a${roundInfo.countdown}`
|
|
184
|
-
: '\u5f53\u524d\u8fdc\u884c\u5546\u4eba\u6682\u65e0\u5546\u54c1\u3002';
|
|
199
|
+
const { data, fallback } = buildMerchantRenderPayload(res);
|
|
185
200
|
const png = await deps.renderer.renderHtml(ctx, 'yuanxing-shangren', data);
|
|
186
201
|
await (0, send_image_1.sendImageWithFallback)(session, png, fallback, 'merchant:yuanxing-shangren', deps.config);
|
|
187
202
|
});
|