Upload main.py
Browse files
main.py
CHANGED
|
@@ -2,6 +2,7 @@ import logging
|
|
| 2 |
import os
|
| 3 |
import asyncio
|
| 4 |
import threading
|
|
|
|
| 5 |
from fastapi import FastAPI
|
| 6 |
import uvicorn
|
| 7 |
import redis.asyncio as redis
|
|
@@ -23,10 +24,12 @@ from telegram.ext import (
|
|
| 23 |
|
| 24 |
# আপনার প্রজেক্টের ফাইলগুলো থেকে ইম্পোর্ট
|
| 25 |
from credentials import BOT_TOKEN, BOT_OWNER_ID, REPORT_CHANNEL_ID, REDIS_URL_1, REDIS_URL_2
|
|
|
|
| 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 |
|
| 31 |
# --- ১. Hugging Face Health Check Server (Port 7860) ---
|
| 32 |
app_web = FastAPI()
|
|
@@ -44,6 +47,7 @@ async def global_error_handler(update: object, context: ContextTypes.DEFAULT_TYP
|
|
| 44 |
logger.error("Exception while handling an update:", exc_info=context.error)
|
| 45 |
|
| 46 |
# --- ৩. ব্যাকগ্রাউন্ড টাস্কসমূহ ---
|
|
|
|
| 47 |
async def user_stats_flush_loop(app):
|
| 48 |
while True:
|
| 49 |
await asyncio.sleep(300) # প্রতি ৫ মিনিট পর DB-তে ডাটা ফ্লাশ হবে
|
|
@@ -68,31 +72,92 @@ async def check_expired_trials_loop(app):
|
|
| 68 |
await conn.execute("DELETE FROM vip_trials WHERE expiry_date < $1", now_utc)
|
| 69 |
except Exception as e:
|
| 70 |
logger.error(f"❌ VIP Monitor Error: {e}")
|
| 71 |
-
|
| 72 |
|
| 73 |
async def recovery_traffic_locks(app):
|
| 74 |
"""বট স্টার্ট হওয়ার সময় চেক করবে কোনো গ্রুপ লক হয়ে আছে কি না"""
|
| 75 |
pool = app.bot_data.get('db_pool')
|
| 76 |
if not pool: return
|
| 77 |
-
|
| 78 |
-
# সব গ্রুপ আইডি আনা (db.py থেকে)
|
| 79 |
group_ids = await db.get_all_group_ids(pool)
|
| 80 |
for chat_id in group_ids:
|
| 81 |
from bot_handlers.redis_manager import get_redis_client_for_chat
|
| 82 |
-
|
| 83 |
-
if
|
| 84 |
-
backup = await
|
| 85 |
if backup:
|
| 86 |
from bot_handlers.utils import restore_group_permissions
|
| 87 |
await restore_group_permissions(app, chat_id, backup)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
async def handle_join_request(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
| 91 |
req = update.chat_join_request
|
| 92 |
user, chat = req.from_user, req.chat
|
| 93 |
pool = context.bot_data.get('db_pool')
|
| 94 |
|
| 95 |
-
# ব্যাকগ্রাউন্ডে রেডিস আপডেট (যাতে /grant কমান্ড ইউজ করা যায়)
|
| 96 |
if user.username:
|
| 97 |
clean_username = user.username.lower().replace("@", "")
|
| 98 |
clients = context.bot_data.get('redis_clients')
|
|
@@ -100,7 +165,6 @@ async def handle_join_request(update: Update, context: ContextTypes.DEFAULT_TYPE
|
|
| 100 |
try: await clients[0].set(f"join_cache:{clean_username}", user.id, ex=2592000)
|
| 101 |
except: pass
|
| 102 |
|
| 103 |
-
# ডাটাবেসে টেম্পোরারি সেভ করা
|
| 104 |
if pool:
|
| 105 |
try:
|
| 106 |
temp_expiry = datetime.now(timezone.utc) + timedelta(minutes=10)
|
|
@@ -120,12 +184,9 @@ async def w_msg(u, c): from bot_handlers.core import handle_message; await handl
|
|
| 120 |
async def w_edit_msg(u, c): from bot_handlers.core import handle_edited_message; await handle_edited_message(u, c)
|
| 121 |
async def w_callback(u, c): from bot_handlers import callbacks; await callbacks.w_callback(u, c)
|
| 122 |
|
| 123 |
-
|
| 124 |
-
async def safe_start_command(update, context):
|
| 125 |
user = update.effective_user
|
| 126 |
if update.effective_chat.type != 'private': return
|
| 127 |
-
|
| 128 |
-
# ইউজার ডাটাবেসে সেভ (ট্র্যাকিংয়ের জন্য)
|
| 129 |
pool = context.bot_data.get('db_pool')
|
| 130 |
if pool:
|
| 131 |
asyncio.create_task(pool.execute("""
|
|
@@ -139,11 +200,7 @@ async def safe_start_command(update, context):
|
|
| 139 |
InlineKeyboardButton("বাংলা 🇧🇩", callback_data='lang_bn')],
|
| 140 |
[InlineKeyboardButton("हिन्दी 🇮🇳", callback_data='lang_hi')]
|
| 141 |
])
|
| 142 |
-
|
| 143 |
-
await update.message.reply_html(
|
| 144 |
-
f"👋 <b>Hello {user.first_name}!</b>\n\nWelcome to Management Bot. Please choose your language to continue:",
|
| 145 |
-
reply_markup=keyboard
|
| 146 |
-
)
|
| 147 |
|
| 148 |
# --- ৬. POST INIT & STOP ---
|
| 149 |
async def post_init(app: Application):
|
|
@@ -167,9 +224,13 @@ async def post_init(app: Application):
|
|
| 167 |
app.bot_data['BOT_OWNER_ID'] = int(BOT_OWNER_ID) if BOT_OWNER_ID else None
|
| 168 |
app.bot_data['REPORT_CHANNEL_ID'] = int(REPORT_CHANNEL_ID) if REPORT_CHANNEL_ID else None
|
| 169 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
asyncio.create_task(check_expired_trials_loop(app))
|
| 171 |
asyncio.create_task(user_stats_flush_loop(app))
|
| 172 |
-
# ট্রাফিক লক রিকভারি টাস্ক শুরু করা
|
| 173 |
asyncio.create_task(recovery_traffic_locks(app))
|
| 174 |
|
| 175 |
async def post_stop(app: Application):
|
|
@@ -177,18 +238,16 @@ async def post_stop(app: Application):
|
|
| 177 |
if p := app.bot_data.get('db_pool'): await p.close()
|
| 178 |
for r in app.bot_data.get('redis_clients', []): await r.close()
|
| 179 |
|
| 180 |
-
# --- ৭. প্রধান ফাংশন
|
| 181 |
def main():
|
| 182 |
if not BOT_TOKEN: return
|
| 183 |
app = Application.builder().token(BOT_TOKEN).post_init(post_init).post_stop(post_stop).build()
|
| 184 |
app.add_error_handler(global_error_handler)
|
| 185 |
|
| 186 |
-
# মডিউল ইম্পোর্ট
|
| 187 |
from bot_handlers import admin_commands as ac
|
| 188 |
from bot_handlers import extra_commands as ec
|
| 189 |
from bot_handlers import owner_commands as oc
|
| 190 |
|
| 191 |
-
# --- ১. সাধারণ ও অ্যাডমিন কমান্ড ---
|
| 192 |
app.add_handler(CommandHandler("start", safe_start_command))
|
| 193 |
app.add_handler(CommandHandler("grant", ac.vip_trial_command))
|
| 194 |
app.add_handler(CommandHandler("whois", ac.whois_command))
|
|
@@ -198,8 +257,6 @@ def main():
|
|
| 198 |
app.add_handler(CommandHandler("delword", ac.del_word_command))
|
| 199 |
app.add_handler(CommandHandler("wordlist", ac.wordlist_command))
|
| 200 |
app.add_handler(CommandHandler("resetsettings", ac.reset_settings_command))
|
| 201 |
-
|
| 202 |
-
# --- ২. এক্সট্রা ও সেটিংস কমান্ড ---
|
| 203 |
app.add_handler(CommandHandler("filter", ec.filter_command))
|
| 204 |
app.add_handler(CommandHandler("stop", ec.stop_filter_command))
|
| 205 |
app.add_handler(CommandHandler("save", ec.save_note_command))
|
|
@@ -222,8 +279,6 @@ def main():
|
|
| 222 |
app.add_handler(CommandHandler("ticket", ec.ticket_command))
|
| 223 |
app.add_handler(CommandHandler("trustscore", ec.trustscore_command))
|
| 224 |
app.add_handler(CommandHandler("fixdb", ec.fix_database_command))
|
| 225 |
-
|
| 226 |
-
# --- ৩. ওনার ও টুলস কমান্ড ---
|
| 227 |
app.add_handler(CommandHandler("broadcast", oc.broadcast_command))
|
| 228 |
app.add_handler(CommandHandler("bc", oc.broadcast_groups_command))
|
| 229 |
app.add_handler(CommandHandler("stats", oc.stats_command))
|
|
@@ -235,7 +290,6 @@ def main():
|
|
| 235 |
app.add_handler(CommandHandler("sdel", oc.sdel_command))
|
| 236 |
app.add_handler(CommandHandler("smute", oc.smute_command))
|
| 237 |
|
| 238 |
-
# --- ৪. কোর মেসেজ ও কলব্যাক হ্যান্ডেলার ---
|
| 239 |
app.add_handler(CallbackQueryHandler(w_callback))
|
| 240 |
app.add_handler(ChatJoinRequestHandler(handle_join_request))
|
| 241 |
app.add_handler(MessageHandler(filters.ALL & ~filters.COMMAND, w_msg))
|
|
@@ -243,10 +297,7 @@ def main():
|
|
| 243 |
app.add_handler(ChatMemberHandler(w_bot_status, ChatMemberHandler.MY_CHAT_MEMBER))
|
| 244 |
app.add_handler(ChatMemberHandler(w_member_status, ChatMemberHandler.CHAT_MEMBER))
|
| 245 |
|
| 246 |
-
# ওয়েব সার্ভার চালু করা (Hugging Face Health Check-এর জন্য)
|
| 247 |
threading.Thread(target=run_web_server, daemon=True).start()
|
| 248 |
-
|
| 249 |
-
# পোলিং মোডে বট চালু করা
|
| 250 |
logger.info("Bot is polling silently...")
|
| 251 |
app.run_polling(allowed_updates=["message", "callback_query", "chat_member", "my_chat_member", "chat_join_request", "edited_message"])
|
| 252 |
|
|
|
|
| 2 |
import os
|
| 3 |
import asyncio
|
| 4 |
import threading
|
| 5 |
+
import re
|
| 6 |
from fastapi import FastAPI
|
| 7 |
import uvicorn
|
| 8 |
import redis.asyncio as redis
|
|
|
|
| 24 |
|
| 25 |
# আপনার প্রজেক্টের ফাইলগুলো থেকে ইম্পোর্ট
|
| 26 |
from credentials import BOT_TOKEN, BOT_OWNER_ID, REPORT_CHANNEL_ID, REDIS_URL_1, REDIS_URL_2
|
| 27 |
+
import config
|
| 28 |
from config import logger, BOT_VERSION, TELEGRAM_API_CONCURRENCY_LIMIT
|
| 29 |
from db import create_db_pool_with_retry, setup_database
|
| 30 |
import db
|
| 31 |
from bot_handlers.batch import BatchDeleter
|
| 32 |
+
from bot_handlers import utils
|
| 33 |
|
| 34 |
# --- ১. Hugging Face Health Check Server (Port 7860) ---
|
| 35 |
app_web = FastAPI()
|
|
|
|
| 47 |
logger.error("Exception while handling an update:", exc_info=context.error)
|
| 48 |
|
| 49 |
# --- ৩. ব্যাকগ্রাউন্ড টাস্কসমূহ ---
|
| 50 |
+
|
| 51 |
async def user_stats_flush_loop(app):
|
| 52 |
while True:
|
| 53 |
await asyncio.sleep(300) # প্রতি ৫ মিনিট পর DB-তে ডাটা ফ্লাশ হবে
|
|
|
|
| 72 |
await conn.execute("DELETE FROM vip_trials WHERE expiry_date < $1", now_utc)
|
| 73 |
except Exception as e:
|
| 74 |
logger.error(f"❌ VIP Monitor Error: {e}")
|
|
|
|
| 75 |
|
| 76 |
async def recovery_traffic_locks(app):
|
| 77 |
"""বট স্টার্ট হওয়ার সময় চেক করবে কোনো গ্রুপ লক হয়ে আছে কি না"""
|
| 78 |
pool = app.bot_data.get('db_pool')
|
| 79 |
if not pool: return
|
|
|
|
|
|
|
| 80 |
group_ids = await db.get_all_group_ids(pool)
|
| 81 |
for chat_id in group_ids:
|
| 82 |
from bot_handlers.redis_manager import get_redis_client_for_chat
|
| 83 |
+
redis_client = get_redis_client_for_chat(app, chat_id)
|
| 84 |
+
if redis_client:
|
| 85 |
+
backup = await redis_client.get(f"backup_perms:{chat_id}")
|
| 86 |
if backup:
|
| 87 |
from bot_handlers.utils import restore_group_permissions
|
| 88 |
await restore_group_permissions(app, chat_id, backup)
|
| 89 |
+
|
| 90 |
+
# 🔥 নতুন: ব্যাকগ্রাউন্ড বায়ো চেকার টাস্ক (একই প্রসেসের ভেতরে চলবে)
|
| 91 |
+
async def background_bio_task(app):
|
| 92 |
+
"""এটি মেইন বটের ভেতরেই কিউ থেকে আইডি নিয়ে এক এক করে প্রোফাইল চেক করবে"""
|
| 93 |
+
while True:
|
| 94 |
+
try:
|
| 95 |
+
# লাইন (Queue) থেকে ডাটা নিবে
|
| 96 |
+
data = await app.bot_data['bio_queue'].get()
|
| 97 |
+
chat_id, user_id, msg_id, settings, user_obj = data
|
| 98 |
+
|
| 99 |
+
# রেট লিমিট এড়াতে ০.১ সেকেন্ড বিরতি
|
| 100 |
+
await asyncio.sleep(0.1)
|
| 101 |
+
|
| 102 |
+
try:
|
| 103 |
+
full_user = await app.bot.get_chat(user_id)
|
| 104 |
+
bio_text = getattr(full_user, 'bio', "") or ""
|
| 105 |
+
first_name = full_user.first_name or ""
|
| 106 |
+
last_name = full_user.last_name or ""
|
| 107 |
+
personal_chat = getattr(full_user, 'personal_chat', None)
|
| 108 |
+
|
| 109 |
+
full_profile_text = f"{first_name} {last_name} {bio_text}".strip()
|
| 110 |
+
is_bad = False
|
| 111 |
+
reason_text = "Bad Profile (Auto-detected)"
|
| 112 |
+
|
| 113 |
+
if personal_chat:
|
| 114 |
+
is_bad = True
|
| 115 |
+
reason_text = "Personal Channel Linked"
|
| 116 |
+
elif full_profile_text:
|
| 117 |
+
if utils.has_link_in_text(full_profile_text) or config.SPAM_PHRASES_COMPILED.search(full_profile_text):
|
| 118 |
+
is_bad = True
|
| 119 |
+
reason_text = "Link or Spam words in Bio/Name"
|
| 120 |
+
|
| 121 |
+
redis_client = app.bot_data.get('redis_clients')[0] if app.bot_data.get('redis_clients') else None
|
| 122 |
+
|
| 123 |
+
if is_bad:
|
| 124 |
+
if redis_client: await redis_client.set(f"bio_status:{user_id}", "bad", ex=1800)
|
| 125 |
+
|
| 126 |
+
# মেসেজ ডিলিট
|
| 127 |
+
try: await app.bot.delete_message(chat_id, msg_id)
|
| 128 |
+
except: pass
|
| 129 |
+
|
| 130 |
+
# ডিলেশন রিপোর্ট পাঠানো (Optional)
|
| 131 |
+
try:
|
| 132 |
+
from bot_handlers.core import _background_processing
|
| 133 |
+
class DummyContext:
|
| 134 |
+
def __init__(self, bot, bot_data):
|
| 135 |
+
self.bot = bot
|
| 136 |
+
self.bot_data = bot_data
|
| 137 |
+
dummy_ctx = DummyContext(app.bot, app.bot_data)
|
| 138 |
+
chat_obj = await app.bot.get_chat(chat_id)
|
| 139 |
+
asyncio.create_task(_background_processing(dummy_ctx, user_obj, chat_obj, settings, ['censor_reason_bio_link'], reason_text))
|
| 140 |
+
except: pass
|
| 141 |
+
else:
|
| 142 |
+
if redis_client: await redis_client.set(f"bio_status:{user_id}", "safe", ex=1600)
|
| 143 |
+
|
| 144 |
+
except Exception:
|
| 145 |
+
pass
|
| 146 |
+
finally:
|
| 147 |
+
# কাজ শেষ হলে ইউজার লক রিলিজ করা
|
| 148 |
+
redis_client = app.bot_data.get('redis_clients')[0] if app.bot_data.get('redis_clients') else None
|
| 149 |
+
if redis_client: await redis_client.delete(f"bio_lock:{user_id}")
|
| 150 |
|
| 151 |
+
app.bot_data['bio_queue'].task_done()
|
| 152 |
+
except Exception as e:
|
| 153 |
+
logger.error(f"Bio Task Error: {e}")
|
| 154 |
+
|
| 155 |
+
# --- ৪. জয়েন রিকোয়েস্ট হ্যান্ডেলার ---
|
| 156 |
async def handle_join_request(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
| 157 |
req = update.chat_join_request
|
| 158 |
user, chat = req.from_user, req.chat
|
| 159 |
pool = context.bot_data.get('db_pool')
|
| 160 |
|
|
|
|
| 161 |
if user.username:
|
| 162 |
clean_username = user.username.lower().replace("@", "")
|
| 163 |
clients = context.bot_data.get('redis_clients')
|
|
|
|
| 165 |
try: await clients[0].set(f"join_cache:{clean_username}", user.id, ex=2592000)
|
| 166 |
except: pass
|
| 167 |
|
|
|
|
| 168 |
if pool:
|
| 169 |
try:
|
| 170 |
temp_expiry = datetime.now(timezone.utc) + timedelta(minutes=10)
|
|
|
|
| 184 |
async def w_edit_msg(u, c): from bot_handlers.core import handle_edited_message; await handle_edited_message(u, c)
|
| 185 |
async def w_callback(u, c): from bot_handlers import callbacks; await callbacks.w_callback(u, c)
|
| 186 |
|
| 187 |
+
async def safe_start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|
|
|
| 188 |
user = update.effective_user
|
| 189 |
if update.effective_chat.type != 'private': return
|
|
|
|
|
|
|
| 190 |
pool = context.bot_data.get('db_pool')
|
| 191 |
if pool:
|
| 192 |
asyncio.create_task(pool.execute("""
|
|
|
|
| 200 |
InlineKeyboardButton("বাংলা 🇧🇩", callback_data='lang_bn')],
|
| 201 |
[InlineKeyboardButton("हिन्दी 🇮🇳", callback_data='lang_hi')]
|
| 202 |
])
|
| 203 |
+
await update.message.reply_html(f"👋 <b>Hello {user.first_name}!</b>\n\nPlease choose language:", reply_markup=keyboard)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
|
| 205 |
# --- ৬. POST INIT & STOP ---
|
| 206 |
async def post_init(app: Application):
|
|
|
|
| 224 |
app.bot_data['BOT_OWNER_ID'] = int(BOT_OWNER_ID) if BOT_OWNER_ID else None
|
| 225 |
app.bot_data['REPORT_CHANNEL_ID'] = int(REPORT_CHANNEL_ID) if REPORT_CHANNEL_ID else None
|
| 226 |
|
| 227 |
+
# 🔥 কিউ তৈরি এবং ব্যাকগ্রাউন্ড টাস্ক চালু করা
|
| 228 |
+
app.bot_data['bio_queue'] = asyncio.Queue()
|
| 229 |
+
asyncio.create_task(background_bio_task(app))
|
| 230 |
+
|
| 231 |
+
# বাকি লুপগুলো শুরু
|
| 232 |
asyncio.create_task(check_expired_trials_loop(app))
|
| 233 |
asyncio.create_task(user_stats_flush_loop(app))
|
|
|
|
| 234 |
asyncio.create_task(recovery_traffic_locks(app))
|
| 235 |
|
| 236 |
async def post_stop(app: Application):
|
|
|
|
| 238 |
if p := app.bot_data.get('db_pool'): await p.close()
|
| 239 |
for r in app.bot_data.get('redis_clients', []): await r.close()
|
| 240 |
|
| 241 |
+
# --- ৭. প্রধান ফাংশন ---
|
| 242 |
def main():
|
| 243 |
if not BOT_TOKEN: return
|
| 244 |
app = Application.builder().token(BOT_TOKEN).post_init(post_init).post_stop(post_stop).build()
|
| 245 |
app.add_error_handler(global_error_handler)
|
| 246 |
|
|
|
|
| 247 |
from bot_handlers import admin_commands as ac
|
| 248 |
from bot_handlers import extra_commands as ec
|
| 249 |
from bot_handlers import owner_commands as oc
|
| 250 |
|
|
|
|
| 251 |
app.add_handler(CommandHandler("start", safe_start_command))
|
| 252 |
app.add_handler(CommandHandler("grant", ac.vip_trial_command))
|
| 253 |
app.add_handler(CommandHandler("whois", ac.whois_command))
|
|
|
|
| 257 |
app.add_handler(CommandHandler("delword", ac.del_word_command))
|
| 258 |
app.add_handler(CommandHandler("wordlist", ac.wordlist_command))
|
| 259 |
app.add_handler(CommandHandler("resetsettings", ac.reset_settings_command))
|
|
|
|
|
|
|
| 260 |
app.add_handler(CommandHandler("filter", ec.filter_command))
|
| 261 |
app.add_handler(CommandHandler("stop", ec.stop_filter_command))
|
| 262 |
app.add_handler(CommandHandler("save", ec.save_note_command))
|
|
|
|
| 279 |
app.add_handler(CommandHandler("ticket", ec.ticket_command))
|
| 280 |
app.add_handler(CommandHandler("trustscore", ec.trustscore_command))
|
| 281 |
app.add_handler(CommandHandler("fixdb", ec.fix_database_command))
|
|
|
|
|
|
|
| 282 |
app.add_handler(CommandHandler("broadcast", oc.broadcast_command))
|
| 283 |
app.add_handler(CommandHandler("bc", oc.broadcast_groups_command))
|
| 284 |
app.add_handler(CommandHandler("stats", oc.stats_command))
|
|
|
|
| 290 |
app.add_handler(CommandHandler("sdel", oc.sdel_command))
|
| 291 |
app.add_handler(CommandHandler("smute", oc.smute_command))
|
| 292 |
|
|
|
|
| 293 |
app.add_handler(CallbackQueryHandler(w_callback))
|
| 294 |
app.add_handler(ChatJoinRequestHandler(handle_join_request))
|
| 295 |
app.add_handler(MessageHandler(filters.ALL & ~filters.COMMAND, w_msg))
|
|
|
|
| 297 |
app.add_handler(ChatMemberHandler(w_bot_status, ChatMemberHandler.MY_CHAT_MEMBER))
|
| 298 |
app.add_handler(ChatMemberHandler(w_member_status, ChatMemberHandler.CHAT_MEMBER))
|
| 299 |
|
|
|
|
| 300 |
threading.Thread(target=run_web_server, daemon=True).start()
|
|
|
|
|
|
|
| 301 |
logger.info("Bot is polling silently...")
|
| 302 |
app.run_polling(allowed_updates=["message", "callback_query", "chat_member", "my_chat_member", "chat_join_request", "edited_message"])
|
| 303 |
|