"""Telegram notification service."""
import os
import logging
from typing import Optional
import httpx
logger = logging.getLogger(name)
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
DEFAULT_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID") # Family group
MATT_CHAT_ID = os.getenv("TELEGRAM_MATTS_ID", "8386527252")
class TelegramNotifier:
"""Send Telegram notifications to family group or Matt directly."""
def __init__(self):
self.token = TELEGRAM_BOT_TOKEN
self.base_url = f"https://api.telegram.org/bot{self.token}" if self.token else None
self.client = httpx.AsyncClient(timeout=30.0) if self.token else None
async def send(
self,
message: str,
chat_id: Optional[str] = None,
parse_mode: str = "HTML",
disable_notification: bool = False
) -> dict:
"""Send a message to Telegram.
Args:
message: Text to send (HTML allowed)
chat_id: Target chat (defaults to family group)
parse_mode: "HTML" or "Markdown"
disable_notification: Send silently if True
Returns:
Telegram API response dict
"""
if not self.client:
logger.warning("Telegram not configured, message not sent")
return {"ok": False, "error": "not_configured"}
chat_id = chat_id or DEFAULT_CHAT_ID
if not chat_id:
logger.error("No chat_id specified and no default configured")
return {"ok": False, "error": "no_chat_id"}
payload = {
"chat_id": chat_id,
"text": message,
"parse_mode": parse_mode,
"disable_notification": disable_notification
}
try:
response = await self.client.post(
f"{self.base_url}/sendMessage",
json=payload
)
response.raise_for_status()
result = response.json()
if result.get("ok"):
logger.info(f"Telegram sent to {chat_id}")
else:
logger.error(f"Telegram API error: {result}")
return result
except Exception as e:
logger.error(f"Failed to send Telegram: {e}")
return {"ok": False, "error": str(e)}
async def to_matt(self, message: str, **kwargs) -> dict:
"""Send directly to Matt's DM."""
return await self.send(message, chat_id=MATT_CHAT_ID, **kwargs)
async def to_family(self, message: str, **kwargs) -> dict:
"""Send to family group."""
return await self.send(message, chat_id=DEFAULT_CHAT_ID, **kwargs)
async def send_with_buttons(
self,
message: str,
reply_markup: dict,
chat_id: Optional[str] = None,
parse_mode: str = "HTML"
) -> dict:
"""Send a message with inline keyboard buttons to Telegram.
Args:
message: Text to send (HTML allowed)
reply_markup: Inline keyboard JSON (e.g., {"inline_keyboard": [[...]]})
chat_id: Target chat (defaults to family group)
parse_mode: "HTML" or "Markdown"
Returns:
Telegram API response dict
"""
if not self.client:
logger.warning("Telegram not configured, message not sent")
return {"ok": False, "error": "not_configured"}
chat_id = chat_id or DEFAULT_CHAT_ID
if not chat_id:
logger.error("No chat_id specified and no default configured")
return {"ok": False, "error": "no_chat_id"}
import json
payload = {
"chat_id": chat_id,
"text": message,
"parse_mode": parse_mode,
"reply_markup": json.dumps(reply_markup)
}
try:
response = await self.client.post(
f"{self.base_url}/sendMessage",
json=payload
)
response.raise_for_status()
result = response.json()
if result.get("ok"):
logger.info(f"Telegram buttons sent to {chat_id}")
else:
logger.error(f"Telegram API error: {result}")
return result
except Exception as e:
logger.error(f"Failed to send Telegram with buttons: {e}")
return {"ok": False, "error": str(e)}
async def close(self):
if self.client:
await self.client.aclose()