pmrony commited on
Commit
bcaebd6
·
verified ·
1 Parent(s): 43cfb9d

Upload 8 files

Browse files
Files changed (8) hide show
  1. config.py +16 -1
  2. credentials.py +29 -10
  3. db.py +21 -7
  4. lang_bn.py +16 -1
  5. lang_en.py +15 -0
  6. lang_hi.py +15 -1
  7. main.py +202 -106
  8. requirements.txt +7 -9
config.py CHANGED
@@ -31,7 +31,7 @@ LINK_PATTERN_COMPILED = re.compile(
31
  r'com|net|org|edu|gov|mil|biz|info|mobi|name|' # সাধারণ TLDs
32
  r'xyz|club|site|online|link|vip|pro|top|click|' # স্প্যামারদের সস্তা TLDs
33
  r'shop|store|app|dev|me|tv|cc|ws|to|io|gg|bot|' # টেক/গেমিং TLDs
34
- r'tech|live|space|icu|cam|'
35
  r')\b' # বাউন্ডারি
36
  r'|' # অথবা
37
  r'(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' # সরাসরি IP Address ব্লক (192.168.1.1)
@@ -76,6 +76,21 @@ DEFAULT_SPAM_PHRASES = [
76
  "get paid to chat", "make easy money", "instant cash", "𝐜𝐡𝐢𝐥𝐝 𝐩𝐨𝐫𝐧", "𝘀𝗲𝗹𝗹𝗲𝗿", "L0W PRlCE", "crypto payment"
77
  ]
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  # --- Keywords to detect links in OCR text ---
80
  LINK_INDICATOR_KEYWORDS = ['t.me', 'tme', 'http', 'https', 'www', '.com', '.net', '.org', '.xyz', '.gg', '.io', '@']
81
 
 
31
  r'com|net|org|edu|gov|mil|biz|info|mobi|name|' # সাধারণ TLDs
32
  r'xyz|club|site|online|link|vip|pro|top|click|' # স্প্যামারদের সস্তা TLDs
33
  r'shop|store|app|dev|me|tv|cc|ws|to|io|gg|bot|' # টেক/গেমিং TLDs
34
+ r'tech|live|space|icu|cam'
35
  r')\b' # বাউন্ডারি
36
  r'|' # অথবা
37
  r'(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' # সরাসরি IP Address ব্লক (192.168.1.1)
 
76
  "get paid to chat", "make easy money", "instant cash", "𝐜𝐡𝐢𝐥𝐝 𝐩𝐨𝐫𝐧", "𝘀𝗲𝗹𝗹𝗲𝗿", "L0W PRlCE", "crypto payment"
77
  ]
78
 
79
+ # --- Bio Check Settings ---
80
+ # এই শব্দগুলো মেসেজে থাকলে বট প্রোফাইল চেক করবে
81
+ BIO_TRIGGER_WORDS = [
82
+ # English
83
+ 'bio', 'profile', 'link', 'see', 'look', 'check', 'visit', 'pinned', 'join',
84
+
85
+ # Bengali (বায়ো দেখার অনুরোধ)
86
+ 'বায়ো', 'প্রোফাইল', 'লিংক', 'লিন্ক', 'দেখো', 'দেখুন', 'পিন', 'ইনবক্স', 'চেক', 'জয়েন', 'জয়েন',
87
+
88
+ # Hindi (प्रोफाइल देखने के लिए शब्द)
89
+ 'dekho', 'deko', 'dekhiye', 'link', 'bio', 'profile', 'check',
90
+ 'बायो', 'प्रोफाइल', 'लिंक', 'देखो', 'देखिये', 'चेक'
91
+ ]
92
+ BIO_TRIGGER_REGEX = re.compile(r'\b(?:' + '|'.join(re.escape(word) for word in BIO_TRIGGER_WORDS) + r')\b', re.IGNORECASE)
93
+
94
  # --- Keywords to detect links in OCR text ---
95
  LINK_INDICATOR_KEYWORDS = ['t.me', 'tme', 'http', 'https', 'www', '.com', '.net', '.org', '.xyz', '.gg', '.io', '@']
96
 
