Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/bub_skills/telegram/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Collect these before execution:
## Execution Policy

1. If handling a direct user message in Telegram and `message_id` is known, send a reply message (`--reply-to`).
2. If source metadata says sender is a bot (`sender_is_bot=true`), do not use reply mode, but send a normal message and prefix content with `@<sender_username>` (or the provided source username).
2. If source metadata says sender is a bot (`sender_is_bot=true`), do not use reply mode, but send a normal message and prefix content with `@<sender_username>` (or the provided source username). If the user doesn't have a username, use `--source-user-id` to mention via `tg://user?id=` link.
3. For long-running tasks, optionally send one progress message, then edit that same message for final status.
4. For multi-line text, pass the content via heredoc command substitution instead of embedding raw line breaks in quoted strings.
5. Avoid emitting HTML tags in message content; use Markdown for formatting instead.
Expand Down Expand Up @@ -91,13 +91,21 @@ uv run ./scripts/telegram_send.py \
--message "<TEXT>" \
--reply-to <MESSAGE_ID>

# Source message sender is bot: no direct reply, use @user_id style
# Source message sender is bot: no direct reply, use @username style
uv run ./scripts/telegram_send.py \
--chat-id <CHAT_ID> \
--message "<TEXT>" \
--source-is-bot \
--source-username <USERNAME>

# Source message sender is bot without username: use tg://user?id= link
uv run ./scripts/telegram_send.py \
--chat-id <CHAT_ID> \
--message "<TEXT>" \
--source-is-bot \
--source-user-id <USER_ID> \
--source-display-name "Display Name"

# Edit existing message
uv run ./scripts/telegram_edit.py \
--chat-id <CHAT_ID> \
Expand All @@ -115,8 +123,10 @@ For other actions that not covered by these scripts, use `curl` to call Telegram
- `--message`, `-m`: required
- `--reply-to`, `-r`: optional
- `--token`, `-t`: optional (normally not needed)
- `--source-is-bot`: optional flag, disables reply mode and switches to `@user_id` style
- `--source-user-id`: optional, required when `--source-is-bot` is set
- `--source-is-bot`: optional flag, disables reply mode and adds mention prefix
- `--source-username`: optional, uses `@username` style mention when set
- `--source-user-id`: optional, uses `tg://user?id=` link mention when username is not available
- `--source-display-name`: optional, display name for user ID mention (defaults to "User")

### `telegram_edit.py`

Expand Down
33 changes: 32 additions & 1 deletion src/bub_skills/telegram/scripts/telegram_send.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ def send_message(
return response.json()


def escape_markdown_v2(text: str) -> str:
"""
Escape special characters for Telegram MarkdownV2 format.
"""
escape_chars = r'_*[]()~`>#+-=|{}.!'
return ''.join('\\' + char if char in escape_chars else char for char in text)


def main():
parser = argparse.ArgumentParser(description="Send messages via Telegram Bot API (auto-converts to MarkdownV2)")
parser.add_argument("--chat-id", "-c", required=True, help="Target chat ID")
Expand All @@ -138,6 +146,14 @@ def main():
"--source-username",
help="Source username for @username prefix when --source-is-bot is enabled",
)
parser.add_argument(
"--source-user-id",
help="Source user ID for mention when username is not available (uses tg://user?id= link)",
)
parser.add_argument(
"--source-display-name",
help="Display name for user ID mention (defaults to 'User')",
)

args = parser.parse_args()

Expand All @@ -150,10 +166,25 @@ def main():
# Parse chat IDs
chat_id = args.chat_id.strip()
reply_to = args.reply_to
message = args.message

# Handle source-is-bot mode: prefix message with mention
if args.source_is_bot:
if args.source_user_id:
# Use tg://user?id= link for mention (works without username)
display_name = args.source_display_name or "User"
# Escape the display name for MarkdownV2
escaped_name = escape_markdown_v2(display_name)
mention = f"[{escaped_name}](tg://user?id={args.source_user_id})"
message = f"{mention}\n\n{message}"
elif args.source_username:
# Fall back to @username mention
message = f"@{args.source_username}\n\n{message}"
# If neither is provided, just send without mention prefix

# Send messages
try:
send_message(bot_token, chat_id, args.message, reply_to)
send_message(bot_token, chat_id, message, reply_to)
print(f"✅ Message sent successfully to {chat_id} (MarkdownV2)")
except requests.HTTPError as e:
print(f"❌ HTTP Error: {e}")
Expand Down
Loading