Admin alerts arrive from Marty's own Hermes bot

Operational alerts should come from @CoolifyHermes_Bot, where his other pings
land, not from the member-facing InstantAdPay bot. The token is supplied as
HERMES_BOT_TOKEN in the app environment and never appears in the repo.

Still admin-only, and it falls back to the IAP bot then email rather than to any
member-visible channel: these alerts name a member's campaign and their email.

Also: the sweep swallowed a failed pause in an empty catch, which is precisely
the failure mode the module exists to prevent. It reports now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-24 14:05:55 -05:00
parent 170d9963f7
commit fd08af36ba
2 changed files with 99 additions and 77 deletions
+19 -2
View File
@@ -485,8 +485,11 @@ async function boot() {
videosweep.init({ dataDir: DATA_DIR, fs, path, db, videoCheck, videosweep.init({ dataDir: DATA_DIR, fs, path, db, videoCheck,
alert: async text => { alert: async text => {
const sc = siteConfig(); const sc = siteConfig();
// Admin channels ONLY. This names a member's campaign and their email, so it must never // Marty's own Hermes chat, where his other operational pings land (@CoolifyHermes_Bot).
// fall back to the shared payments topic where the whole team would read it. // Admin channels ONLY, never a member-visible one: this names a member's campaign and
// their email address, so the shared payments topic would put it in front of the team.
const hermes = String(process.env.HERMES_BOT_TOKEN || '').trim();
if (hermes && sc.telegramAdminChatId) return tgSendAs(hermes, sc.telegramAdminChatId, text);
if (sc.telegramBotToken && sc.telegramAdminChatId) return telegramSend(sc.telegramAdminChatId, text); if (sc.telegramBotToken && sc.telegramAdminChatId) return telegramSend(sc.telegramAdminChatId, text);
if (ADMIN_EMAIL && mailer.hasKey()) return mailer.send(ADMIN_EMAIL, 'InstantAdPay: broken video sources', text.replace(/<[^>]+>/g, '')); if (ADMIN_EMAIL && mailer.hasKey()) return mailer.send(ADMIN_EMAIL, 'InstantAdPay: broken video sources', text.replace(/<[^>]+>/g, ''));
console.log('videosweep: no admin channel configured, alert not sent'); console.log('videosweep: no admin channel configured, alert not sent');
@@ -810,6 +813,20 @@ async function payPromo(email, day) {
}); });
} }
// anti-fraud admin alert (Telegram admin chat, else email): who, which flags, and whether the sign-up was blocked // anti-fraud admin alert (Telegram admin chat, else email): who, which flags, and whether the sign-up was blocked
// Send as a specific bot, for operational alerts that should arrive from Marty's own Hermes bot
// rather than from the member-facing InstantAdPay one.
function tgSendAs(token, chatId, text, threadId) {
return new Promise(resolve => {
const body = JSON.stringify(Object.assign({ chat_id: String(chatId), text, parse_mode: 'HTML', disable_web_page_preview: true },
threadId ? { message_thread_id: Number(threadId) } : {}));
const rq = https.request({ hostname: 'api.telegram.org', path: '/bot' + token + '/sendMessage', method: 'POST', timeout: 12000,
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } },
r => { r.resume(); resolve(r.statusCode === 200); });
rq.on('error', () => resolve(false));
rq.on('timeout', () => { rq.destroy(); resolve(false); });
rq.end(body);
});
}
function fraudAlert(email, fc, spAcct, blocked) { function fraudAlert(email, fc, spAcct, blocked) {
try { try {
const sc = siteConfig(); const sc = siteConfig();
+6 -1
View File
@@ -42,7 +42,12 @@ async function run(opts) {
try { try {
await X.db.q("UPDATE campaigns SET status='paused' WHERE id=?", [c.id]); await X.db.q("UPDATE campaigns SET status='paused' WHERE id=?", [c.id]);
out.paused.push(c.id); out.paused.push(c.id);
} catch (e) { /* reported below either way */ } } catch (e) {
// never swallow this: a guard that fails quietly is the whole reason #146 survived
out.failed = out.failed || [];
out.failed.push({ id: c.id, error: e.message });
console.error('videosweep could not pause #' + c.id + ': ' + e.message);
}
} }
} }
if (!dry) { if (!dry) {