credentials.py CHANGED
@@ -1,10 +1,29 @@
1
- import os
2
-
3
- # Hugging Face Secrets থেকে ডটা সংগ্র হচ্ছ
4
- BOT_TOKEN = os.getenv("BOT_TOKEN")
5
- BOT_OWNER_ID = os.getenv("BOT_OWNER_ID")
6
- REPORT_CHANNEL_ID = os.getenv("REPORT_CHANNEL_ID")
7
- DATABASE_URL = os.getenv("DATABASE_URL")
8
- OCR_API_URL = os.getenv("OCR_API_URL")
9
- REDIS_URL_1 = os.getenv("REDIS_URL_1")
10
- REDIS_URL_2 = os.getenv("REDIS_URL_2")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --- START OF FILE credentials.py (FINAL FIX) ---
2
+
3
+ # আপন টেলিগ্রাম বটেটোক
4
+ BOT_TOKEN = "8736442113:AAGvSnV64n4Lg7aoJ3dWZ4UXBpKWaQ3cdSk"
5
+
6
+ # আপনার টেলিগ্রাম ইউজার আইডি (বটের মালিক)
7
+ BOT_OWNER_ID = "7273609587"
8
+
9
+ # আপনার রিপোর্ট পাঠানোর চ্যানেলের আইডি
10
+ REPORT_CHANNEL_ID = "-1003709977763"
11
+
12
+ # আপনার supabase ডাটাবেসের লিঙ্ক
13
+ DATABASE_URL = "postgresql://postgres.xlbhfttkepdkhprxupbg:yYrmVvtBD9WaQTK6@aws-1-us-east-2.pooler.supabase.com:6543/postgres"
14
+ # আপনার OCR API-এর লিঙ্ক
15
+ OCR_API_URL = "https://pmrony-my-easyocr-api.hf.space/ocr-from-image/"
16
+
17
+ # --- FIX: আপনার Redis cloud amronyvai এর সঠিক URL ---
18
+ # "redis-cli --tls -u" অংশটি বাদ দেওয়া হয়েছে
19
+ REDIS_URL_1 = "redis://default:A5BhOdPYRxV73r9uoKvbEYhibjLWf1Fp@redis-18961.c264.ap-south-1-1.ec2.cloud.redislabs.com:18961"
20
+
21
+
22
+ # --- FIX: আপনার Redis cloud maronyvai এর সঠিক URL --
23
+ REDIS_URL_2 = "redis://default:rDT4ZMYn6zbiv@p@redis-14733.c301.ap-south-1-1.ec2.cloud.redislabs.com:14733"
24
+
25
+ # --- AI Features ---
26
+ GEMINI_API_KEY = "AIzaSyCiBZVtFnFkyHWL_YH0lefRiMl50s4bGrE"
27
+ REDIS_URL_AI = "rediss://default:AVNS_1HXzC8VrviAwOf2jIVc@valkey-2ee15c50-maronyvai-dff2.l.aivencloud.com:21982"
28
+
29
+ # --- END OF FILE credentials.py ---
db.py CHANGED
@@ -67,22 +67,26 @@ async def setup_database(pool):
67
  warn_limit INTEGER DEFAULT 0,
68
  warn_time_window INTEGER DEFAULT 10,
69
  warn_action TEXT DEFAULT 'mute'
 
70
  )""")
71
 
72
  # 🔥 FIX: Missing columns fix for group_settings (Auto-Warn Feature)
73
  # এটি পুরাতন ডাটাবেসে নতুন কলামগুলো যোগ করে দিবে যাতে /setwarn কাজ করে
 
74
  for col, dtype in [
75
- ("warn_limit", "INTEGER DEFAULT 0"),
76
- ("warn_time_window", "INTEGER DEFAULT 10"),
77
- ("warn_action", "TEXT DEFAULT 'mute'")
78
- ]:
 
79
  try: await conn.execute(f"ALTER TABLE group_settings ADD COLUMN {col} {dtype}")
80
  except: pass
81
 
82
  # Filters & Notes
83
  await conn.execute("CREATE TABLE IF NOT EXISTS filters (chat_id BIGINT, keyword TEXT, reply_text TEXT, file_id TEXT, file_type TEXT, PRIMARY KEY (chat_id, keyword))")
84
  await conn.execute("CREATE TABLE IF NOT EXISTS notes (chat_id BIGINT, slug TEXT, content TEXT, file_id TEXT, file_type TEXT, PRIMARY KEY (chat_id, slug))")
85
- await conn.execute("CREATE TABLE IF NOT EXISTS chats (chat_id BIGINT PRIMARY KEY, added_on TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP)")
 
86
 
87
  # VIP SYSTEM
88
  await conn.execute("""
@@ -441,7 +445,7 @@ async def get_all_user_ids(pool):
441
  return [r['user_id'] for r in records]
442
  except: return []
443
 
444
- # --- db.py এর একদম শেষে এই ফাংশনটি যোগ করুন ---
445
 
446
  async def create_db_pool_with_retry(max_retries=5, delay=5):
447
  db_url = DATABASE_URL
@@ -467,7 +471,6 @@ async def track_chat_member(pool, chat_id, user_id):
467
  if not pool: return
468
  try:
469
  async with pool.acquire() as conn:
470
- # টেবিলটি ইনশিওর করার জন্য ON CONFLICT ব্যবহার
471
  await conn.execute("""
472
  INSERT INTO chat_members (chat_id, user_id, last_seen)
473
  VALUES ($1, $2, CURRENT_TIMESTAMP)
@@ -477,4 +480,15 @@ async def track_chat_member(pool, chat_id, user_id):
477
  except Exception as e:
478
  logger.error(f"❌ Track Member DB Error: {e}")
479
 
 
 
 
 
 
 
 
 
 
 
 
480
  # --- END OF FILE db.py ---
 
67
  warn_limit INTEGER DEFAULT 0,
68
  warn_time_window INTEGER DEFAULT 10,
69
  warn_action TEXT DEFAULT 'mute'
70
+
71
  )""")
72
 
73
  # 🔥 FIX: Missing columns fix for group_settings (Auto-Warn Feature)
74
  # এটি পুরাতন ডাটাবেসে নতুন কলামগুলো যোগ করে দিবে যাতে /setwarn কাজ করে
75
+ # Line 74 থেকে শুরু হবে:
76
  for col, dtype in [
77
+ ("force_join_channel", "TEXT DEFAULT NULL"),
78
+ ("warn_limit", "INTEGER DEFAULT 0"),
79
+ ("warn_time_window", "INTEGER DEFAULT 10"),
80
+ ("warn_action", "TEXT DEFAULT 'mute'")
81
+ ]:
82
  try: await conn.execute(f"ALTER TABLE group_settings ADD COLUMN {col} {dtype}")
83
  except: pass
84
 
85
  # Filters & Notes
86
  await conn.execute("CREATE TABLE IF NOT EXISTS filters (chat_id BIGINT, keyword TEXT, reply_text TEXT, file_id TEXT, file_type TEXT, PRIMARY KEY (chat_id, keyword))")
