KeeperHub / KeeperHub/keeperhub

Notification polish: Slack channel templating, Discord username/avatar/embeds, Telegram HTML + disablePreview

Open
#2,447 1 comment 0 reactions 0 assignees View on GitHub
accepted confirmed
Dominant language
TypeScript
Stars
24
Forks
93
Avg merge
1d 8h
Merged PRs (30d)
266

Description

## Reason

Every KeeperHub workflow ends with an alert, and all three notifiers have small paper cuts that break real alerting:

1. **Slack channel ignores `{{variables}}`.** `plugins/slack/index.ts` declares `slackChannel` as `type: "text"`, but its own placeholder advertises `{{NodeName.channel}}`. Per `plugins/registry.ts`, only `template-input` / `template-textarea` resolve `{{...}}` — a `text` field sends the literal braces. A workflow that routes alerts to a channel computed upstream (e.g. severity router) silently posts to the wrong place or fails with `channel_not_found`.
2. **Discord can only send flat text.** `plugins/discord/index.ts` exposes a single `discordMessage` field. The Discord webhook API supports `username`, `avatar_url`, and rich `embeds` with `title`/`color`, but none are reachable — every alert looks identical, so on-call cannot tell critical from info at a glance.
3. **Telegram forces MarkdownV2 or nothing.** `parseMode` options are only `none` / `MarkdownV2`. MarkdownV2 requires escaping `(., -, _, *, [, ], (, ), ~, `, >, #, +, =, |, {, }, !)` with a backslash (the step's own placeholder warns about this). Balances, addresses, and tx hashes are full of those characters, so templated alerts randomly fail with `can't parse entities`. Telegram's `HTML` mode (``, ``, ...) has no such escaping trap and is what most bots use. There is also no `disable_web_page_preview` toggle, so long explorer links render noisy previews.

What told me to expect it: the Slack placeholder itself (`#general or {{NodeName.channel}}`), the Discord webhook docs (`username`/`avatar_url`/`embeds`), and the Telegram Bot API (`parse_mode: HTML`, `disable_web_page_preview`).

### Repro (no credentials needed)

- Slack: create a Send Slack Message node, set Channel to `{{Router.channel}}` — the field type is `text`, so the braces are never resolved.
- Discord: try to set a red critical embed or custom bot name — no such field exists.
- Telegram: set Parse Mode to MarkdownV2 with message `Balance: 1.2345 ETH at 0x1234_abcd` — Telegram rejects it unless every `.`/`_` is escaped; there is no HTML alternative.

## Scope

Covers, all additive and optional (existing workflows unchanged):

- `plugins/slack/index.ts`: `slackChannel` `text` -> `template-input` (1 line, no step change — templating resolves upstream).
- `plugins/discord/index.ts` + `steps/send-message.ts`: optional `username`, `avatarUrl`, `embedTitle`, `embedColor` (none/red/green/yellow/blue/gray). When title or a non-none color is set, the step also sends `embeds: [{title, description, color}]`; otherwise the payload is exactly `{content}` as before. Values truncated to Discord limits (username 80, title 256, description 4096).
- `plugins/telegram/index.ts` + `steps/send-message.ts`: `parseMode` gains `HTML`; new optional `disablePreview` select (true/false, default false) mapped to `disable_web_page_preview`.
- Tests: new `tests/unit/notifications-polish.test.ts` (6 cases) + all 7 existing `discord-send-message` tests still pass.

Explicitly not covered: Slack Block Kit / attachments, Discord thread IDs / message editing, Telegram Markdown (legacy) / inline keyboards / media. Each of those is a separate issue if wanted.

Checked on `staging` at `28233554f`. No schema/migration, no auth change, no new dependency, fixed-host egress only.

## Plan

PR to `staging` with the above + tests, verifying:

- `tsx scripts/discover-plugins.ts` regenerates cleanly
- `tsgo --noEmit` clean except the pre-existing `tests/unit/workflow-directive-detection.test.ts` `@workflow/builders` error (untouched file, also present on clean staging)
- `vitest run tests/unit/notifications-polish.test.ts tests/unit/discord-send-message.test.ts` — 13/13 green
- `safeFetch` with `plugin` tag unchanged; raw `fetch` untouched (no new egress surface)

Contributor guide

Open the contributing guide

Research direction

Start with plugins/slack/index.ts, plugins/discord/index.ts, plugins/telegram/index.ts, and steps/send-message.ts, then inspect the existing discord-send-message tests and plugin field types. Run the two named Vitest suites and review tests/unit/notifications-polish.test.ts. Done means the optional notification fields and Telegram HTML/preview behavior work without changing existing workflows, discovery regenerates cleanly, and the stated checks pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend, testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.