diff --git a/wechat-mp-config.mjs b/wechat-mp-config.mjs index 6e1c43b..c630e9f 100644 --- a/wechat-mp-config.mjs +++ b/wechat-mp-config.mjs @@ -4,6 +4,8 @@ const DEFAULT_STATUS_TEXT = '我在这边。上一条如果还没完成,我会继续把结果发给你;你也可以直接补一句要求。'; const DEFAULT_UNSUPPORTED_TEXT = '当前先支持文字消息,你可以直接发文字给我。'; const DEFAULT_UNBOUND_TEXT = '先点这里完成绑定,再继续和专属 Agent 对话:'; +const DEFAULT_SUBSCRIBE_INTRO_URL = + 'https://m.tkmind.cn/MindSpace/a70ff537-8908-486e-9b6c-042e07cc25db/public/tkmind-deep-intro.html'; const DEFAULT_PROGRESS_DELAY_MS = 8000; const DEFAULT_SESSION_IDLE_ROTATE_MS = 30 * 60 * 1000; const DEFAULT_SESSION_MESSAGE_ROTATE_COUNT = 200; @@ -69,6 +71,8 @@ export function loadWechatMpConfig(env = process.env) { statusText: env.H5_WECHAT_MP_STATUS_TEXT?.trim() || DEFAULT_STATUS_TEXT, unsupportedText: env.H5_WECHAT_MP_UNSUPPORTED_TEXT?.trim() || DEFAULT_UNSUPPORTED_TEXT, unboundTextPrefix: env.H5_WECHAT_MP_UNBOUND_TEXT_PREFIX?.trim() || DEFAULT_UNBOUND_TEXT, + subscribeIntroUrl: + env.H5_WECHAT_MP_SUBSCRIBE_INTRO_URL?.trim() || DEFAULT_SUBSCRIBE_INTRO_URL, tokenUrl: env.H5_WECHAT_MP_TOKEN_URL?.trim() || DEFAULT_WECHAT_TOKEN_URL, customerServiceUrl: env.H5_WECHAT_MP_CUSTOMER_SERVICE_URL?.trim() || DEFAULT_WECHAT_CUSTOMER_SERVICE_URL, diff --git a/wechat-mp.mjs b/wechat-mp.mjs index 039a8ca..89b1f23 100644 --- a/wechat-mp.mjs +++ b/wechat-mp.mjs @@ -42,6 +42,7 @@ import { handleWechatIntentTransaction } from './wechat/handlers/intent-transact import { handleWechatScheduledTaskIntent } from './wechat/handlers/scheduled-task.mjs'; import { buildStatusText, + buildSubscribeWelcomeText, resolveSyncReply, shouldHandleSyncReply, } from './wechat/handlers/sync-replies.mjs'; @@ -3768,7 +3769,10 @@ export function createWechatMpService({ body: buildWechatTextReply({ toUserName: inbound.fromUserName, fromUserName: inbound.toUserName, - content: `${config.unboundTextPrefix}\n${buildBindUrl()}`, + content: buildSubscribeWelcomeText({ + bindUrl: buildBindUrl(), + introUrl: config.subscribeIntroUrl, + }), }), }; } diff --git a/wechat/handlers/sync-replies.mjs b/wechat/handlers/sync-replies.mjs index 92b2a6d..bc73c2b 100644 --- a/wechat/handlers/sync-replies.mjs +++ b/wechat/handlers/sync-replies.mjs @@ -1,5 +1,42 @@ import { resolveWechatAddressName } from '../user/display-name.mjs'; +export const DEFAULT_SUBSCRIBE_INTRO_URL = + 'https://m.tkmind.cn/MindSpace/a70ff537-8908-486e-9b6c-042e07cc25db/public/tkmind-deep-intro.html'; + +/** 103 生产默认绑定入口:{H5_PUBLIC_BASE_URL}/auth/wechat/authorize?intent=login */ +export const DEFAULT_PRODUCTION_BIND_URL = + 'https://m.tkmind.cn/auth/wechat/authorize?intent=login'; + +export function buildWechatTextLink(href, label) { + const normalizedHref = String(href ?? '').trim(); + const normalizedLabel = String(label ?? '').trim(); + if (!normalizedHref) return normalizedLabel; + if (!normalizedLabel) return normalizedHref; + return `${normalizedLabel}`; +} + +export function buildSubscribeWelcomeText({ bindUrl, introUrl = DEFAULT_SUBSCRIBE_INTRO_URL } = {}) { + const normalizedBindUrl = String(bindUrl ?? '').trim(); + const normalizedIntroUrl = String(introUrl ?? '').trim(); + const lines = [ + '欢迎关注 TKMind 智趣 👋', + '', + '在微信里直接发消息即可:', + '· 问答、写作、翻译', + '· 生成精美网页并给链接', + '· 发图解读报告/截图', + '· 设置定时提醒与每日推送', + ]; + if (normalizedIntroUrl) { + lines.push('', '📖 了解更多:', buildWechatTextLink(normalizedIntroUrl, '点击链接')); + } + if (normalizedBindUrl) { + lines.push('', '👉 先完成绑定再开始对话:', buildWechatTextLink(normalizedBindUrl, '点我绑定')); + } + lines.push('', '绑定后回复「你好」,或试试:', '「帮我做一个读书笔记页面」'); + return lines.join('\n'); +} + export function buildGreetingText(user) { const name = resolveWechatAddressName(user); return name ? `你好,${name}!我在呢,有什么需要?` : '你好!我在呢,有什么需要?'; diff --git a/wechat/handlers/sync-replies.test.mjs b/wechat/handlers/sync-replies.test.mjs new file mode 100644 index 0000000..2dd1f59 --- /dev/null +++ b/wechat/handlers/sync-replies.test.mjs @@ -0,0 +1,45 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +import { + buildSubscribeWelcomeText, + buildWechatTextLink, + DEFAULT_PRODUCTION_BIND_URL, + DEFAULT_SUBSCRIBE_INTRO_URL, +} from './sync-replies.mjs'; + +test('buildWechatTextLink renders WeChat anchor without exposing raw url', () => { + assert.equal( + buildWechatTextLink(DEFAULT_PRODUCTION_BIND_URL, '点我绑定'), + `点我绑定`, + ); +}); + +test('buildSubscribeWelcomeText uses clickable anchors instead of raw urls', () => { + const text = buildSubscribeWelcomeText({ bindUrl: DEFAULT_PRODUCTION_BIND_URL }); + + assert.match(text, /欢迎关注 TKMind 智趣/); + assert.match(text, /生成精美网页并给链接/); + assert.match(text, /📖 了解更多:/); + assert.match( + text, + /点击链接<\/a>/, + ); + assert.match(text, /👉 先完成绑定再开始对话:/); + assert.match( + text, + /点我绑定<\/a>/, + ); + assert.doesNotMatch(text, /\nhttps:\/\/m\.tkmind\.cn/); + assert.match(text, /帮我做一个读书笔记页面/); +}); + +test('buildSubscribeWelcomeText omits intro section when introUrl is empty', () => { + const text = buildSubscribeWelcomeText({ + bindUrl: 'https://m.tkmind.cn/bind', + introUrl: '', + }); + + assert.doesNotMatch(text, /📖 了解更多:/); + assert.doesNotMatch(text, /tkmind-deep-intro\.html/); +});