async def get_user_info(_, message): if len(message.command) != 2: return await wrapper.send_message( message.chat.id, plate("invalid_syntax", correct="/getuser id/[@]username")) if message.command[1].isdigit(): name = None user = get_user(message.command[1]) else: name = message.command[1].lstrip("@").lower() user = get_user_by_name(name) if user: logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent /getuser {message.command[1]}" ) result = user text = format_user(result) await wrapper.send_message(message.chat.id, text) else: if name: await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("name_missing", tg_uname=message.command[1]), ) else: await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("id_missing", tg_id=message.command[1]), )
async def bot_disclaimer_handler(_, query): cb_wrapper = MethodWrapper(query) await cb_wrapper.edit_message_text( text=plate("bot_disclaimer_text"), disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup( [[InlineKeyboardButton(plate("back_button"), "back_start")]] ), ) await cb_wrapper.answer()
def format_user(user): (tg_id, tg_uname, date, banned) = user return plate( "user_info", tg_id=tg_id, tg_uname="@" + tg_uname if tg_uname else "N/A", date=date, status=plate("yes") if banned else plate("no"), admin=plate("yes") if tg_id in ADMINS else plate("no"), )
async def count_users(_, message): if len(message.command) > 1: await wrapper.send_message(message.chat.id, plate("no_parameters", command="/count")) else: logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent /count" ) users_count = len(get_users()) await wrapper.send_message( message.chat.id, plate("users_count", users_count=users_count))
async def queue(_, message): if len(message.command) > 1: return await wrapper.send_message( message.chat.id, plate("no_parameters", command="/queue")) logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent /queue") text = "" for user in CACHE: if CACHE[user][0] == "AWAITING_ADMIN": text += f"- 👤 [User]({NAME.format(user)})\n" await wrapper.send_message(message.chat.id, plate("queue_list", queue=text))
async def bot_about_handler(_, query): cb_wrapper = MethodWrapper(query) await cb_wrapper.edit_message_text( text=plate( "credits", python_version=PY_VERSION, botbase_version=BOTBASE_VERSION, bot_version=BOT_VERSION, ), disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup( [[InlineKeyboardButton(plate("back_button"), "back_start")]] ), ) await cb_wrapper.answer()
async def chats(_, message): if len(message.command) > 1: return await wrapper.send_message( message.chat.id, plate("no_parameters", command="/chats")) logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent /chats") text = "" for user in CACHE: if CACHE[user][0] == "IN_CHAT" and user not in ADMINS: admin_id = CACHE[user][1] admin_name = ADMINS[admin_id] text += f"- 👤 [User]({NAME.format(user)}) -> 👨💻 [{admin_name}]({NAME.format(admin_id)})\n" await wrapper.send_message(message.chat.id, plate("chats_list", chats=text))
async def busy(_, message): if len(message.command) > 1: return await wrapper.send_message( message.chat.id, plate("no_parameters", command="/busy")) logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent /busy") if (CACHE[message.from_user.id][0] == "IN_CHAT" and CACHE[message.from_user.id][1] != 1234567): await wrapper.send_message(message.from_user.id, plate("leave_current_chat")) elif CACHE[message.from_user.id][0] == "none": await wrapper.send_message(message.chat.id, plate("marked_busy")) CACHE[message.from_user.id] = ["IN_CHAT", 1234567] else: if message.from_user.id in CACHE: del CACHE[message.from_user.id] await wrapper.send_message(message.chat.id, plate("unmarked_busy"))
async def services_status_history_handler(_, query): cb_wrapper = MethodWrapper(query) await cb_wrapper.edit_message_text( text=plate( "services_status_history_text", website_30d_avg=await get_30d_website_history_ratio(), website_30d_avg_symbol="✅" if await get_30d_website_history_label() else "⚠️", qr_code_api_30d_avg=await get_30d_qr_api_history_ratio(), qr_code_api_30d_avg_symbol="✅" if await get_30d_qr_api_history_ratio() else "⚠️", api_30d_avg=await get_30d_api_history_ratio(), api_30d_avg_symbol="✅" if await get_30d_api_history_label() else "⚠️", docs_30d_avg=await get_30d_api_docs_history_ratio(), docs_30d_avg_symbol="✅" if await get_30d_api_docs_history_label() else "⚠️", internal_api_30d_avg=await get_30d_internal_api_history_ratio(), internal_api_30d_avg_symbol="✅" if await get_30d_internal_api_history_label() else "⚠️", website_90d_avg=await get_90d_website_history_ratio(), website_90d_avg_symbol="✅" if await get_90d_website_history_label() else "⚠️", qr_code_api_90d_avg=await get_90d_qr_api_history_ratio(), qr_code_api_90d_avg_symbol="✅" if await get_90d_qr_api_history_ratio() else "⚠️", api_90d_avg=await get_90d_api_history_ratio(), api_90d_avg_symbol="✅" if await get_90d_api_history_label() else "⚠️", docs_90d_avg=await get_90d_api_docs_history_ratio(), docs_90d_avg_symbol="✅" if await get_90d_api_docs_history_label() else "⚠️", internal_api_90d_avg=await get_90d_internal_api_history_ratio(), internal_api_90d_avg_symbol="✅" if await get_90d_internal_api_history_label() else "⚠️", ), disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup( [[InlineKeyboardButton(plate("back_button"), "back_start")]] ), ) await cb_wrapper.answer()
async def get_random_user(_, message): logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent /getranduser" ) if len(message.command) > 1: await wrapper.send_message( message.chat.id, plate("no_parameters", command="/getranduser")) else: user = random.choice(get_users()) result = get_user(*user) text = format_user(result) await wrapper.send_message(message.chat.id, text)
async def clear_flood(_, message): if len(message.command) == 1: global MESSAGES # Ew... MESSAGES = defaultdict(list) for user in BANNED_USERS.copy(): BANNED_USERS.remove(user) await wrapper.send_message(message.chat.id, plate("flood_cleared")) else: for user in message.command[1:]: if not user.isdigit(): return await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("non_numeric_id") ) BANNED_USERS.discard(int(user)) # noinspection PyUnboundLocalVariable MESSAGES.pop(int(user), None) await wrapper.send_message( message.chat.id, plate( "flood_user_cleared", user="******".join((f"{usr}" for usr in message.command[1:])), ), )
async def whisper(_, message): if len(message.command) < 2: return await wrapper.send_message( message.chat.id, plate("invalid_syntax", correct="/whisper ID") + "\n<b>HTML and Markdown styling supported</b>", ) if not message.command[1].isdigit(): return await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("non_numeric_id")) logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent {message.text.html}" ) tg_id = int(message.command[1]) msg = message.text.html[9:] msg = msg[re.search(message.command[1], msg).end():] if tg_id not in itertools.chain(*get_users()): return await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("id_missing", tg_id=tg_id)) result = await wrapper.send_message( tg_id, plate( "whisper_from", admin= f"[{ADMINS[message.from_user.id]}]({NAME.format(message.from_user.id)})", msg=msg, ), ) if isinstance(result, Exception): logging.error( f"Could not whisper to {tg_id} because of {type(result).__name__}: {result}" ) await wrapper.send_message( message.chat.id, plate("error") + f": {type(result).__name__} -> {result}") else: await wrapper.send_message(message.chat.id, plate("whisper_successful"))
async def global_message(_, message): if len(message.command) > 1: msg = message.text.html[7:] logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent the following global message: {msg}" ) missed = 0 attempts = 0 for tg_id in itertools.chain(*get_users()): attempts += 1 result = await wrapper.send_message(tg_id, msg) if isinstance(result, Exception): logging.error( f"Could not deliver the global message to {tg_id} because of {type(result).__name__}: {result}" ) missed += 1 logging.warning( f"{attempts - missed}/{attempts} global messages were successfully delivered" ) await wrapper.send_message( message.chat.id, plate("global_message_stats", attempts=attempts, success=(attempts - missed), msg=msg), ) else: await wrapper.send_message( message.chat.id, plate("invalid_syntax", correct="/global message") + "\n<b>HTML and Markdown styling supported</b>", )
async def update(_, message): if len(message.command) != 2: return await wrapper.send_message( message.chat.id, plate("invalid_syntax", correct="/update ID")) if not message.command[1].isdigit(): return await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("non_numeric_id")) user = get_user(message.command[1]) if user: logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent /update {message.command[1]}" ) tg_id, tg_uname = user[:2] new = await wrapper.get_users(tg_id) if isinstance(new, Exception): logging.error( f"An error has occurred when calling get_users({tg_id}), {type(new).__name__}: {new}" ) await wrapper.send_message( message.chat.id, plate("error") + f": {type(new).__name__} -> {new}") else: if new.username is None: new.username = "******" if new.username != tg_uname: update_name(tg_id, new.username) await wrapper.send_message(message.chat.id, plate("user_info_updated")) else: await wrapper.send_message(message.chat.id, plate("user_info_unchanged")) else: await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("id_missing", tg_id=message.command[1]), )
async def anti_flood(_, update): """Anti flood module""" user_id = update.from_user.id chat = update.chat.id date = update.date message_id = update.message_id if isinstance(MESSAGES[user_id], tuple): chat, date = MESSAGES[user_id] if time.time() - date >= BAN_TIME: logging.warning( f"{user_id} has waited at least {BAN_TIME} seconds in {chat} and can now text again" ) BANNED_USERS.remove(user_id) del MESSAGES[user_id] elif ( len(MESSAGES[user_id]) >= MAX_UPDATE_THRESHOLD - 1 ): # -1 to avoid acting on the next update MESSAGES[user_id].append({chat: (date, message_id)}) logging.info( f"MAX_UPDATE_THRESHOLD ({MAX_UPDATE_THRESHOLD}) Reached for {user_id}" ) user_data = MESSAGES.pop(user_id) timestamps = [list(*d.values())[0] for d in user_data] updates = [list(*d.values())[1] for d in user_data] if is_flood(timestamps): logging.warning(f"Flood detected from {user_id} in chat {chat}") if user_id in CACHE: del CACHE[user_id] BANNED_USERS.add(user_id) # noinspection PyTypeChecker MESSAGES[user_id] = chat, time.time() if FLOOD_NOTICE: await wrapper.send_message( user_id, plate("flood_notice", time=f"{BAN_TIME / 60:.1f}") ) if DELETE_MESSAGES: await wrapper.delete_messages(chat, updates) else: if user_id in MESSAGES: del MESSAGES[user_id] else: MESSAGES[user_id].append({chat: (date, message_id)})
async def start_handler(_, update): """Simply handles the /start command sending a pre-defined greeting and saving new users to the database""" update_wrapper = MethodWrapper(update) if update.from_user.first_name: name = update.from_user.first_name elif update.from_user.username: name = update.from_user.username else: name = "Anonymous" if update.from_user.id not in itertools.chain(*get_users()): logging.warning( f"New user detected ({update.from_user.id}), adding to database" ) set_user( update.from_user.id, update.from_user.username.lower() if update.from_user.username else None, ) if GREET: if isinstance(update, Message): await update_wrapper.reply( text=plate( "greet", mention=f"[{name}](tg://user?id={update.from_user.id})", id=update.from_user.id, username=update.from_user.username, website_status="Online" if await get_website_status() else "Offline", qr_code_api_status="Online" if await get_qr_api_status() else "Offline", api_status="Online" if await get_api_status() else "Offline", api_docs_status="Online" if await get_api_docs_status() else "Offline", internal_api_status="Online" if await get_internal_api_status() else "Offline", ), reply_markup=BUTTONS, ) elif isinstance(update, CallbackQuery): if CACHE[update.from_user.id][0] == "AWAITING_ADMIN": data = CACHE[update.from_user.id][-1] if isinstance(data, list): for chatid, message_ids in data: await wrapper.delete_messages(chatid, message_ids) for admin in ADMINS: await wrapper.send_message( chat_id=admin, text=plate( "user_left_queue", user=f"[{name}]({NAME.format(update.from_user.id)})", ), ), await update_wrapper.edit_message_text( text=plate( "greet", mention=f"[{name}](tg://user?id={update.from_user.id})", id=update.from_user.id, username=update.from_user.username, website_status="Online" if await get_website_status() else "Offline", qr_code_api_status="Online" if await get_qr_api_status() else "Offline", api_status="Online" if await get_api_status() else "Offline", api_docs_status="Online" if await get_api_docs_status() else "Offline", internal_api_status="Online" if await get_internal_api_status() else "Offline", ), reply_markup=BUTTONS, ) del CACHE[update.from_user.id] await update_wrapper.answer()
async def ban(_, message): cmd = message.command[0] condition = {"ban": False, "unban": True}.get(cmd) if len(message.command) != 2: return await wrapper.send_message( message.chat.id, plate("invalid_syntax", correct=f"/{cmd} ID")) if not message.command[1].isdigit(): return await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("non_numeric_id")) if int(message.command[1]) in ADMINS: return await wrapper.send_message(message.chat.id, plate("cannot_ban_admin")) user = get_user(message.command[1]) if user: if bool(user[3]) is condition: logging.warning( f"{ADMINS[message.from_user.id]} [{message.from_user.id}] sent /{cmd} {message.command[1]}" ) tg_id = user[0] if condition: res = unban_user(tg_id) else: res = ban_user(tg_id) if isinstance(res, Exception): logging.error( f"An error has occurred when calling {cmd}_user({tg_id}), {type(res).__name__}: {res}" ) await wrapper.send_message( message.chat.id, plate("error") + f": {type(res).__name__} -> {res}") else: if condition and tg_id in BANNED_USERS: BANNED_USERS.remove(tg_id) else: BANNED_USERS.add(tg_id) await wrapper.send_message( message.chat.id, plate("user_unbanned") if condition else plate("user_banned"), ) await wrapper.send_message( tg_id, plate("you_are_unbanned") if condition else plate("you_are_banned"), ) else: await wrapper.send_message( message.chat.id, plate("user_not_banned") if condition else plate("user_already_banned"), ) else: await wrapper.send_message( message.chat.id, plate("error") + ":" + plate("id_missing", tg_id=message.command[1]), )