e42417bd6e
Enable daily morning greeting on reply 1 (with custom time, modify, and cancel), random greeting delivery, M发现 in subscribe welcome, and optional LLM parsing when rules miss. Also fix WeChat MP draft/config error passthrough and add Tang E2E scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
146 lines
5.2 KiB
JavaScript
146 lines
5.2 KiB
JavaScript
import { resolveWechatAddressName } from '../user/display-name.mjs';
|
|
import { buildSubscribeMorningReminderPromptLines } from '../subscribe-morning-reminder.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 const DEFAULT_PLAZA_HOME_URL = 'https://plaza.tkmind.cn/plaza';
|
|
|
|
/** 关注欢迎语里推荐的 M发现精选入口(分类页稳定,便于长期维护)。 */
|
|
export const DEFAULT_SUBSCRIBE_PLAZA_PICKS = Object.freeze([
|
|
{
|
|
label: '✈️ 旅行攻略 · 路线/交通/美食预算一站看',
|
|
url: 'https://plaza.tkmind.cn/plaza/?category=travel',
|
|
},
|
|
{
|
|
label: '🍜 生活探店 · 深夜食堂、城市散步地图',
|
|
url: 'https://plaza.tkmind.cn/plaza/?category=lifestyle',
|
|
},
|
|
{
|
|
label: '🎨 创意 showcase · 活动页、海报、品牌视觉',
|
|
url: 'https://plaza.tkmind.cn/plaza/?category=creative',
|
|
},
|
|
]);
|
|
|
|
export function buildWechatTextLink(href, label) {
|
|
const normalizedHref = String(href ?? '').trim();
|
|
const normalizedLabel = String(label ?? '').trim();
|
|
if (!normalizedHref) return normalizedLabel;
|
|
if (!normalizedLabel) return normalizedHref;
|
|
return `<a href="${normalizedHref}">${normalizedLabel}</a>`;
|
|
}
|
|
|
|
export function buildPlazaDiscoveryLines({
|
|
plazaHomeUrl = DEFAULT_PLAZA_HOME_URL,
|
|
plazaPicks = DEFAULT_SUBSCRIBE_PLAZA_PICKS,
|
|
} = {}) {
|
|
const homeUrl = String(plazaHomeUrl ?? '').trim();
|
|
if (!homeUrl) return [];
|
|
const picks = Array.isArray(plazaPicks) ? plazaPicks : DEFAULT_SUBSCRIBE_PLAZA_PICKS;
|
|
const lines = [
|
|
'',
|
|
'🎯 M发现 · 精选作品广场',
|
|
'别人已经做好的精美页面,点开就能看;改改文字和图片,就是你的攻略/活动页:',
|
|
];
|
|
for (const pick of picks) {
|
|
const url = String(pick?.url ?? '').trim();
|
|
const label = String(pick?.label ?? '').trim();
|
|
if (!url || !label) continue;
|
|
lines.push(`· ${buildWechatTextLink(url, label)}`);
|
|
}
|
|
lines.push('', `👉 ${buildWechatTextLink(homeUrl, '进入 M发现,逛热门作品')}`);
|
|
return lines;
|
|
}
|
|
|
|
export function buildSubscribeWelcomeText({
|
|
bindUrl,
|
|
introUrl = DEFAULT_SUBSCRIBE_INTRO_URL,
|
|
plazaHomeUrl = DEFAULT_PLAZA_HOME_URL,
|
|
plazaPicks = DEFAULT_SUBSCRIBE_PLAZA_PICKS,
|
|
morningReminderEnabled = true,
|
|
morningReminderHour = 8,
|
|
morningReminderMinute = 0,
|
|
} = {}) {
|
|
const normalizedBindUrl = String(bindUrl ?? '').trim();
|
|
const normalizedIntroUrl = String(introUrl ?? '').trim();
|
|
const lines = [
|
|
'欢迎关注 TKMind 智趣 👋',
|
|
'',
|
|
'在微信里直接发消息即可:',
|
|
'· 问答、写作、翻译',
|
|
'· 生成精美网页并给链接',
|
|
'· 发图解读报告/截图',
|
|
'· 设置定时提醒与每日推送',
|
|
...buildPlazaDiscoveryLines({ plazaHomeUrl, plazaPicks }),
|
|
];
|
|
if (morningReminderEnabled) {
|
|
lines.push(
|
|
...buildSubscribeMorningReminderPromptLines({
|
|
hour: morningReminderHour,
|
|
minute: morningReminderMinute,
|
|
}),
|
|
);
|
|
}
|
|
if (normalizedIntroUrl) {
|
|
lines.push('', '📖 了解 TKMind 能做什么:', 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}!我在呢,有什么需要?` : '你好!我在呢,有什么需要?';
|
|
}
|
|
|
|
export function buildStatusText(user, fallbackText) {
|
|
const name = resolveWechatAddressName(user);
|
|
if (!name) return fallbackText;
|
|
return `我在这边,${name}。上一条如果还没完成,我会继续把结果发给你;你也可以直接补一句要求。`;
|
|
}
|
|
|
|
export function buildConnectivityTestReply(user) {
|
|
const name = resolveWechatAddressName(user);
|
|
return name
|
|
? `${name},公众号消息通道正常,我收到你的测试了。`
|
|
: '公众号消息通道正常,我收到你的测试了。';
|
|
}
|
|
|
|
/**
|
|
* Resolve an immediate sync XML reply for lightweight intents.
|
|
* @returns {string|null}
|
|
*/
|
|
export function resolveSyncReply(wechatIntent, user, { statusFallbackText = '' } = {}) {
|
|
switch (wechatIntent?.kind) {
|
|
case 'status.probe':
|
|
return buildStatusText(user, statusFallbackText);
|
|
case 'greeting':
|
|
return buildGreetingText(user);
|
|
case 'connectivity.test':
|
|
return buildConnectivityTestReply(user);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether this intent should be answered synchronously before agent dispatch.
|
|
*/
|
|
export function shouldHandleSyncReply(intent, wechatIntent) {
|
|
const msgType = String(intent?.msgType ?? '').toLowerCase();
|
|
if (wechatIntent?.kind === 'connectivity.test') {
|
|
return msgType === 'text' || msgType === 'voice';
|
|
}
|
|
if (wechatIntent?.kind === 'status.probe' || wechatIntent?.kind === 'greeting') {
|
|
return msgType === 'text';
|
|
}
|
|
return false;
|
|
}
|