87
  await conn.execute("CREATE TABLE IF NOT EXISTS notes (chat_id BIGINT, slug TEXT, content TEXT, file_id TEXT, file_type TEXT, PRIMARY KEY (chat_id, slug))")
88
+ try: await conn.execute("ALTER TABLE chats ADD COLUMN chat_title TEXT")
89
+ except: pass
90
 
91
  # VIP SYSTEM
92
  await conn.execute("""
 
445
  return [r['user_id'] for r in records]
446
  except: return []
447
 
448
+ # --- db.py এর একদম শেষে এই ফাংশনগুলো থাবে ---
449
 
450
  async def create_db_pool_with_retry(max_retries=5, delay=5):
451
  db_url = DATABASE_URL
 
471
  if not pool: return
472
  try:
473
  async with pool.acquire() as conn:
 
474
  await conn.execute("""
475
  INSERT INTO chat_members (chat_id, user_id, last_seen)
476
  VALUES ($1, $2, CURRENT_TIMESTAMP)
 
480
  except Exception as e:
481
  logger.error(f"❌ Track Member DB Error: {e}")
482
 
483
+ async def update_chat_title(pool, chat_id, title):
484
+ if not pool or not title: return
485
+ try:
486
+ async with pool.acquire() as conn:
487
+ await conn.execute("""
488
+ INSERT INTO chats (chat_id, chat_title) VALUES ($1, $2)
489
+ ON CONFLICT (chat_id) DO UPDATE SET chat_title = EXCLUDED.chat_title
490
+ """, chat_id, title)
491
+ except Exception as e:
492
+ pass
493
+
494
  # --- END OF FILE db.py ---
lang_bn.py CHANGED
@@ -307,7 +307,22 @@ BN_LANGUAGE_DATA = {
307
  "welcome_set_error_pairs": "⚠️ বাটনের নাম এবং লিংক/লেখা জোড়ায় জোড়ায় হতে হবে।",
308
  "welcome_set_success": "✅ <b>ওয়েলকাম সেট করা হয়েছে!</b>\n\n📝 <b>টেক্সট:</b> {text}...\n🔘 <b>বাটন:</b> {count} টি\n⏱ <b>টাইমার:</b> {time}",
309
  "welcome_set_mode_replace": "আগের মেসেজ রিপ্লেস হবে",
310
- "button_expired": "❌ বাটনটি আর কাজ করছে না বা মুছে ফেলা হয়েছে।"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  }
312
 
313
  # --- END OF FILE lang_bn.py ---
 
307
  "welcome_set_error_pairs": "⚠️ বাটনের নাম এবং লিংক/লেখা জোড়ায় জোড়ায় হতে হবে।",
308
  "welcome_set_success": "✅ <b>ওয়েলকাম সেট করা হয়েছে!</b>\n\n📝 <b>টেক্সট:</b> {text}...\n🔘 <b>বাটন:</b> {count} টি\n⏱ <b>টাইমার:</b> {time}",
309
  "welcome_set_mode_replace": "আগের মেসেজ রিপ্লেস হবে",
310
+ "button_expired": "❌ বাটনটি আর কাজ করছে না বা মুছে ফেলা হয়েছে।",
311
+
312
+ # --- Force Join ---
313
+ "forcejoin_button_label": "📢 জয়েন ফোর্স",
314
+ "forcejoin_toggle_alert": "⚠️ এটি চালু করতে আপনার চ্যানেলের একটি মেসেজ এই বটের ইনবক্সে Forward করুন।",
315
+ "forcejoin_example_full_text": (
316
+ "<b>📢 ফোর্স সাবস্ক্রাইব</b>\n\n"
317
+ "গ্রুপে মেসেজ পাঠানোর পূর্বে সদস্যদের নির্ধারিত চ্যানেলে যুক্ত হওয়া বাধ্যতামূলক করতে এই ফিচারটি ব্যবহার করুন।\n\n"
318
+ "<b>⚙️ কনফিগারেশন:</b>\n"
319
+ "চ্যানেলটি সিঙ্ক করতে আপনার চ্যানেল থেকে যেকোনো একটি মেসেজ এখানে <b>Forward</b> করুন।\n\n"
320
+ "⚠️ <b>শর্ত:</b> বটকে অবশ্যই উক্ত চ্যানেলের <b>Admin</b> হতে হবে।"
321
+ ),
322
+ "forcejoin_no_group_selected": "⚠️ <b>গ্রুপ সিলেক্ট করা নেই!</b>\nআগে 'Manage My Groups' থেকে একটি গ্রুপ সিলেক্ট করুন, তারপর চ্যানেলের মেসেজ ফরোয়ার্ড করুন।",
323
+ "forcejoin_not_admin": "❌ <b>বট অ্যাডমিন নয়!</b>\nদয়া করে আমাকে <b>{title}</b> চ্যানেলে অ্যাডমিন বানিয়ে আবার ট্রাই করুন।",
324
+ "forcejoin_access_error": "❌ <b>এক্সেস এরর!</b>\nআমাকে ওই চ্যানেলে অ্যাডমিন করা আছে তো?",
325
+ "forcejoin_link_success": "✅ <b>সফলভাবে লিঙ্ক করা হয়েছে!</b>\n\n🏢 <b>গ্রুপ:</b> {g_title}\n📢 <b>চ্যানেল:</b> {c_title}\n🆔 <b>ID:</b> <code>{c_id}</code>\n\n<i>এখন থেকে এই চ্যানেলে জয়েন না থাকলে কেউ ওই গ্রুপে মেসেজ দিতে পারবে না।</i>",
326
  }
