utkarshpathak48 commited on
Commit
fcb1319
·
1 Parent(s): 0690679

Route Telegram alerts via Cloudflare Worker relay (HF blocks api.telegram.org)

Browse files
Files changed (2) hide show
  1. main.py +8 -10
  2. telegram_alert.py +22 -17
main.py CHANGED
@@ -383,20 +383,18 @@ def catalyst(symbol: str = ""):
383
 
384
  @app.get("/api/test-telegram")
385
  def test_telegram():
386
- """Send a test Telegram message to verify bot credentials are working."""
387
  import requests as req
388
- bot_token = os.environ.get("TELEGRAM_BOT_TOKEN", "")
389
- chat_id = os.environ.get("TELEGRAM_CHAT_ID", "")
390
- if not bot_token or not chat_id:
391
- return JSONResponse({"ok": False, "error": f"Missing secretstoken={'set' if bot_token else 'MISSING'} chat={'set' if chat_id else 'MISSING'}"}, status_code=500)
392
- text = "✅ Trade Copilot — Test Message\n\nTelegram alerts are working correctly."
393
- url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
394
  try:
395
- resp = req.post(url, json={"chat_id": chat_id, "text": text}, timeout=10)
396
  body = resp.json()
397
  if resp.status_code == 200:
398
- return {"ok": True, "message": "Test message sent successfully"}
399
- return JSONResponse({"ok": False, "status": resp.status_code, "telegram_error": body}, status_code=500)
400
  except Exception as e:
401
  return JSONResponse({"ok": False, "error": type(e).__name__ + ": " + str(e)}, status_code=500)
402
 
 
383
 
384
  @app.get("/api/test-telegram")
385
  def test_telegram():
386
+ """Send a test Telegram message via Cloudflare Worker relay."""
387
  import requests as req
388
+ worker_url = os.environ.get("CLOUDFLARE_WORKER_URL", "")
389
+ if not worker_url:
390
+ return JSONResponse({"ok": False, "error": "CLOUDFLARE_WORKER_URL not set in HF Secrets"}, status_code=500)
391
+ text = " <b>Trade CopilotTest Message</b>\n\nTelegram alerts are working correctly via Cloudflare Worker."
 
 
392
  try:
393
+ resp = req.post(worker_url, json={"text": text, "parse_mode": "HTML"}, timeout=10)
394
  body = resp.json()
395
  if resp.status_code == 200:
396
+ return {"ok": True, "message": "Test message sent successfully via CF Worker"}
397
+ return JSONResponse({"ok": False, "status": resp.status_code, "error": body}, status_code=500)
398
  except Exception as e:
399
  return JSONResponse({"ok": False, "error": type(e).__name__ + ": " + str(e)}, status_code=500)
400
 
telegram_alert.py CHANGED
@@ -1,8 +1,12 @@
1
  """Telegram alert sender for high-confidence trade cards.
2
 
3
- Env vars required:
4
- TELEGRAM_BOT_TOKEN — bot token from @BotFather
5
- TELEGRAM_CHAT_ID — chat/channel ID (numeric)
 
 
 
 
6
 
7
  Dedup design (verified by 7-agent panel):
8
  - Background loop uses key prefix "bg:" — 4-hour cooldown
@@ -19,6 +23,8 @@ from datetime import datetime, timezone
19
 
20
  logger = logging.getLogger("telegram_alert")
21
 
 
 
22
  TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
23
  TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "")
24
 
@@ -192,11 +198,10 @@ async def send_alert(card: dict, tv_sym: str, manual: bool = False) -> bool:
192
  """
193
  global _warned_missing
194
 
195
- if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID:
196
  if not _warned_missing:
197
  logger.warning(
198
- "Telegram alerts disabled — set TELEGRAM_BOT_TOKEN and "
199
- "TELEGRAM_CHAT_ID in HF Spaces Secrets."
200
  )
201
  _warned_missing = True
202
  return False
@@ -210,24 +215,24 @@ async def send_alert(card: dict, tv_sym: str, manual: bool = False) -> bool:
210
  )
211
  return False
