feat: configure max notification items
This commit is contained in:
@@ -34,13 +34,13 @@ export function getNotifyConfig() {
|
||||
try {
|
||||
if (!fs.existsSync(NOTIFY_FILE)) return { ...DEFAULT_CONFIG, targetOptions: HERMES_TARGET_OPTIONS };
|
||||
const cfg = JSON.parse(fs.readFileSync(NOTIFY_FILE, 'utf-8'));
|
||||
return { ...DEFAULT_CONFIG, ...cfg, targetOptions: HERMES_TARGET_OPTIONS, channels: Array.isArray(cfg.channels) ? cfg.channels : DEFAULT_CONFIG.channels };
|
||||
return normalizeNotifyConfig({ ...DEFAULT_CONFIG, ...cfg, channels: Array.isArray(cfg.channels) ? cfg.channels : DEFAULT_CONFIG.channels }, true);
|
||||
} catch {
|
||||
return { ...DEFAULT_CONFIG, targetOptions: HERMES_TARGET_OPTIONS };
|
||||
}
|
||||
}
|
||||
|
||||
export function saveNotifyConfig(config = {}) {
|
||||
function normalizeNotifyConfig(config = {}, includeTargetOptions = false) {
|
||||
const normalized = {
|
||||
enabled: config.enabled !== false,
|
||||
notifyOnlyGood: config.notifyOnlyGood !== false,
|
||||
@@ -57,6 +57,11 @@ export function saveNotifyConfig(config = {}) {
|
||||
target: c.target || c.platform || 'feishu',
|
||||
})),
|
||||
};
|
||||
return includeTargetOptions ? { ...normalized, targetOptions: HERMES_TARGET_OPTIONS } : normalized;
|
||||
}
|
||||
|
||||
export function saveNotifyConfig(config = {}) {
|
||||
const normalized = normalizeNotifyConfig(config);
|
||||
fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||
fs.writeFileSync(NOTIFY_FILE, JSON.stringify(normalized, null, 2), 'utf-8');
|
||||
return { ...normalized, targetOptions: HERMES_TARGET_OPTIONS };
|
||||
|
||||
@@ -912,6 +912,7 @@ async function loadNotifyConfig() {
|
||||
document.getElementById('n-enabled').checked = notifyConfig.enabled !== false;
|
||||
document.getElementById('n-good-only').checked = notifyConfig.notifyOnlyGood !== false;
|
||||
document.getElementById('n-min-score').value = notifyConfig.minScore ?? 75;
|
||||
document.getElementById('n-max-items').value = notifyConfig.maxNotifyItems ?? 10;
|
||||
renderNotifyChannels();
|
||||
}
|
||||
|
||||
@@ -962,7 +963,7 @@ function renderNotifyChannelsFromDom() {
|
||||
}
|
||||
|
||||
function addNotifyChannel(type) {
|
||||
if (!notifyConfig) notifyConfig = { enabled: true, notifyOnlyGood: true, minScore: 75, channels: [] };
|
||||
if (!notifyConfig) notifyConfig = { enabled: true, notifyOnlyGood: true, minScore: 75, maxNotifyItems: 10, channels: [] };
|
||||
const normalizedType = type === 'hermes-webhook' ? 'hermes-send-message' : type;
|
||||
const defaults = {
|
||||
'hermes-send-message': { name: 'Hermes 平台消息', target: 'feishu:oc_3e963a150d98f254d8f38881bcfa1812', url: '' },
|
||||
@@ -997,6 +998,7 @@ function collectNotifyConfig() {
|
||||
enabled: document.getElementById('n-enabled').checked,
|
||||
notifyOnlyGood: document.getElementById('n-good-only').checked,
|
||||
minScore: Number(document.getElementById('n-min-score').value || 75),
|
||||
maxNotifyItems: Number(document.getElementById('n-max-items').value || 10),
|
||||
channels,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -183,6 +183,11 @@
|
||||
<label>通知最低分</label>
|
||||
<input id="n-min-score" type="number" min="0" max="100" value="75">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>单次最多推送商品数</label>
|
||||
<input id="n-max-items" type="number" min="1" max="50" value="10">
|
||||
<span class="hint">每次分析完成后最多单独推送多少个候选,避免刷屏。</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notify-help">
|
||||
推荐:Hermes 平台消息会直接调用本机 Hermes send_message,可选择飞书/微信/QQ 等目标;Bark、PushPlus 和自定义 Webhook 仍保留为备用。
|
||||
|
||||
@@ -137,7 +137,7 @@ app.post('/api/notify-test', async (req, res) => {
|
||||
const task = { id: 'notify-test', name: '通知渠道测试' };
|
||||
const summary = {
|
||||
minScore: Number(config.minScore ?? 75),
|
||||
maxNotifyItems: 1,
|
||||
maxNotifyItems: config.maxNotifyItems ?? 1,
|
||||
total: 1,
|
||||
recommendedCount: 1,
|
||||
cautionCount: 0,
|
||||
|
||||
@@ -358,6 +358,8 @@ Item link normalization for mobile-clickable reports/notifications is captured i
|
||||
|
||||
Per-item notifications and Feishu Markdown clickable-link formatting are captured in `references/per-item-notifications-feishu-links-2026-05.md`.
|
||||
|
||||
Single-run max notification item count (`maxNotifyItems`) is captured in `references/max-notify-items-config-2026-05.md`.
|
||||
|
||||
Portable export packaging for sharing this skill/project with other Hermes instances is captured in `references/portable-skill-package-2026-05.md`.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# 单次最大推送商品数配置(2026-05)
|
||||
|
||||
## 背景
|
||||
|
||||
BOSS 希望在每商品单独推送之后,增加一个“单次运行最多推送多少个商品”的可配置项,避免一次分析结果过多导致飞书/微信刷屏。
|
||||
|
||||
## 实现要点
|
||||
|
||||
- 配置字段:`maxNotifyItems`
|
||||
- 默认:`10`
|
||||
- 保存时限制范围:`1–50`
|
||||
- 位置:`data/notify-config.json`
|
||||
- 后端:`lib/notifier.mjs`
|
||||
- `DEFAULT_CONFIG.maxNotifyItems = 10`
|
||||
- `normalizeNotifyConfig()` 同时用于读取和保存,确保旧配置文件缺少字段时也能返回默认值。
|
||||
- `sendNotifications()` 调用 `buildNotificationMessages()` 时传入 `cfg.maxNotifyItems`。
|
||||
- `selectNotificationItems()` 对符合条件的候选 `.slice(0, maxNotifyItems)`。
|
||||
- 前端:`public/index.html` + `public/app.js`
|
||||
- 通知设置弹窗增加输入框:`单次最多推送商品数`。
|
||||
- `loadNotifyConfig()` 回填 `n-max-items`。
|
||||
- `collectNotifyConfig()` 提交 `maxNotifyItems`。
|
||||
- 测试通知:`server.mjs` 的 `/api/notify-test` 读取 config 中的 `maxNotifyItems`,但测试样例本身仍只有 1 个商品。
|
||||
|
||||
## 验证命令
|
||||
|
||||
```bash
|
||||
cd /Users/chick/.Hermes/workspace/research/xianyu-hunter
|
||||
node --check lib/notifier.mjs
|
||||
node --check server.mjs
|
||||
node --check public/app.js
|
||||
node --input-type=module <<'JS'
|
||||
import { getNotifyConfig, saveNotifyConfig, sendNotifications } from './lib/notifier.mjs';
|
||||
const before = getNotifyConfig();
|
||||
const saved = saveNotifyConfig({ enabled: true, notifyOnlyGood: false, minScore: 50, perItem: true, maxNotifyItems: 3, channels: [] });
|
||||
const summary = { topItems: Array.from({length: 8}, (_, i) => ({ id: String(1000+i), title: `候选${i+1}`, score: 90-i, verdict: 'recommend', price: '150', location: '测试', reason: '验证单次最大推送数量', href: `https://www.goofish.com/item?id=${1000+i}` })) };
|
||||
const result = await sendNotifications({ id:'t', name:'maxNotifyItems 验证' }, summary, { force: true, config: saved });
|
||||
console.log(result.messages.length); // expected: 3
|
||||
saveNotifyConfig(before);
|
||||
JS
|
||||
```
|
||||
|
||||
## 运行时注意
|
||||
|
||||
修改 `lib/notifier.mjs` / `server.mjs` 后必须重启正在监听 3000 的 `node server.mjs`,否则 Web API 和通知逻辑仍可能是旧代码。
|
||||
Reference in New Issue
Block a user