327
 
328
  # --- END OF FILE lang_bn.py ---
lang_en.py CHANGED
@@ -310,6 +310,21 @@ EN_LANGUAGE_DATA = {
310
  "welcome_set_success": "✅ <b>Welcome Message Set!</b>\n\n📝 <b>Text:</b> {text}...\n🔘 <b>Buttons:</b> {count}\n⏱ <b>Timer:</b> {time}",
311
  "welcome_set_mode_replace": "Previous message will be replaced",
312
  "button_expired": "❌ This button is no longer working.",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  }
314
 
315
  # --- END OF FILE lang_en.py ---
 
310
  "welcome_set_success": "✅ <b>Welcome Message Set!</b>\n\n📝 <b>Text:</b> {text}...\n🔘 <b>Buttons:</b> {count}\n⏱ <b>Timer:</b> {time}",
311
  "welcome_set_mode_replace": "Previous message will be replaced",
312
  "button_expired": "❌ This button is no longer working.",
313
+
314
+ # --- Force Join ---
315
+ "forcejoin_button_label": "📢 Force Join",
316
+ "forcejoin_toggle_alert": "⚠️ To enable: Forward any message from your channel to this bot chat.",
317
+ "forcejoin_example_full_text": (
318
+ "<b>📢 Force Subscribe</b>\n\n"
319
+ "Use this feature to make channel subscription mandatory before members can message in the group.\n\n"
320
+ "<b>⚙️ Configuration:</b>\n"
321
+ "To sync your channel, simply <b>Forward</b> any message from that channel to this bot.\n\n"
322
+ "⚠️ <b>Note:</b> Bot must be an <b>Admin</b> in the selected channel."
323
+ ),
324
+ "forcejoin_no_group_selected": "⚠️ <b>No group selected!</b>\nPlease select a group from 'Manage My Groups' first, then forward the channel message.",
325
+ "forcejoin_not_admin": "❌ <b>Bot is not Admin!</b>\nPlease make me an Admin in <b>{title}</b> and try again.",
326
+ "forcejoin_access_error": "❌ <b>Access Error!</b>\nAm I an Admin in that channel?",
327
+ "forcejoin_link_success": "✅ <b>Successfully Linked!</b>\n\n🏢 <b>Group:</b> {g_title}\n📢 <b>Channel:</b> {c_title}\n🆔 <b>ID:</b> <code>{c_id}</code>\n\n<i>Now members must join this channel to chat in the group.</i>",
328
  }
329
 
330
  # --- END OF FILE lang_en.py ---
lang_hi.py CHANGED
@@ -307,7 +307,21 @@ HI_LANGUAGE_DATA = {
307
  "welcome_set_error_pairs": "⚠️ बटन का नाम और लिंक जोड़े में होना चाहिए।",
308
  "welcome_set_success": "✅ <b>वेलकम सेट हो गया!</b>\n\n📝 <b>टेक्स्ट:</b> {text}...\n🔘 <b>बटन:</b> {count}\n⏱ <b>टाइमर:</b> {time}",
309
  "welcome_set_mode_replace": "पिछला संदेश बदला जाएगा",
310
- "button_expired": "❌ यह बटन अब काम नहीं कर रहा है।"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  }
312
 
313
  # --- END OF FILE lang_hi.py ---
 
307
  "welcome_set_error_pairs": "⚠️ बटन का नाम और लिंक जोड़े में होना चाहिए।",
308
  "welcome_set_success": "✅ <b>वेलकम सेट हो गया!</b>\n\n📝 <b>टेक्स्ट:</b> {text}...\n🔘 <b>बटन:</b> {count}\n⏱ <b>टाइमर:</b> {time}",
309
  "welcome_set_mode_replace": "पिछला संदेश बदला जाएगा",
310
+ "button_expired": "❌ यह बटन अब काम नहीं कर रहा है।",
311
+ # --- Force Join ---
312
+ "forcejoin_button_label": "📢 जॉइन फोर्स",
313
+ "forcejoin_toggle_alert": "⚠️ इसे चालू करने के लिए अपने चैनल का कोई भी मैसेज इस बॉट को Forward करें।",
314
+ "forcejoin_example_full_text": (
315
+ "<b>📢 फ़ोर्स सब्सक्राइब</b>\n\n"
316
+ "ग्रुप में मैसेज भेजने से पहले सदस्यों के लिए चैनल से जुड़ना अनिवार्य बनाने के लिए इस फीचर का उपयोग करें।\n\n"
317
+ "<b>⚙️ कॉन्फ़िगरेशन:</b>\n"
318
+ "चैनल को सिंक करने के लिए अपने चैनल से कोई भी मैसेज यहाँ <b>Forward</b> करें।\n\n"
319
+ "⚠️ <b>नोट:</b> बॉट का उक्त चैनल में <b>Admin</b> होना अनिवार्य है।"
320
+ ),
321
+ "forcejoin_no_group_selected": "⚠️ <b>ग्रुप चुना नहीं गया है!</b>\nपहले 'Manage My Groups' से एक ग्रुप चुनें, फिर चैनल मैसेज फॉरवर्ड करें।",
322
+ "forcejoin_not_admin": "❌ <b>बॉट एडमिन नहीं है!</b>\nकृपया मुझे <b>{title}</b> चैनल में एडमिन बनाएं और फिर से कोशिश करें।",
323
+ "forcejoin_access_error": "❌ <b>एक्सेस एरर!</b>\nक्या मुझे उस चैनल में एडमिन बनाया गया है?",
324
+ "forcejoin_link_success": "✅ <b>सफलतापूर्वक लिंक किया गया!</b>\n\n🏢 <b>ग्रुप:</b> {g_title}\n📢 <b>चैनल:</b> {c_title}\n🆔 <b>ID:</b> <code>{c_id}</code>\n\n<i>अब इस चैनल में शामिल हुए बिना कोई भी ग्रुप में मैसेज नहीं भेज पाएगा।</i>",
325
  }
