async def all_errors_handler(message, error): message = (message.message if message.message is not None else message.callback_query.message if message.callback_query is not None else message) chat_id = message.chat.id err_tlt = sys.exc_info()[0].__name__ err_msg = str(sys.exc_info()[1]) log.warn('Error caused update is: \n' + html.escape(str(parse_update(message)))) if redis.get(chat_id) == str(error): # by err_tlt we assume that it is same error return if err_tlt == 'BadRequest' and err_msg == 'Have no rights to send a message': return True if err_tlt in ('FloodWaitError', 'RetryAfter', 'SlowModeWaitError'): return True text = "<b>Sorry, I encountered a error!</b>\n" text += f'<code>{html.escape(err_tlt)}: {html.escape(err_msg)}</code>' redis.set(chat_id, str(error), ex=600) await bot.send_message(chat_id, text)
async def export_chat_data(message, chat, strings): chat_id = chat['chat_id'] key = 'export_lock:' + str(chat_id) if redis.get(key) and message.from_user.id not in OPERATORS: ttl = format_timedelta(timedelta(seconds=redis.ttl(key)), strings['language_info']['babel']) await message.reply(strings['exports_locked'] % ttl) return redis.set(key, 1) redis.expire(key, 7200) msg = await message.reply(strings['started_exporting']) data = { 'general': { 'chat_name': chat['chat_title'], 'chat_id': chat_id, 'timestamp': datetime.now(), 'version': VERSION } } for module in [m for m in LOADED_MODULES if hasattr(m, '__export__')]: await asyncio.sleep(0.2) if k := await module.__export__(chat_id): data.update(k)
async def export_chat_data(message, chat, strings): chat_id = chat['chat_id'] key = 'export_lock:' + str(chat_id) if redis.get(key) and message.from_user.id not in OPERATORS: ttl = format_timedelta(timedelta(seconds=redis.ttl(key)), strings['language_info']['babel']) await message.reply(strings['exports_locked'] % ttl) return redis.set(key, 1) redis.expire(key, 7200) msg = await message.reply(strings['started_exporting']) data = { 'general': { 'chat_name': chat['chat_title'], 'chat_id': chat_id, 'timestamp': datetime.now(), 'version': 1 } } for module in [m for m in LOADED_MODULES if hasattr(m, '__export__')]: await asyncio.sleep(0.2) data.update(await module.__export__(chat_id)) jfile = InputFile(io.StringIO(ujson.dumps(data, indent=2)), filename=f'{chat_id}_export.json') text = strings['export_done'] % chat['chat_title'] await message.answer_document(jfile, text, reply=message.message_id) await msg.delete()
async def import_fun(message, document, chat, strings): chat_id = chat['chat_id'] key = 'import_lock:' + str(chat_id) if redis.get(key) and message.from_user.id not in OPERATORS: ttl = format_timedelta(timedelta(seconds=redis.ttl(key)), strings['language_info']['babel']) await message.reply(strings['imports_locked'] % ttl) return redis.set(key, 1) redis.expire(key, 7200) msg = await message.reply(strings['started_importing']) if document['file_size'] > 52428800: await message.reply(strings['big_file']) return data = await bot.download_file_by_id(document.file_id, io.BytesIO()) data = ujson.load(data) if 'general' not in data: await message.reply(strings['bad_file']) return imported = [] for module in [m for m in LOADED_MODULES if hasattr(m, '__import__')]: module_name = module.__name__.replace('sophie_bot.modules.', '') print(module_name) if module_name in data: imported.append(module_name) await asyncio.sleep(0.2) await module.__import__(chat_id, data[module_name]) await msg.delete() await message.answer(strings['import_done'], reply=message.message_id)
async def importfbans_cmd(message, fed, strings): fed_id = fed['fed_id'] key = 'importfbans_lock:' + str(fed_id) if redis.get(key) and message.from_user.id not in OPERATORS: ttl = format_timedelta(timedelta(seconds=redis.ttl(key)), strings['language_info']['babel']) await message.reply(strings['importfbans_locked'] % ttl) return redis.set(key, 1) redis.expire(key, 7200) if 'document' in message: document = message.document else: if 'reply_to_message' not in message: await ImportFbansFileWait.waiting.set() await message.reply(strings['send_import_file']) return elif 'document' not in message.reply_to_message: await message.reply(strings['rpl_to_file']) return document = message.reply_to_message.document await importfbans_func(message, fed, document=document)
async def change_chat_lang(chat_id, lang): redis.set('lang_cache_{}'.format(chat_id), lang) await db.lang.update_one({'chat_id': chat_id}, {"$set": { 'chat_id': chat_id, 'lang': lang }}, upsert=True)
async def welcome_security_handler(message, strings): chat_id = message.chat.id if not check_admin_rights(chat_id, BOT_ID, ['can_restrict_members']): await message.reply(strings['not_admin_ws']) return user_id = message.from_user.id db_item = await db.greetings.find_one({'chat_id': chat_id}) if not db_item or 'welcome_security' not in db_item: return user = await message.chat.get_member(user_id) # Check if user was muted before if 'can_send_messages' in user and user['can_send_messages'] is False: return # Check on OPs and chat owner if await is_user_admin(chat_id, user_id): return # Mute user await mute_user(chat_id, user_id) if 'security_note' not in db_item: db_item = {'security_note': {}} db_item['security_note']['text'] = strings['default_security_note'] db_item['security_note']['parse_mode'] = 'md' text, kwargs = await t_unparse_note_item(message, db_item['security_note'], chat_id) kwargs['reply_to'] = message.message_id kwargs['buttons'] = None if not kwargs['buttons'] else kwargs['buttons'] msg = await tbot.send_message(chat_id, text, **kwargs) # Edit msg to apply button kwargs['buttons'] = [] if not kwargs['buttons'] else kwargs['buttons'] kwargs['buttons'] += [Button.url(strings['click_here'], f'https://t.me/{BOT_USERNAME}?start=welcome_security_{chat_id}_{user_id}_{msg.id}')] del kwargs['reply_to'] await msg.edit(text, **kwargs) redis.set(f'welcomesecurity_users_{user_id}', chat_id) scheduler.add_job( join_expired, "date", id=f"wc_expire:{chat_id}:{user_id}", run_date=datetime.utcnow() + convert_time(get_str_key('JOIN_CONFIRM_DURATION')), kwargs={'chat_id': chat_id, 'user_id': user_id, 'message_id': msg.id, 'wlkm_msg_id': message.message_id}, replace_existing=True )
async def connect_to_chat_direct(message, strings): user_id = message.from_user.id chat_id = message.chat.id chat_title = (await db.chat_list.find_one({'chat_id': chat_id}))['chat_title'] text = strings['pm_connected'].format(chat_name=chat_title) try: await bot.send_message(user_id, text) await def_connect_chat(message, user_id, chat_id, chat_title) except BotBlocked: await message.reply(strings['connected_pm_to_me'].format(chat_name=chat_title)) redis.set('sophie_connected_start_state:' + str(user_id), 1)
async def join_expired(chat_id, user_id, message_id, wlkm_msg_id): user = await bot.get_chat_member(chat_id, user_id) if 'can_send_messages' not in user or user['can_send_messages'] is True: return bot_user = await bot.get_chat_member(chat_id, BOT_ID) if 'can_restrict_members' not in bot_user or bot_user['can_restrict_members'] is False: return key = 'leave_silent:' + str(chat_id) redis.set(key, user_id) await unmute_user(chat_id, user_id) await kick_user(chat_id, user_id) await tbot.delete_messages(chat_id, [message_id, wlkm_msg_id])
async def connect_to_chat_direct(message, strings): user_id = message.from_user.id chat_id = message.chat.id chat = await db.chat_list.find_one({'chat_id': chat_id}) chat_title = chat['chat_title'] if chat is not None else message.chat.title text = strings['pm_connected'].format(chat_name=chat_title) try: await bot.send_message(user_id, text) await def_connect_chat(message, user_id, chat_id, chat_title) except (BotBlocked, CantInitiateConversation): await message.reply( strings['connected_pm_to_me'].format(chat_name=chat_title)) redis.set('sophie_connected_start_state:' + str(user_id), 1)
async def add_handler(message, chat, strings): handler = get_args_str(message).lower() text = strings['adding_filter'].format(handler=handler, chat_name=chat['chat_title']) buttons = InlineKeyboardMarkup(row_width=2) for action in FILTERS_ACTIONS.items(): filter_id = action[0] data = action[1] buttons.add(InlineKeyboardButton( await get_string(chat['chat_id'], data['title']['module'], data['title']['string']), callback_data=filter_action_cp.new(filter_id=filter_id) )) user_id = message.from_user.id chat_id = chat['chat_id'] redis.set(f'add_filter:{user_id}:{chat_id}', handler) await message.reply(text, reply_markup=buttons)
async def get_chat_lang(chat_id): r = redis.get('lang_cache_{}'.format(chat_id)) if r: return r else: db_lang = await db.lang.find_one({'chat_id': chat_id}) if db_lang: # Rebuild lang cache redis.set('lang_cache_{}'.format(chat_id), db_lang['lang']) return db_lang['lang'] user_lang = await db.user_list.find_one({'user_id': chat_id}) if user_lang and user_lang['user_lang'] in LANGUAGES: # Add telegram language in lang cache redis.set('lang_cache_{}'.format(chat_id), user_lang['user_lang']) return user_lang['user_lang'] else: return 'en'
async def import_fun(message, document, chat, strings): chat_id = chat['chat_id'] key = 'import_lock:' + str(chat_id) if redis.get(key) and message.from_user.id not in OPERATORS: ttl = format_timedelta(timedelta(seconds=redis.ttl(key)), strings['language_info']['babel']) await message.reply(strings['imports_locked'] % ttl) return redis.set(key, 1) redis.expire(key, 7200) msg = await message.reply(strings['started_importing']) if document['file_size'] > 52428800: await message.reply(strings['big_file']) return data = await bot.download_file_by_id(document.file_id, io.BytesIO()) data = ujson.load(data) if 'general' not in data: await message.reply(strings['bad_file']) return file_version = data['general']['version'] if file_version > VERSION: await message.reply(strings['file_version_so_new']) return imported = [] for module in [m for m in LOADED_MODULES if hasattr(m, '__import__')]: module_name = module.__name__.replace('sophie_bot.modules.', '') if module_name not in data: continue if not data[module_name]: continue imported.append(module_name) await asyncio.sleep(0) # Switch to other events before continue await module.__import__(chat_id, data[module_name]) await msg.edit_text(strings['import_done'])
async def fban_export(message, fed, strings): fed_id = fed['fed_id'] key = 'fbanlist_lock:' + str(fed_id) if redis.get(key) and message.from_user.id not in OPERATORS: ttl = format_timedelta(timedelta(seconds=redis.ttl(key)), strings['language_info']['babel']) await message.reply(strings['fbanlist_locked'] % ttl) return redis.set(key, 1) redis.expire(key, 7200) msg = await message.reply(strings['creating_fbanlist']) fields = ['user_id', 'reason', 'by', 'time', 'banned_chats'] with io.StringIO() as f: writer = csv.DictWriter(f, fields) writer.writeheader() async for banned_data in db.fed_bans.find({'fed_id': fed_id}): await asyncio.sleep(0) data = {'user_id': banned_data['user_id']} if 'reason' in banned_data: data['reason'] = banned_data['reason'] if 'time' in banned_data: data['time'] = int(time.mktime(banned_data['time'].timetuple())) if 'by' in banned_data: data['by'] = banned_data['by'] if 'banned_chats' in banned_data: data['banned_chats'] = banned_data['banned_chats'] writer.writerow(data) text = strings['fbanlist_done'] % fed['fed_name'] f.seek(0) await message.answer_document( InputFile(f, filename='fban_export.csv'), text ) await msg.delete()
text = strings['user_kicked'].format(user=await get_user_link(user_id), admin=await get_user_link(message.from_user.id), chat_name=chat['chat_title']) # Add reason if len(args := message.get_args().split(' ', 1)) > 1: text += strings['reason'] % args[1] # Check if silent silent = False if get_cmd(message) == 'skick': silent = True key = 'leave_silent:' + str(chat_id) redis.set(key, user_id) redis.expire(key, 30) text += strings['purge'] await kick_user(chat_id, user_id) msg = await message.reply(text) # Del msgs if silent if silent: to_del = [msg.message_id, message.message_id] if 'reply_to_message' in message and message.reply_to_message.from_user.id == user_id: to_del.append(message.reply_to_message.message_id) await asyncio.sleep(5) await tbot.delete_messages(chat_id, to_del)
text, kwargs = await t_unparse_note_item(event.message, db_item, chat_id, event=event) if user_id == event.message.chat.id: await event.message.delete() try: await send_note(user_id, text, **kwargs) await event.answer(strings['rules_was_pmed']) except (UserIsBlockedError, PeerIdInvalidError): await event.answer(strings['user_blocked'], show_alert=True) key = 'btn_rules_start_state:' + str(user_id) redis.set(key, chat_id) redis.expire(key, 900) async def __export__(chat_id): rules = await db.rules.find_one({'chat_id': chat_id}) if rules: del rules['_id'] del rules['chat_id'] return {'rules': rules} async def __import__(chat_id, data): rules = data for column in [i for i in data if i not in ALLOWED_COLUMNS]:
async def fed_ban_user(message, fed, user, reason, strings): user_id = user['user_id'] # Checks if user_id in OPERATORS: await message.reply(strings['user_wl']) return elif user_id == message.from_user.id: await message.reply(strings['fban_self']) return elif user_id == BOT_ID: await message.reply(strings['fban_self']) return elif user_id == fed['creator']: await message.reply(strings['fban_creator']) return elif 'admins' in fed and user_id in fed['admins']: await message.reply(strings['fban_fed_admin']) return elif await db.fed_bans.find_one({'fed_id': fed['fed_id'], 'user_id': user_id}): await message.reply(strings['already_fbanned'].format(user=await get_user_link(user_id))) return text = strings['fbanned_header'] text += strings['fban_info'].format( fed=fed['fed_name'], fadmin=await get_user_link(message.from_user.id), user=await get_user_link(user_id), user_id=user['user_id'] ) if reason: text += strings['fbanned_reason'].format(reason=reason) # fban processing msg msg = await message.reply(text + strings['fbanned_process'].format(num=len(fed['chats']))) user = await db.user_list.find_one({'user_id': user_id}) banned_chats = [] for chat_id in fed['chats']: # We not found the user or user wasn't detected if not user or 'chats' not in user: continue if chat_id in user['chats']: await asyncio.sleep(0) # Do not slow down other updates if await ban_user(chat_id, user_id): banned_chats.append(chat_id) new = { 'fed_id': fed['fed_id'], 'user_id': user_id, 'banned_chats': banned_chats, 'time': datetime.now(), 'by': message.from_user.id } if reason: new['reason'] = reason await db.fed_bans.insert_one(new) channel_text = strings['fban_log_fed_log'].format( fed_name=fed['fed_name'], fed_id=fed['fed_id'], user=await get_user_link(user_id), user_id=user_id, chat_count=len(banned_chats), all_chats=len(fed['chats']) ) if reason: channel_text += strings['fban_reason_fed_log'].format(reason=reason) # Check if silent silent = False if get_cmd(message) == 'sfban': silent = True key = 'leave_silent:' + str(message.chat.id) redis.set(key, user_id) redis.expire(key, 30) text += strings['fbanned_silence'] # SubsFeds process if len(sfeds_list := await get_all_subs_feds_r(fed['fed_id'], [])) > 1: sfeds_list.remove(fed['fed_id']) this_fed_banned_count = len(banned_chats) await msg.edit_text(text + strings['fbanned_subs_process'].format(feds=len(sfeds_list))) all_banned_chats_count = 0 for s_fed_id in sfeds_list: s_fed = await db.feds.find_one({'fed_id': s_fed_id}) banned_chats = [] for chat_id in s_fed['chats']: if not user: continue elif chat_id == user['user_id']: continue elif 'chats' not in user: continue elif chat_id not in user['chats']: continue # Do not slow down other updates await asyncio.sleep(0.2) if await ban_user(chat_id, user_id): banned_chats.append(chat_id) all_banned_chats_count += 1 new = { 'fed_id': s_fed_id, 'user_id': user_id, 'banned_chats': banned_chats, 'time': datetime.now(), 'origin_fed': fed['fed_id'], 'by': message.from_user.id } if reason: new['reason'] = reason await db.fed_bans.insert_one(new) await msg.edit_text(text + strings['fbanned_subs_done'].format( chats=this_fed_banned_count, subs_chats=all_banned_chats_count, feds=len(sfeds_list) )) channel_text += strings['fban_subs_fed_log'].format( subs_chats=all_banned_chats_count, feds=len(sfeds_list) )