Files
memind/word-filter.mjs
T
john 9b4a25799f Add smart ACK provider for WeChat MP replies
Replace fixed ackText with a rule-based AckProvider that picks
response templates by message type and intent (translate, summary,
rewrite, poster, ppt, mindmap, code, search, schedule). Pure sync,
zero I/O, auto-falls back to config.ackText on any error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 15:19:03 +08:00

84 lines
3.2 KiB
JavaScript

import crypto from 'node:crypto';
export async function ensureWordFilterSchema(pool) {
await pool.query(`
CREATE TABLE IF NOT EXISTS h5_blocked_words (
id CHAR(36) PRIMARY KEY,
word VARCHAR(200) NOT NULL,
replacement VARCHAR(200) NOT NULL DEFAULT '***',
note VARCHAR(500) NULL,
status ENUM('active', 'disabled') NOT NULL DEFAULT 'active',
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
UNIQUE KEY uk_blocked_word (word)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`);
}
export function createWordFilterService(pool) {
async function listBlockedWords() {
const [rows] = await pool.query(
'SELECT id, word, replacement, note, status, created_at, updated_at FROM h5_blocked_words ORDER BY created_at DESC',
);
return rows;
}
async function createBlockedWord({ word, replacement, note }) {
const trimmed = String(word ?? '').trim();
if (!trimmed) return { ok: false, message: '词语不能为空' };
const id = crypto.randomUUID();
const now = Date.now();
try {
await pool.query(
'INSERT INTO h5_blocked_words (id, word, replacement, note, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)',
[id, trimmed, replacement ?? '***', note ?? '', 'active', now, now],
);
const [rows] = await pool.query('SELECT * FROM h5_blocked_words WHERE id = ?', [id]);
return { ok: true, blockedWord: rows[0] };
} catch (err) {
if (err?.code === 'ER_DUP_ENTRY') return { ok: false, message: '该词语已存在' };
throw err;
}
}
async function updateBlockedWord(id, { word, replacement, note, status }) {
const updates = [];
const values = [];
if (word !== undefined) {
const trimmed = String(word).trim();
if (!trimmed) return { ok: false, message: '词语不能为空' };
updates.push('word = ?');
values.push(trimmed);
}
if (replacement !== undefined) { updates.push('replacement = ?'); values.push(replacement); }
if (note !== undefined) { updates.push('note = ?'); values.push(note); }
if (status !== undefined) { updates.push('status = ?'); values.push(status); }
if (!updates.length) return { ok: false, message: '没有可更新的字段' };
updates.push('updated_at = ?');
values.push(Date.now(), id);
const [result] = await pool.query(
`UPDATE h5_blocked_words SET ${updates.join(', ')} WHERE id = ?`,
values,
);
if (!result.affectedRows) return { ok: false, message: '记录不存在' };
const [rows] = await pool.query('SELECT * FROM h5_blocked_words WHERE id = ?', [id]);
return { ok: true, blockedWord: rows[0] };
}
async function deleteBlockedWord(id) {
const [result] = await pool.query('DELETE FROM h5_blocked_words WHERE id = ?', [id]);
if (!result.affectedRows) return { ok: false, message: '记录不存在' };
return { ok: true };
}
async function listAllForFrontend() {
const [rows] = await pool.query(
'SELECT word, replacement FROM h5_blocked_words WHERE status = ? ORDER BY word',
['active'],
);
return rows;
}
return { listBlockedWords, createBlockedWord, updateBlockedWord, deleteBlockedWord, listAllForFrontend };
}