#!/usr/bin/env python3 """Diagnose TelegramForwarder → Hermes forwarding chain. Run inside the container: docker exec telegram-forwarder python3 /app/scripts/diagnose_chain.py Or from host (container must be stopped): cd ~/TelegramForwarder && docker run --rm -v $(pwd)/sessions:/app/sessions \ -v $(pwd)/db:/app/db --network host \ -e HTTP_PROXY=http://127.0.0.1:7890 \ telegramforwarder-telegram-forwarder python3 /app/scripts/diagnose_chain.py """ import sqlite3 import asyncio import socks DB_PATH = '/app/db/forward.db' def check_db(): """Check forwarder database configuration.""" print("=" * 50) print("1. DATABASE CONFIG") print("=" * 50) conn = sqlite3.connect(DB_PATH) c = conn.cursor() print("\n--- Chats ---") for row in c.execute('SELECT id, telegram_chat_id, name FROM chats'): print(f" {row}") print("\n--- Forward Rules ---") for row in c.execute('SELECT id, source_chat_id, target_chat_id, forward_mode, enable_rule FROM forward_rules'): print(f" Rule {row[0]}: source={row[1]} → target={row[2]} | mode={row[3]} | enabled={row[4]}") print("\n--- Keywords ---") for row in c.execute('SELECT * FROM keywords'): print(f" {row}") conn.close() async def check_entity_resolution(): """Test if bot can resolve the target group entity.""" print("\n" + "=" * 50) print("2. ENTITY RESOLUTION TEST") print("=" * 50) try: from telethon import TelegramClient except ImportError: print(" SKIP: telethon not available") return import os api_id = os.getenv('API_ID') api_hash = os.getenv('API_HASH') if not api_id or not api_hash: print(" SKIP: API_ID/API_HASH not set") return proxy = (socks.SOCKS5, '127.0.0.1', 7890) bot = TelegramClient('./sessions/bot', int(api_id), api_hash, proxy=proxy) await bot.start() conn = sqlite3.connect(DB_PATH) c = conn.cursor() targets = c.execute('SELECT id, telegram_chat_id, name FROM chats').fetchall() conn.close() for chat_id, tg_id, name in targets: try: entity = await bot.get_entity(int(tg_id) if not tg_id.startswith('-') else tg_id) deactivated = getattr(entity, 'deactivated', False) migrated = getattr(entity, 'migrated_to', None) if deactivated and migrated: new_id = f"-100{migrated.channel_id}" print(f" ❌ {name} ({tg_id}): DEACTIVATED → migrated to {new_id}") else: print(f" ✅ {name} ({tg_id}): OK - {type(entity).__name__}") except Exception as e: print(f" ❌ {name} ({tg_id}): {e}") await bot.disconnect() async def check_send_permission(): """Test if bot can send to the target group.""" print("\n" + "=" * 50) print("3. SEND PERMISSION TEST") print("=" * 50) try: from telethon import TelegramClient except ImportError: print(" SKIP: telethon not available") return import os api_id = os.getenv('API_ID') api_hash = os.getenv('API_HASH') if not api_id or not api_hash: print(" SKIP: API_ID/API_HASH not set") return proxy = (socks.SOCKS5, '127.0.0.1', 7890) bot = TelegramClient('./sessions/bot', int(api_id), api_hash, proxy=proxy) await bot.start() conn = sqlite3.connect(DB_PATH) c = conn.cursor() rules = c.execute('SELECT id, target_chat_id FROM forward_rules WHERE enable_rule=1').fetchall() conn.close() for rule_id, target_id in rules: conn = sqlite3.connect(DB_PATH) c = conn.cursor() tg_id = c.execute('SELECT telegram_chat_id FROM chats WHERE id=?', (target_id,)).fetchone() conn.close() if not tg_id: print(f" ❌ Rule {rule_id}: target chat {target_id} not found in chats table") continue tg_id = tg_id[0] try: await bot.send_message(int(tg_id) if not tg_id.startswith('-') else tg_id, '🤖 Forward chain diagnostic test') print(f" ✅ Rule {rule_id}: can send to {tg_id}") except Exception as e: print(f" ❌ Rule {rule_id}: cannot send to {tg_id}: {e}") await bot.disconnect() def check_hermes_config(): """Check if Hermes free_response_chats matches.""" print("\n" + "=" * 50) print("4. HERMES CONFIG CHECK") print("=" * 50) import os config_path = os.path.expanduser('~/.hermes/config.yaml') if not os.path.exists(config_path): print(" SKIP: ~/.hermes/config.yaml not found") return with open(config_path) as f: content = f.read() # Extract telegram section in_telegram = False in_extra = False found_correct_key = False found_wrong_key = False for line in content.split('\n'): stripped = line.strip() if stripped == 'telegram:': in_telegram = True continue if in_telegram and line and not line.startswith(' ') and not line.startswith('\t'): in_telegram = False in_extra = False if in_telegram: if stripped == 'extra:': in_extra = True continue if in_extra and not line.startswith(' ') and not line.startswith('\t') and stripped: in_extra = False # CORRECT key: telegram.extra.free_response_chats if in_extra and 'free_response_chats' in line: val = line.split(':')[-1].strip().strip("'\"") found_correct_key = True if val: print(f" ✅ free_response_chats = {val}") else: print(f" ❌ free_response_chats is EMPTY — Hermes will ignore group messages") # WRONG key: telegram.free_response_channels (legacy, ignored by code) if not in_extra and 'free_response_channels' in line: val = line.split(':')[-1].strip().strip("'\"") found_wrong_key = True if val: print(f" ⚠️ free_response_channels = {val} (WRONG KEY — ignored by Hermes code!)") else: print(f" ⚠️ free_response_channels is empty (also wrong key)") if not found_correct_key: print(f" ❌ free_response_chats NOT FOUND in telegram.extra — add it!") if found_wrong_key and not found_correct_key: print(f" 💡 Move free_response_channels value to telegram.extra.free_response_chats") async def main(): check_db() await check_entity_resolution() await check_send_permission() check_hermes_config() print("\n" + "=" * 50) print("DIAGNOSIS COMPLETE") print("=" * 50) if __name__ == '__main__': asyncio.run(main())