#22331: Fix Telegram reaction emoji normalization
channel: telegram
size: S
Cluster:
Telegram Message Handling Fixes
## What changed
- Normalize Telegram reaction emoji inputs by trimming and NFC-normalizing before sending.
- Centralize reaction payload building in `src/telegram/send.ts` and reuse it for regular reactions and ack-reaction flows.
- Keep ack-reaction behavior unchanged while avoiding invalid payloads for whitespace-padded emoji values.
## Why this fixes the issue
`REACTION_INVALID` can be triggered when configured or computed reaction strings include leading/trailing whitespace (for example `" 👍 "`), which Telegram rejects as malformed. This fix ensures payloads are sanitized and empty/invalid input becomes a no-op.
## Tests run
- `pnpm vitest run --config vitest.unit.config.ts src/telegram/send.test.ts src/telegram/bot.create-telegram-bot.test.ts src/telegram/*.test.ts`
## Edge cases
- Empty/whitespace-only reaction values now produce an empty reaction payload.
- Remove-reaction flows still pass an empty array intentionally.
- Ack reaction is sent only when a non-empty, sanitized emoji value is available.
## Notes
No unresolved blockers found for these changes.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR centralizes Telegram reaction emoji normalization by introducing `normalizeTelegramReaction()` and `buildTelegramReactionPayload()` helper functions. The normalization applies both trimming and NFC Unicode normalization before sending reactions to Telegram's API, preventing errors from whitespace-padded or differently-composed emoji strings.
**Key changes:**
- Centralized reaction payload building in `buildTelegramReactionPayload()` function
- Added NFC Unicode normalization to handle emoji composition variations
- Applied normalization to ack reactions, remove reactions, and regular reactions
- Added test coverage for whitespace normalization
The implementation is defensive and handles edge cases (empty strings, whitespace-only values) by returning empty arrays, which Telegram interprets as reaction removal.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The changes are well-tested with 453 passing tests, including new test cases specifically for whitespace normalization. The refactoring centralizes logic into reusable functions without changing behavior, and the NFC normalization follows established patterns already used elsewhere in the codebase (chat.ts). The implementation is defensive, handling edge cases appropriately.
- No files require special attention
<sub>Last reviewed commit: 92bb311</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#20236: fix(telegram): make reaction handling soft-fail and message-id resi...
by PeterShanxin · 2026-02-18
80.6%
#21346: [AI-assisted] Telegram: add reaction state machine with fallback an...
by Archie818 · 2026-02-19
80.6%
#23728: fix(telegram): clear done reaction when removeAckAfterReply is true
by kevinWangSheng · 2026-02-22
78.4%
#17316: fix: ack reaction not removed when block streaming is enabled (Tele...
by czmathew · 2026-02-15
76.6%
#14977: fix(telegram): remove ack reaction after block-streamed replies
by Diaspar4u · 2026-02-12
76.3%
#7980: feat(telegram): multi-stage reaction system for message pipeline vi...
by macmimi23 · 2026-02-03
76.0%
#11340: Telegram: skip empty message text instead of throwing (#11238)
by lailoo · 2026-02-07
75.8%
#12978: fix(telegram): use real message_id for inline button callback react...
by omair445 · 2026-02-10
74.7%
#19375: telegram: align tool-error summaries
by NorthyIE · 2026-02-17
74.4%
#22335: fix: harden normalizeE164 against empty/invalid inputs returning ba...
by CZH-THU · 2026-02-21
74.2%