ttup / recap_tg_bot.py
Phoe2004's picture
Upload 4 files
02f0dd4 verified
"""
recap_tg_bot.py — Recap Studio Telegram Bot
Uses requests library (urllib SSL fails on HuggingFace)
Env vars:
TELEGRAM_BOT_TOKEN — @BotFather
RECAP_WEBAPP_URL — https://recap.psonline.shop
ADMIN_TELEGRAM_CHAT_ID — numeric Telegram ID
ADMIN_USERNAME — backend admin username
"""
import os, json, time, logging, requests
logging.basicConfig(level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s')
log = logging.getLogger(__name__)
BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN', '')
WEBAPP_URL = os.getenv('RECAP_WEBAPP_URL', 'https://recap.psonline.shop')
ADMIN_ID = os.getenv('ADMIN_TELEGRAM_CHAT_ID', '')
ADMIN_U = os.getenv('ADMIN_USERNAME', '')
API_BASE = f'https://api.telegram.org/bot{BOT_TOKEN}'
if not BOT_TOKEN:
raise RuntimeError('TELEGRAM_BOT_TOKEN not set!')
# ── API helpers ───────────────────────────────────────────────────────────────
def _tg(method, **kw):
try:
r = requests.post(f'{API_BASE}/{method}', json=kw, timeout=30)
data = r.json()
if not data.get('ok'):
log.warning('[BOT] %s not ok: %s', method, data.get('description', ''))
return data
except Exception as e:
log.error('[BOT] %s error: %s', method, e)
return {'ok': False}
def send_msg(chat_id, text, markup=None, parse_mode='HTML'):
kw = {'chat_id': chat_id, 'text': text, 'parse_mode': parse_mode,
'disable_web_page_preview': True}
if markup:
kw['reply_markup'] = markup
r = _tg('sendMessage', **kw)
return r.get('result', {}).get('message_id')
def edit_msg(chat_id, msg_id, text, parse_mode='HTML'):
_tg('editMessageText', chat_id=chat_id, message_id=msg_id,
text=text, parse_mode=parse_mode)
# ── Keyboards ─────────────────────────────────────────────────────────────────
def inline_kb():
return {'inline_keyboard': [[
{'text': '🎬 Recap Studio ဖွင့်မည်', 'web_app': {'url': WEBAPP_URL}}
]]}
def reply_kb():
return {
'keyboard': [[{'text': '🎬 Recap Studio', 'web_app': {'url': WEBAPP_URL}}]],
'resize_keyboard': True, 'persistent': True,
}
# ── Handlers ──────────────────────────────────────────────────────────────────
def on_start(msg):
chat_id = msg['chat']['id']
fname = msg['from'].get('first_name', 'User')
send_msg(chat_id,
f'👋 မင်္ဂလာပါ <b>{fname}</b>!\n\n'
'🎬 <b>Recap Studio</b> မှ ကြိုဆိုပါသည်။\n\n'
'AI ဖြင့် မြန်မာဘာသာ Movie Recap ဗီဒီယိုများ '
'အလိုအလျောက် ထုတ်လုပ်ပေးသော app ဖြစ်သည်။\n\n'
'⬇️ Button နှိပ်၍ ဖွင့်ပါ။',
markup=reply_kb()
)
log.info('/start chat=%s user=%s', chat_id, msg['from'].get('username', fname))
def on_help(msg):
send_msg(msg['chat']['id'],
'<b>📖 Recap Studio — သုံးနည်း</b>\n\n'
'1️⃣ /start နှိပ်ပါ\n'
'2️⃣ <b>🎬 Recap Studio</b> button နှိပ်ပါ\n'
'3️⃣ Video URL ထည့်ပါ\n'
'4️⃣ Settings ချိန်ညှိပြီး <b>Auto Process</b> နှိပ်ပါ\n'
'5️⃣ App ပိတ်၍ progress + video ဤ chat ထဲ ရောက်မည်\n\n'
'<b>💰 Coins:</b>\n'
'• Process တစ်ခု = 1 Coin\n'
'• App ထဲ Buy Coins နှိပ်ဝယ်ပါ\n\n'
'<b>Commands:</b>\n'
'/start — App ဖွင့်မည်\n'
'/coins — Coin လက်ကျန် စစ်မည်\n'
'/help — ဤ message',
markup=inline_kb()
)
def on_coins(msg):
chat_id = msg['chat']['id']
tg_id = str(msg['from']['id'])
try:
r = requests.get(f'{WEBAPP_URL}/api/coins_by_tgid',
params={'tg_id': tg_id}, timeout=10)
d = r.json()
except Exception:
d = {'ok': False}
if d.get('ok'):
send_msg(chat_id,
f"🪙 <b>{d.get('username','')}</b>\n\n"
f"လက်ကျန် Coins: <b>{d.get('coins', 0)}</b>\n\n"
'Coins ဝယ်ရန် App ဖွင့်ပါ 👇',
markup=inline_kb()
)
else:
send_msg(chat_id,
'❌ Account မတွေ့ပါ။\n/start နှိပ်၍ app ကနေ login ဝင်ပါ။',
markup=inline_kb()
)
def on_broadcast(msg):
chat_id = msg['chat']['id']
if str(chat_id) != str(ADMIN_ID):
send_msg(chat_id, '❌ Admin only.')
return
parts = (msg.get('text') or '').split(None, 1)
if len(parts) < 2:
send_msg(chat_id, '❌ Usage: /broadcast <message>')
return
try:
r = requests.get(f'{WEBAPP_URL}/api/admin/tg_users',
params={'caller': ADMIN_U}, timeout=15)
tg_ids = r.json().get('tg_ids', [])
except Exception:
tg_ids = []
sent = 0
for tid in tg_ids:
if send_msg(tid, f'📢 <b>Recap Studio</b>\n\n{parts[1]}'):
sent += 1
time.sleep(0.05)
send_msg(chat_id, f'✅ Sent to {sent}/{len(tg_ids)} users.')
def on_unknown(msg):
send_msg(msg['chat']['id'],
'🎬 Recap Studio ဖွင့်ရန်:',
markup=inline_kb())
# ── Dispatcher ────────────────────────────────────────────────────────────────
def dispatch(update):
msg = update.get('message')
if not msg:
return
text = (msg.get('text') or '').strip()
if not text:
return
cmd = text.split()[0].split('@')[0].lower() if text.startswith('/') else ''
log.info('MSG chat=%s cmd=%r', msg['chat']['id'], cmd or text[:20])
if cmd == '/start': on_start(msg)
elif cmd == '/help': on_help(msg)
elif cmd == '/coins': on_coins(msg)
elif cmd == '/broadcast': on_broadcast(msg)
else: on_unknown(msg)
# ── Startup: clear webhook ────────────────────────────────────────────────────
def clear_webhook():
for attempt in range(1, 4):
try:
r = requests.post(f'{API_BASE}/deleteWebhook',
json={'drop_pending_updates': True}, timeout=15)
if r.json().get('ok'):
log.info('Webhook cleared (attempt %d)', attempt)
return
log.warning('deleteWebhook not ok: %s', r.json())
except Exception as e:
log.warning('deleteWebhook attempt %d failed: %s', attempt, e)
time.sleep(3)
log.error('Could not clear webhook — polling may conflict')
# ── Polling loop ──────────────────────────────────────────────────────────────
def run_polling():
log.info('Recap Studio Bot — clearing webhook...')
clear_webhook()
time.sleep(2)
log.info('Recap Studio Bot — polling started')
offset = 0
while True:
try:
r = requests.get(
f'{API_BASE}/getUpdates',
params={'offset': offset, 'timeout': 25,
'allowed_updates': ['message']},
timeout=30
)
data = r.json()
if not data.get('ok'):
desc = data.get('description', '')
log.warning('getUpdates not ok: %s', desc)
time.sleep(15 if 'conflict' in desc.lower() else 5)
continue
for upd in data.get('result', []):
offset = upd['update_id'] + 1
try:
dispatch(upd)
except Exception as e:
log.error('dispatch error: %s', e)
except requests.exceptions.ReadTimeout:
pass
except requests.exceptions.ConnectionError as e:
log.error('Connection error: %s — retry 10s', e)
time.sleep(10)
except Exception as e:
log.error('Polling error: %s', e)
time.sleep(5)
if __name__ == '__main__':
run_polling()