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>
143 lines
4.8 KiB
JavaScript
143 lines
4.8 KiB
JavaScript
const CONFIG_TABLE = 'h5_wechat_admin_config';
|
|
const CONFIG_KEY = 'subscribe_morning_llm';
|
|
|
|
function normalizeBoolean(value, fallback = false) {
|
|
if (value == null || value === '') return fallback;
|
|
if (typeof value === 'boolean') return value;
|
|
const normalized = String(value).trim().toLowerCase();
|
|
if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
|
|
if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
|
|
return fallback;
|
|
}
|
|
|
|
function normalizeNumber(value, fallback) {
|
|
if (value == null || value === '') return fallback;
|
|
const parsed = Number(value);
|
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
}
|
|
|
|
function parseConfigJson(value) {
|
|
if (!value) return {};
|
|
if (typeof value === 'object') return value;
|
|
try {
|
|
return JSON.parse(String(value));
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
async function ensureConfigTable(pool) {
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS ${CONFIG_TABLE} (
|
|
config_key VARCHAR(64) PRIMARY KEY,
|
|
config_json JSON NOT NULL,
|
|
updated_by CHAR(36) NULL,
|
|
updated_at BIGINT NOT NULL
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
`);
|
|
}
|
|
|
|
function defaultsFromEnv(env = process.env) {
|
|
return {
|
|
subscribeMorningLlmEnabled: normalizeBoolean(env.H5_WECHAT_SUBSCRIBE_MORNING_LLM_ENABLED, false),
|
|
modelProviderKeyId: String(env.MEMIND_WECHAT_SUBSCRIBE_MORNING_LLM_MODEL_PROVIDER_KEY_ID ?? '').trim() || null,
|
|
model: String(env.MEMIND_WECHAT_SUBSCRIBE_MORNING_LLM_MODEL ?? '').trim() || null,
|
|
minConfidence: normalizeNumber(env.MEMIND_WECHAT_SUBSCRIBE_MORNING_LLM_MIN_CONFIDENCE, 0.65),
|
|
timeoutMs: Math.max(500, normalizeNumber(env.MEMIND_WECHAT_SUBSCRIBE_MORNING_LLM_TIMEOUT_MS, 4000)),
|
|
};
|
|
}
|
|
|
|
export function createWechatSubscribeMorningLlmConfigService(pool, { env = process.env } = {}) {
|
|
let ensurePromise = null;
|
|
|
|
async function ensureReady() {
|
|
if (!ensurePromise) ensurePromise = ensureConfigTable(pool);
|
|
await ensurePromise;
|
|
}
|
|
|
|
async function readRow() {
|
|
await ensureReady();
|
|
const [rows] = await pool.query(
|
|
`SELECT config_json, updated_by, updated_at
|
|
FROM ${CONFIG_TABLE}
|
|
WHERE config_key = ?
|
|
LIMIT 1`,
|
|
[CONFIG_KEY],
|
|
);
|
|
return rows[0] ?? null;
|
|
}
|
|
|
|
function mergeConfig(row) {
|
|
const defaults = defaultsFromEnv(env);
|
|
const stored = parseConfigJson(row?.config_json);
|
|
return {
|
|
subscribeMorningLlmEnabled: normalizeBoolean(
|
|
stored.subscribeMorningLlmEnabled,
|
|
defaults.subscribeMorningLlmEnabled,
|
|
),
|
|
modelProviderKeyId: String(stored.modelProviderKeyId ?? defaults.modelProviderKeyId ?? '').trim() || null,
|
|
model: String(stored.model ?? defaults.model ?? '').trim() || null,
|
|
minConfidence: normalizeNumber(stored.minConfidence, defaults.minConfidence),
|
|
timeoutMs: normalizeNumber(stored.timeoutMs, defaults.timeoutMs),
|
|
};
|
|
}
|
|
|
|
return {
|
|
async getConfig() {
|
|
const row = await readRow();
|
|
return {
|
|
...mergeConfig(row),
|
|
updatedAt: row?.updated_at ? Number(row.updated_at) : null,
|
|
updatedBy: row?.updated_by ?? null,
|
|
};
|
|
},
|
|
|
|
async isSubscribeMorningLlmEnabled() {
|
|
const config = await this.getConfig();
|
|
return config.subscribeMorningLlmEnabled;
|
|
},
|
|
|
|
async updateConfig(payload = {}, { updatedBy = null } = {}) {
|
|
const current = await this.getConfig();
|
|
const next = {
|
|
subscribeMorningLlmEnabled:
|
|
payload.subscribeMorningLlmEnabled === undefined
|
|
? current.subscribeMorningLlmEnabled
|
|
: normalizeBoolean(payload.subscribeMorningLlmEnabled, current.subscribeMorningLlmEnabled),
|
|
modelProviderKeyId:
|
|
payload.modelProviderKeyId === undefined
|
|
? (current.modelProviderKeyId ?? '')
|
|
: String(payload.modelProviderKeyId ?? '').trim(),
|
|
model:
|
|
payload.model === undefined
|
|
? (current.model ?? '')
|
|
: String(payload.model ?? '').trim(),
|
|
minConfidence:
|
|
payload.minConfidence === undefined
|
|
? current.minConfidence
|
|
: normalizeNumber(payload.minConfidence, current.minConfidence),
|
|
timeoutMs:
|
|
payload.timeoutMs === undefined
|
|
? current.timeoutMs
|
|
: normalizeNumber(payload.timeoutMs, current.timeoutMs),
|
|
};
|
|
const now = Date.now();
|
|
await pool.query(
|
|
`INSERT INTO ${CONFIG_TABLE} (config_key, config_json, updated_by, updated_at)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
config_json = VALUES(config_json),
|
|
updated_by = VALUES(updated_by),
|
|
updated_at = VALUES(updated_at)`,
|
|
[CONFIG_KEY, JSON.stringify(next), updatedBy, now],
|
|
);
|
|
return this.getConfig();
|
|
},
|
|
};
|
|
}
|
|
|
|
export const wechatSubscribeMorningLlmConfigInternals = {
|
|
normalizeBoolean,
|
|
defaultsFromEnv,
|
|
};
|