830f8a4011
Memind CI / Test, build, and release guards (push) Successful in 5m36s
Ship the 10-item numeric subscription loop on WeChat MP, wire schedule delivery for news/weather/quote/health/finance/tech/knowledge/night/surprise pushes, and extend news morning draft cover generation plus a one-shot draft push script. Co-authored-by: Cursor <cursoragent@cursor.com>
185 lines
4.7 KiB
JavaScript
185 lines
4.7 KiB
JavaScript
/** 微信服务号数字订阅目录(仅限服务号客服消息推送)。 */
|
|
|
|
export const WECHAT_PUSH_SUBSCRIPTION_PREFIX = 'wechat_push';
|
|
export const LEGACY_MORNING_SOURCE = 'subscribe_morning_reminder';
|
|
|
|
export const PUSH_SUBSCRIPTION_ITEMS = Object.freeze([
|
|
{
|
|
id: 1,
|
|
key: 'morning',
|
|
emoji: '☀️',
|
|
label: '早安问候',
|
|
defaultHour: 8,
|
|
defaultMinute: 0,
|
|
aliases: ['早安', '问候'],
|
|
deliveryReady: true,
|
|
},
|
|
{
|
|
id: 2,
|
|
key: 'news',
|
|
emoji: '📰',
|
|
label: '每日新闻早报',
|
|
defaultHour: 7,
|
|
defaultMinute: 30,
|
|
aliases: ['新闻', '早报', '新闻早报'],
|
|
deliveryReady: true,
|
|
},
|
|
{
|
|
id: 3,
|
|
key: 'weather',
|
|
emoji: '🌤',
|
|
label: '每日天气',
|
|
defaultHour: 7,
|
|
defaultMinute: 0,
|
|
aliases: ['天气'],
|
|
deliveryReady: true,
|
|
requiresOptions: ['city'],
|
|
},
|
|
{
|
|
id: 4,
|
|
key: 'finance',
|
|
emoji: '💰',
|
|
label: '财经市场',
|
|
defaultHour: 8,
|
|
defaultMinute: 30,
|
|
aliases: ['财经', '股市', '市场'],
|
|
deliveryReady: true,
|
|
},
|
|
{
|
|
id: 5,
|
|
key: 'tech',
|
|
emoji: '🤖',
|
|
label: 'AI / 科技早报',
|
|
defaultHour: 8,
|
|
defaultMinute: 0,
|
|
aliases: ['科技', 'AI', 'ai'],
|
|
deliveryReady: true,
|
|
},
|
|
{
|
|
id: 6,
|
|
key: 'knowledge',
|
|
emoji: '📚',
|
|
label: '每日知识',
|
|
defaultHour: 12,
|
|
defaultMinute: 0,
|
|
aliases: ['知识'],
|
|
deliveryReady: true,
|
|
},
|
|
{
|
|
id: 7,
|
|
key: 'quote',
|
|
emoji: '💡',
|
|
label: '每日金句',
|
|
defaultHour: 7,
|
|
defaultMinute: 30,
|
|
aliases: ['金句', '名言'],
|
|
deliveryReady: true,
|
|
},
|
|
{
|
|
id: 8,
|
|
key: 'health',
|
|
emoji: '🏃',
|
|
label: '健康提醒',
|
|
defaultHour: 10,
|
|
defaultMinute: 0,
|
|
aliases: ['健康'],
|
|
deliveryReady: true,
|
|
},
|
|
{
|
|
id: 9,
|
|
key: 'night',
|
|
emoji: '🌙',
|
|
label: '晚安总结',
|
|
defaultHour: 22,
|
|
defaultMinute: 0,
|
|
aliases: ['晚安'],
|
|
deliveryReady: true,
|
|
},
|
|
{
|
|
id: 10,
|
|
key: 'surprise',
|
|
emoji: '🎁',
|
|
label: '每日惊喜',
|
|
defaultHour: 18,
|
|
defaultMinute: 0,
|
|
aliases: ['惊喜'],
|
|
deliveryReady: true,
|
|
},
|
|
]);
|
|
|
|
const byId = new Map(PUSH_SUBSCRIPTION_ITEMS.map((item) => [item.id, item]));
|
|
const byKey = new Map(PUSH_SUBSCRIPTION_ITEMS.map((item) => [item.key, item]));
|
|
|
|
export function pushSubscriptionMetadataSource(key) {
|
|
return `${WECHAT_PUSH_SUBSCRIPTION_PREFIX}:${String(key ?? '').trim()}`;
|
|
}
|
|
|
|
export function getPushSubscriptionById(id) {
|
|
const num = Number(id);
|
|
if (!Number.isInteger(num)) return null;
|
|
return byId.get(num) ?? null;
|
|
}
|
|
|
|
export function getPushSubscriptionByKey(key) {
|
|
const normalized = String(key ?? '').trim().toLowerCase();
|
|
if (!normalized) return null;
|
|
return byKey.get(normalized) ?? null;
|
|
}
|
|
|
|
export function resolvePushSubscriptionFromMetadataSource(source) {
|
|
const raw = String(source ?? '').trim();
|
|
if (!raw) return null;
|
|
if (raw === LEGACY_MORNING_SOURCE) {
|
|
return getPushSubscriptionByKey('morning');
|
|
}
|
|
const prefix = `${WECHAT_PUSH_SUBSCRIPTION_PREFIX}:`;
|
|
if (!raw.startsWith(prefix)) return null;
|
|
return getPushSubscriptionByKey(raw.slice(prefix.length));
|
|
}
|
|
|
|
export function resolvePushSubscriptionFromAlias(text) {
|
|
const compact = String(text ?? '').replace(/\s+/g, '');
|
|
if (!compact) return null;
|
|
for (const item of PUSH_SUBSCRIPTION_ITEMS) {
|
|
for (const alias of item.aliases) {
|
|
if (compact.includes(alias)) return item;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function formatPushSubscriptionTimeLabel(hour, minute) {
|
|
const safeHour = Number(hour);
|
|
const safeMinute = Number(minute ?? 0);
|
|
return `${String(safeHour).padStart(2, '0')}:${String(safeMinute).padStart(2, '0')}`;
|
|
}
|
|
|
|
export function formatPushSubscriptionMenuLine(item) {
|
|
const timeLabel = formatPushSubscriptionTimeLabel(item.defaultHour, item.defaultMinute);
|
|
return `${item.id} ${item.emoji} ${item.label} 默认 ${timeLabel}`;
|
|
}
|
|
|
|
export function buildPushSubscriptionMenuLines() {
|
|
const lines = [
|
|
'',
|
|
'📬 每日推送订阅(仅限本服务号)',
|
|
'回复数字即可开通,例如「2」或「2 7点」:',
|
|
...PUSH_SUBSCRIPTION_ITEMS.map((item) => formatPushSubscriptionMenuLine(item)),
|
|
'',
|
|
'· 查状态:回复「我的订阅」',
|
|
'· 改时间:回复「2改到7点」',
|
|
'· 取消:回复「取消2」',
|
|
'· 天气城市:回复「3 北京」或「3改城市上海」',
|
|
'· 帮助:回复「订阅帮助」',
|
|
];
|
|
return lines;
|
|
}
|
|
|
|
export function isPushSubscriptionEnabled(env = process.env) {
|
|
const explicit = env.H5_WECHAT_MP_PUSH_SUBSCRIPTION_ENABLED;
|
|
if (explicit != null && String(explicit).trim() !== '') {
|
|
return String(explicit).trim() !== '0';
|
|
}
|
|
return String(env.H5_WECHAT_MP_SUBSCRIBE_MORNING_REMINDER_ENABLED ?? '1').trim() !== '0';
|
|
}
|