File size: 5,391 Bytes
48ecd01 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | #!/usr/bin/env python3
"""
Standalone Telegram notification helper for FRANKENSTALLM 3B training.
Usage:
python3 scripts/telegram_notify.py "Your message here"
python3 scripts/telegram_notify.py "<b>Bold</b> message" --parse-mode HTML
Function API:
from scripts.telegram_notify import send_telegram
send_telegram("message text")
"""
import os
import sys
import json
import urllib.request
import urllib.parse
import urllib.error
import logging
from typing import Optional
# βββ Configuration ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "")
TIMEOUT = 15 # seconds
MAX_MSG_LEN = 4096 # Telegram limit
logging.basicConfig(
level=logging.WARNING,
format="%(asctime)s [telegram_notify] %(levelname)s: %(message)s",
)
log = logging.getLogger("telegram_notify")
def send_telegram(
message: str,
parse_mode: str = "HTML",
token: str = BOT_TOKEN,
chat_id: str = CHAT_ID,
disable_web_page_preview: bool = True,
) -> bool:
"""
Send a Telegram message via Bot API using urllib (curl-free).
Args:
message: Text to send (HTML or Markdown depending on parse_mode).
parse_mode: "HTML" or "Markdown" or "" (plain).
token: Bot token (defaults to module-level BOT_TOKEN).
chat_id: Recipient chat/channel ID.
disable_web_page_preview: Suppress link previews.
Returns:
True on success, False on any error.
"""
if not message:
log.warning("Empty message β skipping send.")
return False
# Truncate if over Telegram limit, with notice
if len(message) > MAX_MSG_LEN:
truncated_notice = "\n\n<i>[message truncated]</i>" if parse_mode == "HTML" else "\n\n[message truncated]"
message = message[: MAX_MSG_LEN - len(truncated_notice)] + truncated_notice
url = f"https://api.telegram.org/bot{token}/sendMessage"
payload: dict = {
"chat_id": chat_id,
"text": message,
"disable_web_page_preview": disable_web_page_preview,
}
if parse_mode:
payload["parse_mode"] = parse_mode
data = urllib.parse.urlencode(payload).encode("utf-8")
try:
req = urllib.request.Request(
url,
data=data,
method="POST",
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
body = resp.read().decode("utf-8")
result = json.loads(body)
if result.get("ok"):
return True
else:
log.error("Telegram API error: %s", result.get("description", result))
return False
except urllib.error.HTTPError as e:
try:
err_body = e.read().decode("utf-8")
except Exception:
err_body = str(e)
log.error("HTTP %d from Telegram: %s", e.code, err_body)
return False
except urllib.error.URLError as e:
log.error("Network error sending Telegram message: %s", e.reason)
return False
except json.JSONDecodeError as e:
log.error("Failed to parse Telegram response: %s", e)
return False
except Exception as e: # noqa: BLE001
log.error("Unexpected error in send_telegram: %s", e)
return False
def send_telegram_safe(message: str, **kwargs) -> bool:
"""
Wrapper that catches ALL exceptions β guaranteed never to crash the caller.
Suitable for embedding in training loops where stability is critical.
"""
try:
return send_telegram(message, **kwargs)
except Exception as e: # noqa: BLE001
log.error("send_telegram_safe caught unhandled exception: %s", e)
return False
# βββ CLI entry point ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description="Send a Telegram message from the command line."
)
parser.add_argument("message", nargs="?", help="Message text to send")
parser.add_argument(
"--parse-mode",
default="HTML",
choices=["HTML", "Markdown", "MarkdownV2", ""],
help="Telegram parse_mode (default: HTML)",
)
parser.add_argument(
"--token", default=BOT_TOKEN, help="Override bot token"
)
parser.add_argument(
"--chat-id", default=CHAT_ID, help="Override chat ID"
)
args = parser.parse_args()
# Allow piped stdin if no positional arg given
if args.message is None:
if not sys.stdin.isatty():
args.message = sys.stdin.read().strip()
else:
parser.print_help()
sys.exit(1)
ok = send_telegram(
args.message,
parse_mode=args.parse_mode,
token=args.token,
chat_id=args.chat_id,
)
if ok:
print("Telegram message sent successfully.")
sys.exit(0)
else:
print("ERROR: Failed to send Telegram message.", file=sys.stderr)
sys.exit(1)
|