Files

180 lines
6.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Telegram 订阅处理器
每 30 分钟由 GitHub Actions 触发,处理 @GGmanm_bot 的订阅/退订请求。
用户命令:
/subscribe 或 /start subscribe → 加入每日投资早报
/unsubscribe → 取消订阅
/start → 欢迎消息 + 订阅说明
"""
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
import requests
# ─── Config ──────────────────────────────────────────────────────────────────
TELEGRAM_TOKEN = os.environ.get("TELEGRAM_TOKEN", "8518954174:AAHIWuxR4DDTtqxqjFzeUi33WxFUtnLyQQc")
API_BASE = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}"
ROOT_DIR = Path(__file__).resolve().parent.parent
SUBS_FILE = ROOT_DIR / "data" / "investment-subscribers.json"
OFFSET_FILE = ROOT_DIR / "data" / "telegram-offset.json"
WELCOME_MSG = """👋 欢迎关注 *金融日报* @GGmanm_bot
📊 每个交易日自动推送每日投资早报,包含:
• 全球主要指数涨跌
• AI & 科技 / 新能源 / 加密行情
• A股 / 港股最新动态
• 大宗商品(黄金、原油、铜)
• 趋势判断 + 市场情绪
发送 /subscribe 开始接收每日推送
发送 /unsubscribe 取消订阅
🌐 网页版:https://yang1bai.github.io/finance-daily-site/"""
SUBSCRIBE_OK = "✅ 订阅成功!明天早上 8:30 Toronto 时间,你将收到第一份每日投资早报。\n\n发送 /unsubscribe 可随时取消。"
ALREADY_SUBBED = "👌 你已经订阅了,无需重复操作。\n\n发送 /unsubscribe 可取消订阅。"
UNSUBSCRIBE_OK = "🔕 已取消订阅,你不会再收到每日早报推送。\n\n发送 /subscribe 可随时重新订阅。"
NOT_SUBBED = "️ 你当前没有活跃订阅。\n\n发送 /subscribe 开始接收每日投资早报。"
# ─── Helpers ─────────────────────────────────────────────────────────────────
def load_subscribers() -> dict:
try:
return json.loads(SUBS_FILE.read_text(encoding="utf-8"))
except Exception:
return {"subscribers": {}, "stats": {}}
def save_subscribers(data: dict):
SUBS_FILE.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
def load_offset() -> int:
try:
return json.loads(OFFSET_FILE.read_text())["offset"]
except Exception:
return 0
def save_offset(offset: int):
OFFSET_FILE.write_text(json.dumps({"offset": offset}))
def send_message(chat_id: str | int, text: str):
resp = requests.post(f"{API_BASE}/sendMessage", json={
"chat_id": chat_id,
"text": text,
"parse_mode": "Markdown",
}, timeout=10)
return resp.ok
def get_updates(offset: int) -> list:
resp = requests.get(f"{API_BASE}/getUpdates", params={
"offset": offset,
"timeout": 5,
"limit": 100,
}, timeout=15)
if resp.ok:
return resp.json().get("result", [])
return []
# ─── Main ─────────────────────────────────────────────────────────────────────
def main():
print("🤖 Telegram Subscription Handler starting...")
offset = load_offset()
updates = get_updates(offset)
if not updates:
print(" ✅ No new updates")
save_offset(offset) # ensure file exists even when idle
return
data = load_subscribers()
subs = data.setdefault("subscribers", {})
changed = False
for update in updates:
update_id = update["update_id"]
offset = update_id + 1
msg = update.get("message") or update.get("edited_message")
if not msg:
continue
chat_id = str(msg["chat"]["id"])
text = (msg.get("text") or "").strip()
name = msg.get("from", {}).get("first_name", "朋友")
print(f" 📨 [{chat_id}] {text!r}")
# /start (with optional deep-link payload)
if text.startswith("/start"):
payload = text[6:].strip()
if payload in ("subscribe", "sub"):
# Deep-link subscribe
if subs.get(chat_id, {}).get("active"):
send_message(chat_id, ALREADY_SUBBED)
else:
subs[chat_id] = {"active": True, "joined": datetime.now(timezone.utc).strftime("%Y-%m-%d"), "name": name}
send_message(chat_id, SUBSCRIBE_OK)
changed = True
print(f" ✅ Subscribed: {chat_id}")
else:
send_message(chat_id, WELCOME_MSG)
# /subscribe
elif text in ("/subscribe", "/sub"):
if subs.get(chat_id, {}).get("active"):
send_message(chat_id, ALREADY_SUBBED)
else:
subs[chat_id] = {"active": True, "joined": datetime.now(timezone.utc).strftime("%Y-%m-%d"), "name": name}
send_message(chat_id, SUBSCRIBE_OK)
changed = True
print(f" ✅ Subscribed: {chat_id}")
# /unsubscribe
elif text in ("/unsubscribe", "/unsub", "/stop"):
if subs.get(chat_id, {}).get("active"):
subs[chat_id]["active"] = False
subs[chat_id]["left"] = datetime.now(timezone.utc).strftime("%Y-%m-%d")
send_message(chat_id, UNSUBSCRIBE_OK)
changed = True
print(f" 🔕 Unsubscribed: {chat_id}")
else:
send_message(chat_id, NOT_SUBBED)
# /status
elif text == "/status":
sub_info = subs.get(chat_id)
if sub_info and sub_info.get("active"):
stats = data.get("stats", {})
last_bc = stats.get("last_broadcast", "暂无")
msg_text = f"✅ 你已订阅每日投资早报\n📅 加入时间:{sub_info.get('joined','未知')}\n📡 上次推送:{last_bc}"
else:
msg_text = "❌ 你当前未订阅\n\n发送 /subscribe 开始接收每日早报"
send_message(chat_id, msg_text)
save_offset(offset)
if changed:
save_subscribers(data)
count = sum(1 for s in subs.values() if s.get("active"))
print(f" 💾 Saved subscribers ({count} active)")
else:
print(f" ️ No subscription changes ({sum(1 for s in subs.values() if s.get('active'))} active)")
if __name__ == "__main__":
main()