326
 
327
  # --- END OF FILE lang_hi.py ---
main.py CHANGED
@@ -1,125 +1,221 @@
 
1
  import logging
 
2
  import asyncio
3
  import threading
4
- import socket
5
- import http.server
6
- import socketserver
 
7
  from datetime import datetime, timedelta, timezone
 
8
 
9
- # ১. IPv4 Force Patch (Hugging Face-এর নেটওয়ার্ক ব্লকিং বাইপাস করার জন্য)
10
- old_getaddrinfo = socket.getaddrinfo
11
- def new_getaddrinfo(*args, **kwargs):
12
- responses = old_getaddrinfo(*args, **kwargs)
13
- return [r for r in responses if r[0] == socket.AF_INET]
14
- socket.getaddrinfo = new_getaddrinfo
15
 
16
- # Telebot Async version ইম্পোর্ট
17
- from telebot.async_telebot import AsyncTeleBot
18
- from telebot import types
 
 
19
 
20
- # আপনার প্রজেক্টের ফাইলগুলো থেকে ইম্পোর্ট
21
- from credentials import BOT_TOKEN, BOT_OWNER_ID, REPORT_CHANNEL_ID, REDIS_URL_1, REDIS_URL_2
22
  import config
23
- from config import logger, BOT_VERSION
24
  from db import create_db_pool_with_retry, setup_database
25
- import db
26
- import redis.asyncio as redis
27
  from bot_handlers import utils
28
 
29
- # বট অবজেক্ট তৈরি
30
- bot = AsyncTeleBot(BOT_TOKEN.strip())
31
-
32
- # --- ২. Hugging Face Health Check Server (Port 7860) ---
33
- def run_web_server():
34
- class HealthCheckHandler(http.server.SimpleHTTPRequestHandler):
35
- def do_GET(self):
36
- self.send_response(200); self.end_headers()
37
- self.wfile.write(f"Telebot is Running! Version: {BOT_VERSION}".encode('utf-8'))
38
- def log_message(self, *args): pass
39
- try:
40
- socketserver.TCPServer(("0.0.0.0", 7860), HealthCheckHandler).serve_forever()
41
- except: pass
42
-
43
- # --- ৩. ব্যাকগ্রাউন্ড টাস্কসমূহ ---
44
-
45
- async def background_tasks_loop():
46
- """বট স্টার্ট হওয়ার পর এই লুপগুলো ব্যাকগ্রাউন্ডে চলবে"""
47
- # ডাটাবেস রেডিয়াস সেটআপ
48
- bot.db_pool = await create_db_pool_with_retry()
49
- await setup_database(bot.db_pool)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
- bot.redis_clients = []
52
- for url in [REDIS_URL_1, REDIS_URL_2]:
53
  if url:
54
  try:
55
- r = redis.Redis.from_url(url, decode_responses=True)
56
- await r.ping(); bot.redis_clients.append(r)
57
  except: pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- logger.info("✅ Database & Redis connected in background!")
60
-
61
- # এখানে আপনার অন্যান্য লুপ (Stats flush, Expired trial) গুলো asyncio.create_task দিয়ে চালু করতে পারেন
62
- # উদাহরণ: asyncio.create_task(user_stats_flush_loop())
63
-
64
- # --- ৪. কমান্ড ও মেসেজ হ্যান্ডেলার্স ---
65
-
66
- @bot.message_handler(commands=['start'])
67
- async def start_cmd(message):
68
- user = message.from_user
69
- # ডাটাবেসে ইউজার সেভ করার লজিক (আপনার আগের কোড অনুযায়ী)
70
- if hasattr(bot, 'db_pool'):
71
- asyncio.create_task(bot.db_pool.execute(
72
- "INSERT INTO users (user_id, first_name, has_started_bot) VALUES ($1, $2, TRUE) ON CONFLICT DO UPDATE SET has_started_bot = TRUE",
73
- user.id, user.first_name
74
- ))
75
 
76
- markup = types.InlineKeyboardMarkup()
77
- markup.add(types.InlineKeyboardButton("English 🇬🇧", callback_data="lang_en"),
78
- types.InlineKeyboardButton("বাংলা 🇧🇩", callback_data="lang_bn"))
79
- await bot.reply_to(message, f"👋 Hello {user.first_name}!\nChoose Language:", reply_markup=markup)
80
-
81
- # জয়েন রিকোয়েস্ট হ্যান্ডেলার
82
- @bot.chat_join_request_handler()
83
- async def handle_join(request):
84
- # আপনার জয়েন রিকোয়েস্ট লজিক এখানে লিখুন
85
- await bot.approve_chat_join_request(request.chat.id, request.from_user.id)
86
-
87
- # অল মেসেজ হ্যান্ডেলার (আপনার ফিল্টার ও কোর লজিক ব্যবহারের জন্য)
88
- @bot.message_handler(func=lambda message: True, content_types=['text', 'photo', 'document', 'video'])
89
- async def handle_all_messages(message):
90
- # আপনার bot_handlers.core.handle_message কল করুন
91
- from bot_handlers.core import handle_message
92
- # Telebot এবং python-telegram-bot এর অবজেক্ট আলাদা, তাই সরাসরি কাজ না করলে র‍্যাপার লাগবে
93
- await handle_message(message, bot)
94
-
95
- # কলব্যাক কুয়েরি হ্যান্ডেলার
96
- @bot.callback_query_handler(func=lambda call: True)
97
- async def handle_callback(call):
98
- from bot_handlers.callbacks import w_callback
99
- await w_callback(call, bot)
100
-
101
- # --- ৫. মেইন রানার ---
102
-
103
- async def main():
104
- if not BOT_TOKEN: return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
- # ওয়েব সার্ভার শুরু
 
 
 
 
 
 
 
