| | |
| | """ |
| | 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 |
| |
|
| | |
| | BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "") |
| | CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "") |
| | TIMEOUT = 15 |
| | MAX_MSG_LEN = 4096 |
| |
|
| | 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 |
| |
|
| | |
| | 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: |
| | 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: |
| | log.error("send_telegram_safe caught unhandled exception: %s", e) |
| | return False |
| |
|
| |
|
| | |
| | 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() |
| |
|
| | |
| | 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) |
| |
|