feat(wechat): add secret codes for morning subscribe and news push
Memind CI / Test, build, and release guards (push) Successful in 4m14s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-20 19:10:27 +08:00
parent 830f8a4011
commit bb6aa75b3b
6 changed files with 148 additions and 0 deletions
+34
View File
@@ -32,6 +32,8 @@ import {
updatePushSubscriptionTime,
} from '../push-subscription-service.mjs';
import { resolveSubscribeMorningLlmIntent } from '../../wechat-subscribe-morning-llm.mjs';
import { formatNewsMorningPushDelivery } from '../push-content/news-morning-delivery.mjs';
import { parsePushSubscriptionSecretCode } from '../push-subscription-secret-codes.mjs';
async function tryCommitSubscription({
userId,
@@ -238,12 +240,44 @@ export async function handlePushSubscriptionMessages({
sourceMessageId = null,
wechatSubscribeMorningLlmConfigService = null,
llmProviderService = null,
mysqlPool = null,
h5Root = '',
env = process.env,
logger = console,
} = {}) {
if (parsePushSubscriptionHelpRequest(text)) {
return buildPushSubscriptionHelpText();
}
const secretCode = parsePushSubscriptionSecretCode(text);
if (secretCode?.kind === 'morning_subscribe') {
const morningItem = getPushSubscriptionByKey('morning');
if (morningItem) {
return tryCommitSubscription({
userId: boundUser?.userId,
scheduleService,
catalogItem: morningItem,
hour: morningItem.defaultHour,
minute: morningItem.defaultMinute,
timezone,
bindUrl,
boundUser,
sourceMessageId,
sourceText: String(text ?? ''),
logger,
});
}
}
if (secretCode?.kind === 'news_immediate') {
const delivery = await formatNewsMorningPushDelivery({
timezone,
h5Root: String(h5Root ?? '').trim() || process.cwd(),
pool: mysqlPool,
env,
});
return delivery.text;
}
if (parsePushSubscriptionListRequest(text)) {
if (!boundUser?.userId || !scheduleService) {
return '绑定账号后可查看订阅状态。';
@@ -154,3 +154,40 @@ test('handlePushSubscriptionMessages returns help menu', async () => {
assert.match(reply, /每日推送订阅/);
assert.match(reply, /10 🎁 每日惊喜/);
});
test('handlePushSubscriptionMessages opens morning subscription on secret code 美丽心情', async () => {
const calls = [];
const reply = await handlePushSubscriptionMessages({
text: '美丽心情',
boundUser: { userId: 'user-1' },
scheduleService: {
async listItems() {
return [];
},
async findActiveItemByMetadataSource() {
return null;
},
async createItem(payload) {
calls.push(['createItem', payload]);
return { id: 'item-morning', ...payload };
},
async createReminder(payload) {
calls.push(['createReminder', payload]);
return { id: 'rem-morning', ...payload };
},
},
});
assert.match(reply, /已开启 ☀️ 早安问候/u);
assert.equal(calls[0][1].metadata.source, 'wechat_push:morning');
assert.equal(calls[0][1].metadata.dailyHour, 8);
});
test('handlePushSubscriptionMessages pushes news digest on secret code 新闻早报', async () => {
const reply = await handlePushSubscriptionMessages({
text: '新闻早报',
env: {},
});
assert.match(reply, /📰 每日新闻早报/u);
});
+26
View File
@@ -0,0 +1,26 @@
/** 微信服务号订阅暗号(精确匹配,忽略首尾与中间空白)。 */
export const PUSH_SUBSCRIPTION_SECRET_CODES = Object.freeze({
morningSubscribe: '美丽心情',
newsImmediate: '新闻早报',
});
function normalizeSecretCodeText(text) {
return String(text ?? '').trim().replace(/\s+/g, '');
}
/**
* 解析订阅暗号。
* @returns {{ kind: 'morning_subscribe' }|{ kind: 'news_immediate' }|null}
*/
export function parsePushSubscriptionSecretCode(text) {
const compact = normalizeSecretCodeText(text);
if (!compact) return null;
if (compact === PUSH_SUBSCRIPTION_SECRET_CODES.morningSubscribe) {
return { kind: 'morning_subscribe' };
}
if (compact === PUSH_SUBSCRIPTION_SECRET_CODES.newsImmediate) {
return { kind: 'news_immediate' };
}
return null;
}
@@ -0,0 +1,24 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
parsePushSubscriptionSecretCode,
PUSH_SUBSCRIPTION_SECRET_CODES,
} from './push-subscription-secret-codes.mjs';
test('parsePushSubscriptionSecretCode matches morning subscribe code', () => {
assert.deepEqual(parsePushSubscriptionSecretCode('美丽心情'), { kind: 'morning_subscribe' });
assert.deepEqual(parsePushSubscriptionSecretCode(' 美丽 心情 '), { kind: 'morning_subscribe' });
});
test('parsePushSubscriptionSecretCode matches news immediate code', () => {
assert.deepEqual(parsePushSubscriptionSecretCode('新闻早报'), { kind: 'news_immediate' });
assert.deepEqual(parsePushSubscriptionSecretCode(' 新闻 早报 '), { kind: 'news_immediate' });
});
test('parsePushSubscriptionSecretCode ignores partial or unrelated text', () => {
assert.equal(parsePushSubscriptionSecretCode('美丽'), null);
assert.equal(parsePushSubscriptionSecretCode('2'), null);
assert.equal(parsePushSubscriptionSecretCode('取消新闻早报'), null);
assert.equal(parsePushSubscriptionSecretCode(PUSH_SUBSCRIPTION_SECRET_CODES.morningSubscribe + '呀'), null);
});