107
  threading.Thread(target=run_web_server, daemon=True).start()
108
-
109
- # ব্যাকগ্রাউন্ড টাস্ক শুরু
110
- asyncio.create_task(background_tasks_loop())
111
-
112
- logger.info("🚀 Telebot is starting in Async Mode...")
113
-
114
- # পোলিং শুরু (এটি অটোমেটিক রিস্টার্ট এবং টাইমআউট হ্যান্ডেল করে)
115
- try:
116
- await bot.polling(non_stop=True, interval=0, timeout=60)
117
- except Exception as e:
118
- logger.error(f"Polling Error: {e}")
119
- await asyncio.sleep(5)
120
-
121
- if __name__ == '__main__':
122
- try:
123
- asyncio.run(main())
124
- except KeyboardInterrupt:
125
- pass
 
1
+ # --- START OF FILE main.py ---
2
  import logging
3
+ import os
4
  import asyncio
5
  import threading
6
+ import re
7
+ from fastapi import FastAPI
8
+ import uvicorn
9
+ import redis.asyncio as redis
10
  from datetime import datetime, timedelta, timezone
11
+ from concurrent.futures import ThreadPoolExecutor
12
 
13
+ try:
14
+ import uvloop
15
+ asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
16
+ except ImportError: pass
 
 
17
 
18
+ from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ChatMember, ChatPermissions
19
+ from telegram.ext import (
20
+ Application, CommandHandler, MessageHandler, ChatJoinRequestHandler,
21
+ filters, ChatMemberHandler, ContextTypes, CallbackQueryHandler
22
+ )
23
 
24
+ import credentials
 
25
  import config
26
+ from config import logger, BOT_VERSION, TELEGRAM_API_CONCURRENCY_LIMIT
27
  from db import create_db_pool_with_retry, setup_database
28
+ import db
29
+ from bot_handlers.batch import BatchDeleter
30
  from bot_handlers import utils
31
 