212
 
 
 
 
 
213
  text = build_message(card, tv_sym)
214
- url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
215
- payload = {
216
- "chat_id": TELEGRAM_CHAT_ID,
217
- "text": text,
218
- "parse_mode": "HTML",
219
- "disable_web_page_preview": True,
220
- }
221
 
222
  try:
223
  async with aiohttp.ClientSession() as session:
224
  async with session.post(
225
- url, json=payload, timeout=aiohttp.ClientTimeout(total=10)
 
 
226
  ) as resp:
227
  if resp.status == 200:
228
  _mark_sent(card, manual)
229
  logger.info(
230
- "Telegram alert sent [%s]: %s %s conf=%.1f",
231
  "manual" if manual else "bg",
232
  card.get("symbol"), card.get("direction"),
233
  float(card.get("confidence", 0)),
@@ -235,12 +240,12 @@ async def send_alert(card: dict, tv_sym: str, manual: bool = False) -> bool:
235
  return True
236
  body = await resp.text()
237
  logger.error(
238
- "Telegram API error %d for %s: %s",
239
  resp.status, card.get("symbol"), body[:300],
240
  )
241
  except Exception as exc:
242
  logger.error(
243
- "Telegram send failed for %s: %s",
244
  card.get("symbol"), exc, exc_info=True,
245
  )
246
 
 
1
  """Telegram alert sender for high-confidence trade cards.
2
 
3
+ Routing: HF Spaces cannot reach api.telegram.org directly (IP blocked).
4
+ Messages are routed via a Cloudflare Worker relay:
5
+ HF Space CLOUDFLARE_WORKER_URL → api.telegram.org → Telegram
6
+
7
+ Env vars required (set in HF Spaces Secrets):
8
+ CLOUDFLARE_WORKER_URL — your Worker URL, e.g. https://trade-alert.yourname.workers.dev
9
+ (TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID are set as env vars on the Worker itself, not here)
10
 
11
  Dedup design (verified by 7-agent panel):
12
  - Background loop uses key prefix "bg:" — 4-hour cooldown
 
23
 
24
  logger = logging.getLogger("telegram_alert")
25
 
26
+ CLOUDFLARE_WORKER_URL = os.environ.get("CLOUDFLARE_WORKER_URL", "")
27
+ # Keep these for backward compat / fallback reference
28
  TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
29
  TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "")
30
 
 
198
  """
199
  global _warned_missing
200
 
201
+ if not CLOUDFLARE_WORKER_URL:
202
  if not _warned_missing:
203
  logger.warning(
204
+ "Telegram alerts disabled — set CLOUDFLARE_WORKER_URL in HF Spaces Secrets."
 
205
  )
206
  _warned_missing = True
207
  return False
 
215
  )
216
  return False
217
 
218
+ if not CLOUDFLARE_WORKER_URL:
219
+ logger.warning("CLOUDFLARE_WORKER_URL not set in HF Secrets — alerts disabled.")
220
+ return False
221
+
222
  text = build_message(card, tv_sym)
223
+ payload = {"text": text, "parse_mode": "HTML"}
 
 
 
 
 
 
224
 
225
  try:
226
  async with aiohttp.ClientSession() as session:
227
  async with session.post(
228
+ CLOUDFLARE_WORKER_URL,
229
+ json=payload,
230
+ timeout=aiohttp.ClientTimeout(total=10)
231
  ) as resp:
232
  if resp.status == 200:
233
  _mark_sent(card, manual)
234
  logger.info(
235
+ "Telegram alert sent via CF Worker [%s]: %s %s conf=%.1f",
236
  "manual" if manual else "bg",
237
  card.get("symbol"), card.get("direction"),
238
  float(card.get("confidence", 0)),
 
240
  return True
241
  body = await resp.text()
242
  logger.error(
243
+ "CF Worker error %d for %s: %s",
244
  resp.status, card.get("symbol"), body[:300],
245
  )
246
  except Exception as exc:
247
  logger.error(
248
+ "CF Worker request failed for %s: %s",
249
  card.get("symbol"), exc, exc_info=True,
250
  )
251