Spaces:
Runtime error
Runtime error
Upload 8 files
Browse files- config.py +16 -1
- credentials.py +29 -10
- db.py +21 -7
- lang_bn.py +16 -1
- lang_en.py +15 -0
- lang_hi.py +15 -1
- main.py +202 -106
- 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 |
-
|
| 2 |
-
|
| 3 |
-
#
|
| 4 |
-
BOT_TOKEN =
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 76 |
-
|
| 77 |
-
|
| 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("
|
|
|
|
| 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
|
| 5 |
-
import
|
| 6 |
-
import
|
|
|
|
| 7 |
from datetime import datetime, timedelta, timezone
|
|
|
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
return [r for r in responses if r[0] == socket.AF_INET]
|
| 14 |
-
socket.getaddrinfo = new_getaddrinfo
|
| 15 |
|
| 16 |
-
|
| 17 |
-
from
|
| 18 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 27 |
from bot_handlers import utils
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
def run_web_server():
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
|
| 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 |
-
|
| 57 |
except: pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
))
|
| 75 |
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
threading.Thread(target=run_web_server, daemon=True).start()
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 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 |
-
|
| 2 |
-
aiohttp
|
| 3 |
-
redis
|
| 4 |
-
asyncio
|
| 5 |
-
asyncpg
|
| 6 |
-
fastapi
|
| 7 |
-
uvicorn
|
| 8 |
python-dotenv
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
python-telegram-bot[webhooks]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
python-dotenv
|
| 3 |
+
orjson
|
| 4 |
+
httpx
|
| 5 |
+
asyncpg
|
| 6 |
+
redis
|
| 7 |
+
Pillow
|
| 8 |
+
uvloop
|