32
+ app_web = FastAPI()
33
+ @app_web.get("/")
34
+ def read_root(): return {"status": "Bot is alive!", "version": BOT_VERSION}
35
+
36
+ def run_web_server(): uvicorn.run(app_web, host="0.0.0.0", port=7860)
37
+
38
+ async def global_error_handler(update: object, context: ContextTypes.DEFAULT_TYPE):
39
+ logger.error("Exception while handling an update:", exc_info=context.error)
40
+
41
+ async def user_stats_flush_loop(app):
42
+ while True:
43
+ await asyncio.sleep(300)
44
+ await db.flush_user_stats_to_db(app)
45
+
46
+ async def check_expired_trials_loop(app):
47
+ while True:
48
+ await asyncio.sleep(60)
49
+ pool = app.bot_data.get('db_pool')
50
+ if not pool: continue
51
+ try:
52
+ async with pool.acquire() as conn:
53
+ now_utc = datetime.now(timezone.utc)
54
+ expired_vips = await conn.fetch("SELECT chat_id, user_id FROM vip_trials WHERE expiry_date < $1 AND is_safe = FALSE", now_utc)
55
+ for row in expired_vips:
56
+ try:
57
+ await app.bot.ban_chat_member(row['chat_id'], row['user_id'])
58
+ await app.bot.unban_chat_member(row['chat_id'], row['user_id'])
59
+ except: pass
60
+ await conn.execute("DELETE FROM vip_trials WHERE expiry_date < $1", now_utc)
61
+ except Exception as e: logger.error(f"VIP Monitor Error: {e}")
62
+
63
+ async def recovery_traffic_locks(app):
64
+ pool = app.bot_data.get('db_pool')
65
+ if not pool: return
66
+ group_ids = await db.get_all_group_ids(pool)
67
+ for chat_id in group_ids:
68
+ from bot_handlers.redis_manager import get_redis_client_for_chat
69
+ redis_client = get_redis_client_for_chat(app, chat_id)
70
+ if redis_client and (backup := await redis_client.get(f"backup_perms:{chat_id}")):
71
+ from bot_handlers.utils import restore_group_permissions
72
+ await restore_group_permissions(app, chat_id, backup)
73
+
74
+ async def background_bio_task(app):
75
+ redis_client = app.bot_data.get('redis_clients')[0] if app.bot_data.get('redis_clients') else None
76
+ while True:
77
+ try:
78
+ data = await app.bot_data['bio_queue'].get()
79
+ chat_id, user_id, msg_id, settings, user_obj = data
80
+ await asyncio.sleep(0.2)
81
+ full_user = await app.bot.get_chat(user_id)
82
+ bio_text = getattr(full_user, 'bio', "") or ""
83
+ full_profile_text = f"{full_user.first_name} {getattr(full_user, 'last_name', '')} {bio_text}".strip()
84
+ is_bad = utils.has_link_in_text(full_profile_text) or getattr(full_user, 'personal_chat', None)
85
+ if redis_client: await redis_client.set(f"bio_status:{user_id}", "bad" if is_bad else "safe", ex=300)
86
+ if is_bad:
87
+ try: await app.bot.delete_message(chat_id, msg_id)
88
+ except: pass
89
+ except: pass
90
+ finally: app.bot_data['bio_queue'].task_done()
91
+
92
+ async def handle_join_request(update: Update, context: ContextTypes.DEFAULT_TYPE):
93
+ req = update.chat_join_request
94
+ user, chat = req.from_user, req.chat
95
+ pool = context.bot_data.get('db_pool')
96
+ if user.username:
97
+ clients = context.bot_data.get('redis_clients')
98
+ if clients: await clients[0].set(f"join_cache:{user.username.lower()}", user.id, ex=2592000)
99
+ if pool:
100
+ try:
101
+ temp_expiry = datetime.now(timezone.utc) + timedelta(minutes=10)
102
+ async with pool.acquire() as conn:
103
+ await conn.execute("INSERT INTO vip_trials (chat_id, user_id, expiry_date, joined_at, is_safe) VALUES ($1, $2, $3, CURRENT_TIMESTAMP, TRUE) ON CONFLICT (chat_id, user_id) DO UPDATE SET expiry_date = EXCLUDED.expiry_date, is_safe = TRUE", chat.id, user.id, temp_expiry)
104
+ except: pass
105
+
106
+ async def safe_start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
107
+ user = update.effective_user
108
+ if update.effective_chat.type != 'private': return
109
+ pool = context.bot_data.get('db_pool')
110
+ if pool: asyncio.create_task(pool.execute("INSERT INTO users (user_id, first_name, username, has_started_bot, last_active_on) VALUES ($1, $2, $3, TRUE, CURRENT_TIMESTAMP) ON CONFLICT (user_id) DO UPDATE SET has_started_bot = TRUE", user.id, user.first_name, user.username))
111
+ keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("English 🇬🇧", callback_data='lang_en'), InlineKeyboardButton("বাংলা 🇧🇩", callback_data='lang_bn')], [InlineKeyboardButton("हिन्दी 🇮🇳", callback_data='lang_hi')]])
112
+ await update.message.reply_html(f"👋 <b>Hello {user.first_name}!</b>\n\nPlease choose language:", reply_markup=keyboard)
113
+
114
+ async def w_bot_status(u, c): from bot_handlers.core import handle_bot_status_change; await handle_bot_status_change(u, c)
115
+ async def w_member_status(u, c): from bot_handlers.core import handle_member_status_change; await handle_member_status_change(u, c)
116
+ async def w_msg(u, c): from bot_handlers.core import handle_message; await handle_message(u, c)
117
+ async def w_edit_msg(u, c): from bot_handlers.core import handle_edited_message; await handle_edited_message(u, c)
118
+ async def w_callback(u, c): from bot_handlers import callbacks; await callbacks.w_callback(u, c)
119
+
120
+ async def post_init(app: Application):
121
+ app.bot_data['db_pool'] = await create_db_pool_with_retry()
122
+ await setup_database(app.bot_data['db_pool'])
123
 
124
+ app.bot_data['redis_clients'] = []
125
+ for url in [credentials.REDIS_URL_1, credentials.REDIS_URL_2]:
126
  if url:
127
  try:
128
+ r = redis.Redis.from_url(url, decode_responses=True); await r.ping()
129
+ app.bot_data['redis_clients'].append(r)
130
  except: pass
131
+
132
+ # --- AI Redis Connection (From credentials.py) ---
133
+ if hasattr(credentials, 'REDIS_URL_AI') and credentials.REDIS_URL_AI:
134
+ try:
135
+ r_ai = redis.Redis.from_url(credentials.REDIS_URL_AI, decode_responses=True); await r_ai.ping()
136
+ app.bot_data['redis_ai'] = r_ai
137
+ logger.info("✅ Dedicated AI Redis Connected!")
138
+ except Exception as e: logger.error(f"❌ AI Redis Connection Failed: {e}"); app.bot_data['redis_ai'] = None
139
+
140
+ app.bot_data['api_semaphore'] = asyncio.Semaphore(TELEGRAM_API_CONCURRENCY_LIMIT)
141
+ app.bot_data['ocr_semaphore'] = asyncio.Semaphore(2)
142
+ app.bot_data['batch_deleter'] = BatchDeleter(app.bot)
143
+ app.bot_data['proc_pool'] = ThreadPoolExecutor(max_workers=4)
144
+ app.bot_data['BOT_OWNER_ID'] = int(credentials.BOT_OWNER_ID) if credentials.BOT_OWNER_ID else None
145
+ app.bot_data['REPORT_CHANNEL_ID'] = int(credentials.REPORT_CHANNEL_ID) if credentials.REPORT_CHANNEL_ID else None
146
+ app.bot_data['bio_queue'] = asyncio.Queue()
147
 
148
+ asyncio.create_task(background_bio_task(app))
149
+ asyncio.create_task(check_expired_trials_loop(app))
150
+ asyncio.create_task(user_stats_flush_loop(app))
151
+ asyncio.create_task(recovery_traffic_locks(app))
152
+
153
+ async def post_stop(app: Application):
154
+ await db.flush_user_stats_to_db(app)
155
+ if p := app.bot_data.get('db_pool'): await p.close()
156
+ for r in app.bot_data.get('redis_clients', []): await r.close()
157
+ if r_ai := app.bot_data.get('redis_ai'): await r_ai.close()
158
+
159
+ def main():
160
+ if not credentials.BOT_TOKEN: return
161
+ app = Application.builder().token(credentials.BOT_TOKEN).post_init(post_init).post_stop(post_stop).build()
162
+ app.add_error_handler(global_error_handler)
 
163
 
164
+ from bot_handlers import admin_commands as ac, extra_commands as ec, owner_commands as oc, ai_commands as aic
165
+
166
+ app.add_handler(CommandHandler("start", safe_start_command))
167
+ app.add_handler(CommandHandler("ask", aic.ask_command))
168
+ app.add_handler(CommandHandler("summary", aic.summary_command))
169
+ app.add_handler(CommandHandler("grant", ac.vip_trial_command))
170
+ app.add_handler(CommandHandler("whois", ac.whois_command))
171
+ app.add_handler(CommandHandler("whisper", ac.whisper_command))
172
+ app.add_handler(CommandHandler("setwarn", ac.setwarn_command))
173
+ app.add_handler(CommandHandler("addword", ac.add_word_command))
174
+ app.add_handler(CommandHandler("delword", ac.del_word_command))
175
+ app.add_handler(CommandHandler("wordlist", ac.wordlist_command))
176
+ app.add_handler(CommandHandler("resetsettings", ac.reset_settings_command))
177
+ app.add_handler(CommandHandler("filter", ec.filter_command))
178
+ app.add_handler(CommandHandler("stop", ec.stop_filter_command))
179
+ app.add_handler(CommandHandler("save", ec.save_note_command))
180
+ app.add_handler(CommandHandler("setwelcome", ec.set_welcome_command))
181
+ app.add_handler(CommandHandler("welcomecard", ec.welcome_card_command))
182
+ app.add_handler(CommandHandler("shadow", ec.shadow_command))
183
+ app.add_handler(CommandHandler("clean_ghosts", ec.clean_ghosts_command))
184
+ app.add_handler(CommandHandler("linktomute", ec.linktomute_command))
185
+ app.add_handler(CommandHandler("spamscan", ec.spamscan_command))
186
+ app.add_handler(CommandHandler("antiduplicate", ec.antiduplicate_command))
187
+ app.add_handler(CommandHandler("blockforwards", ec.blockforwards_command))
188
+ app.add_handler(CommandHandler("blockbiolink", ec.blockbiolink_command))
189
+ app.add_handler(CommandHandler("ocrscan", ec.ocrscan_command))
190
+ app.add_handler(CommandHandler("traffic", ec.traffic_command))
191
+ app.add_handler(CommandHandler("manageadmins", ec.manageadmins_command))
192
+ app.add_handler(CommandHandler("allowusernames", ec.allowusernames_command))
193
+ app.add_handler(CommandHandler("maxlength", ec.maxlength_command))
194
+ app.add_handler(CommandHandler("autodelete", ec.autodelete_command))
195
+ app.add_handler(CommandHandler("sentiment", ec.sentiment_command))
196
+ app.add_handler(CommandHandler("ticket", ec.ticket_command))
197
+ app.add_handler(CommandHandler("trustscore", ec.trustscore_command))
198
+ app.add_handler(CommandHandler("fixdb", ec.fix_database_command))
199
+ app.add_handler(CommandHandler("bc", oc.broadcast_groups_command))
200
+ app.add_handler(CommandHandler("stats", oc.stats_command))
201
+ app.add_handler(CommandHandler("activity", oc.check_activity_command))
202
+ app.add_handler(CommandHandler("get_dead_groups", oc.get_dead_groups_command))
203
+ app.add_handler(CommandHandler("purge_dead_groups", oc.purge_dead_groups_command))
204
+ app.add_handler(CommandHandler("sban", oc.sban_command))
205
+ app.add_handler(CommandHandler("sdel", oc.sdel_command))
206
+ app.add_handler(CommandHandler("smute", oc.smute_command))
207
+ app.add_handler(CommandHandler("setforcejoin", ec.set_force_join_command))
208
 
209
+ app.add_handler(MessageHandler(filters.FORWARDED & filters.ChatType.PRIVATE, ec.handle_channel_forward))
210
+ app.add_handler(CallbackQueryHandler(w_callback))
211
+ app.add_handler(ChatJoinRequestHandler(handle_join_request))
212
+ app.add_handler(MessageHandler(filters.ALL & ~filters.COMMAND, w_msg))
213
+ app.add_handler(MessageHandler(filters.UpdateType.EDITED_MESSAGE, w_edit_msg))
214
+ app.add_handler(ChatMemberHandler(w_bot_status, ChatMemberHandler.MY_CHAT_MEMBER))
215
+ app.add_handler(ChatMemberHandler(w_member_status, ChatMemberHandler.CHAT_MEMBER))
216
+
217
  threading.Thread(target=run_web_server, daemon=True).start()
218
+ app.run_polling(allowed_updates=["message", "callback_query", "chat_member", "my_chat_member", "chat_join_request", "edited_message"])
219
+
220
+ if __name__ == '__main__': main()
221
+ # --- END OF FILE main.py ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,10 +1,8 @@
1
- pyTelegramBotAPI
2
- aiohttp
3
- redis
4
- asyncio
5
- asyncpg
6
- fastapi
7
- uvicorn
8
  python-dotenv
9
- certifi
10
- orjson
 
 
 
 
 
1
+ python-telegram-bot[webhooks]
 
 
 
 
 
 
2
  python-dotenv
3
+ orjson
4
+ httpx
5
+ asyncpg
6
+ redis
7
+ Pillow
8
+ uvloop