async def update_user(from_user, update_visit=True): if not from_user.is_bot: user = await db.users.find_one({'id': from_user.id}) if not user: from_user = from_user.to_python() user = from_user user.update({'_id': from_user['id']}) if user.get('first_name'): user['first_name'] = quote_html(user['first_name']) if user.get('last_name'): user['last_name'] = quote_html(user['last_name']) if update_visit: user.update({'last_visit': datetime.utcnow()}) await db.users.insert_one(user) else: from_user = from_user.to_python() if 'last_name' not in from_user: from_user['last_name'] = None if 'username' not in from_user: from_user['username'] = None if from_user.get('first_name'): from_user['first_name'] = quote_html(from_user['first_name']) if from_user.get('last_name'): from_user['last_name'] = quote_html(from_user['last_name']) if update_visit: from_user.update({'last_visit': datetime.utcnow()}) usr = {'$set': from_user} user = await db.users.find_one_and_update({'_id': user['id']}, usr, upsert=True, return_document=ReturnDocument.AFTER) else: user = None return user
def detect_message_text_formatting(message: Message) -> Optional[str]: """ Detects message formatting (html, markdown or None if message has special entities) """ raw_text: str = message.text before_escape_md = raw_text.count('\\') before_escape_html = raw_text.count('&') escaped_md = escape_md(raw_text).count('\\') - before_escape_md escaped_html = quote_html(raw_text).count('&') - before_escape_html with dont_change_plain_urls, dont_escape_md: with_entities = message.md_text escaped_with_entities = escape_md(with_entities).count( '\\') - before_escape_md if escaped_with_entities > max(escaped_html, escaped_md): parse_mode = None elif escaped_html > escaped_md: parse_mode = 'html' else: parse_mode = 'markdown' return parse_mode
async def translate_at_transifex(message: types.Message): user = await get_user(message.from_user.id) language = user.get("language_code") or config.DEFAULT_LANGUAGE project = user.get("project") or config.DEFAULT_PROJECT resource, string = await random_string( language, project, translated=False, max_size=300, ) string_url = transifex_string_url(resource, string["key"], language, project) response = await render_template( message.from_user.id, "translate_at_transifex", source=string["source_string"], transifex_url=string_url, docsurl=docsurl(resource).replace("__", "\_\_"), ) response = quote_html(response) try: await bot.send_message( message.chat.id, response, disable_web_page_preview=True, parse_mode="markdown", ) except BotBlocked: logger.exception("i17obot blocked by user. userid=%r", message.chat.id)
async def karma_change(message: types.Message, karma: dict, user: User, chat: Chat, target: User, config: Config): try: result_change_karma = await change_karma( target_user=target, chat=chat, user=user, how_change=karma['karma_change'], comment=karma['comment'], bot=message.bot, ) except SubZeroKarma: return await message.reply("У Вас слишком мало кармы для этого") except DontOffendRestricted: return await message.reply("Не обижай его, он и так наказан!") except CantChangeKarma as e: logger.info("user {user} can't change karma, {e}", user=user.tg_id, e=e) return if result_change_karma.was_auto_restricted: notify_text = config.auto_restriction.render_auto_restriction( target, result_change_karma.count_auto_restrict) elif result_change_karma.karma_after < 0 and await is_enable_karmic_restriction(chat): notify_text = config.auto_restriction.render_negative_karma_notification( target, result_change_karma.count_auto_restrict) else: notify_text = "" # How match karma was changed. Sign show changed difference, not difference for cancel how_changed_karma = result_change_karma.user_karma.karma \ - result_change_karma.karma_after \ + result_change_karma.abs_change msg = await message.reply( "Вы {how_change} карму <b>{name}</b> до <b>{karma_new:.2f}</b> ({power:+.2f})" "\n\n{notify_text}".format( how_change=get_how_change_text(karma['karma_change']), name=quote_html(target.fullname), karma_new=result_change_karma.karma_after, power=result_change_karma.abs_change, notify_text=notify_text, ), disable_web_page_preview=True, allow_sending_without_reply=True, reply_markup=kb.get_kb_karma_cancel( user=user, karma_event=result_change_karma.karma_event, rollback_karma=-how_changed_karma, moderator_event=result_change_karma.moderator_event, ) ) asyncio.create_task(remove_kb(msg, config.time_to_cancel_actions))
async def _consume_tg_log(self): while True: item = await self._queue.get() if item is None: break msg, silent, quote = item self._logger.debug('sending message to %s: %s', self._channel_id, item) await self._bot.send_message(self._channel_id, quote_html(msg) if quote else msg, disable_notification=silent) await asyncio.sleep(0.0)
async def update_chat(from_chat): chat = await db.chats.find_one({'id': from_chat.id}) if not chat: chat = from_chat.to_python() chat['title'] = quote_html(chat['title']) c = {'_id': chat['id']} chat.update(c) await db.chats.insert_one(chat) else: # I don't care about chat title, really, even username pass return chat
async def cmd_report(message: types.Message, config: Config, lang: str): """ Handle /report command in main group :param message: Telegram message starting with /report :param config: bot config :param lang: preferred bot language """ reported = await message.chat.get_member( message.reply_to_message.from_user.id) if reported.is_chat_admin(): await message.reply(get_string(lang, "error_report_admin")) return available_options = { get_string(lang, "action_del_msg"): "del", get_string(lang, "action_del_and_ban"): "ban" } parts = message.text.split(maxsplit=1) report_msg_template = get_string(lang, "report_message") if len(parts) == 2: report_msg_template += get_string( lang, "report_note").format(note=quote_html(parts[1])) msg = await message.reply(get_string(lang, "report_sent")) kb = types.InlineKeyboardMarkup() for button_text, option in available_options.items(): kb.add( types.InlineKeyboardButton( text=button_text, callback_data=report_msg_cb.new( option=option, user_id=message.reply_to_message.from_user.id, # Collect all IDs (initial message, report message, report confirmation) to delete afterwards message_ids= f"{message.reply_to_message.message_id},{message.message_id},{msg.message_id}" ))) await message.reply_to_message.forward(config.group.reports) await message.bot.send_message( config.group.reports, report_msg_template.format( time=message.reply_to_message.date.strftime( get_string(lang, "report_date_format")), msg_url=get_message_url(message.chat.id, message.reply_to_message.message_id), ), reply_markup=kb)
async def karma_change(message: types.Message, karma: dict, user: User, chat: Chat, target: User): try: result_change_karma = await change_karma( target_user=target, chat=chat, user=user, how_change=karma['karma_change'], comment=karma['comment'], bot=message.bot, ) except SubZeroKarma: return await message.reply("У Вас слишком мало кармы для этого") except DontOffendRestricted: return await message.reply("Не обижай его, он и так наказан!") except CantChangeKarma as e: logger.info("user {user} can't change karma, {e}", user=user.tg_id, e=e) return if result_change_karma.count_auto_restrict: notify_auto_restrict_text = await render_text_auto_restrict(result_change_karma.count_auto_restrict, target) else: notify_auto_restrict_text = "" # How match karma was changed. Sign show changed difference, not difference for cancel how_changed_karma = result_change_karma.user_karma.karma \ - result_change_karma.karma_after \ + result_change_karma.abs_change msg = await message.reply( "Вы {how_change} рейтинг <b>{name}</b> до <b>{karma_new}</b> ({power})" "\n\n{notify_auto_restrict_text}".format( how_change=get_how_change_text(karma['karma_change']), name=quote_html(target.fullname), karma_new=result_change_karma.karma_after, power=result_change_karma.abs_change, notify_auto_restrict_text=notify_auto_restrict_text ), disable_web_page_preview=True, reply_markup=kb.get_kb_karma_cancel( user=user, karma_event=result_change_karma.karma_event, rollback_karma=-how_changed_karma, moderator_event=result_change_karma.moderator_event, ) ) asyncio.create_task(remove_kb(msg, config.TIME_TO_CANCEL_ACTIONS)) asyncio.create_task(delete_message(msg, config.TIME_TO_REMOVE_TEMP_MESSAGES))
async def text_report_admins(message: types.Message): logger.info( "User {user} report message {message} in chat {chat} from user {from_user}", user=message.from_user.id, message=message.message_id, chat=message.chat.id, from_user=message.reply_to_message.from_user.id, ) if not message.reply_to_message: return await message.reply( "Используйте эту команду в ответ (реплай) на нужное сообщение пользователя" ) admins: List[types.ChatMember] = await message.chat.get_administrators() text = "[РЕПОРТ] Пользователь {user} пожаловался на другого пользователя в чате {chat}.".format( user=message.from_user.get_mention(), chat=hlink( message.chat.title, f"https://t.me/{message.chat.username}/{message.reply_to_message.message_id}", ) if message.chat.username else quote_html(repr(message.chat.title)), ) admin_ids = [ admin.user.id for admin in admins if admin.is_chat_admin() and not admin.user.is_bot ] if admin_ids: for admin in await User.query.where(User.id.in_(admin_ids)).gino.all(): with suppress(Unauthorized): await bot.send_message(admin.id, text) logger.info("Send alert message to admin {admin}", admin=admin.id) await asyncio.sleep(0.3) reply_msg = await message.reply_to_message.reply( "Ваша жалоба была отправлена!") await asyncio.sleep(30) await reply_msg.delete()
async def text_report_admins(message: types.Message): logger.info( "User {user} report message {message} in chat {chat} from user {from_user}", user=message.from_user.id, message=message.message_id, chat=message.chat.id, from_user=message.reply_to_message.from_user.id, ) if not message.reply_to_message: return await message.reply( _( "Please use this command is only in reply to message what do you want to report " "and this message will be reported to chat administrators." ) ) admins: List[types.ChatMember] = await message.chat.get_administrators() text = _("[ALERT] User {user} is reported message in chat {chat}.").format( user=message.from_user.get_mention(), chat=hlink( message.chat.title, f"https://t.me/{message.chat.username}/{message.reply_to_message.message_id}", ) if message.chat.username else quote_html(repr(message.chat.title)), ) admin_ids = [ admin.user.id for admin in admins if admin.is_chat_admin() and not admin.user.is_bot ] if admin_ids: for admin in await User.query.where( User.id.in_(admin_ids) & (User.do_not_disturb == False) # NOQA ).gino.all(): # NOQA with suppress(Unauthorized): await bot.send_message(admin.id, text) logger.info("Send alert message to admin {admin}", admin=admin.id) await asyncio.sleep(0.3) await message.reply_to_message.reply(_("This message is reported to chat administrators."))
async def ask_for_translation(query: types.CallbackQuery): user = await User.get(query.from_user.id) if not user.transifex_username: response = await render_template(user.id, "missing_username") keyboard_markup = make_keyboard( ("⚙️ Configurar usuário", "configure-username"), ) await bot.edit_message_text( quote_html(response), query.message.chat.id, query.message.message_id, disable_web_page_preview=True, reply_markup=keyboard_markup, parse_mode="Markdown", ) return string = user.translating_string user.translate() await user.update() response = await render_template( user.id, "init_translation", source=string.source, transifex_url=string.url, docsurl=docsurl(string.resource).replace("__", "\_\_"), ) await bot.edit_message_text( response, query.message.chat.id, query.message.message_id, disable_web_page_preview=True, parse_mode="Markdown", )
async def admin_commands(m: Message): if m.text == '!info': count = await db.func.count(User.id).gino.scalar() # pylint: disable=no-member time = datetime.now().strftime('|%d.%m.%y - %H:%M|') await m.reply(f"<b>Server time:</b> {time}\n<b>User count:</b> {count}") elif '!log' in m.text: data = '' with open('log.log', 'r') as log: try: for row in deque(log, int(m.text[4:])): data += quote_html(row) await m.reply(data) except (ValueError) as err: await m.reply(f'<b>{err.__class__.__name__}</b> - Введи правильное число строк!') elif '!get' in m.text: if len(m.text) > 4: lst = m.text.split(' ') result = await User.get(int(lst[1])) if result: m.from_user.first_name = result.id await user_profile(m, result, False) else: await m.reply('❗ Ничего не найдено') elif m.text == '!get': await AdminStates.getuser.set() await m.reply("Перешли любое сообщение от юзера.") elif '!deluser' in m.text: if m.text == '!deluser': await AdminStates.deluser.set() await m.reply("Перешли любое сообщение от юзера.") elif '!reload' == m.text: count, user_count = await broadcaster(text='❗ Бот будет перезагружен через через (1) минуту. <i>Рекомендуется покинуть бой/торговую площадку/лазарет</i>.', disable_notification=False) await m.reply(f'❕ Ваше сообщение получили {count}/{user_count} пользователя/ей.') elif m.text[:10] == '!broadcast' or m.text[:11] == '!sbroadcast': text = m.text.split(' ', 1) count, user_count = await broadcaster(text=text[1], disable_notification=False if text[0] == '!broadcast' else True) await m.reply(f'❕ Ваше сообщение получили {count}/{user_count} пользователя/ей.')
async def guide_cmd(message: types.Message): await message.reply( quote_html("Here's what I can do:\n\n" "/btns <count:int> <text> - create a new keyboard\n" "/rm - clear the keyboard"))
def mention_no_link(self): if self.username: rez = hlink(self.fullname, f"t.me/{self.username}") else: rez = quote_html(self.fullname) return rez
async def cmd_version(message: types.Message): await message.reply( _("My Engine:\n{aiogram}").format(aiogram=quote_html(str(aiogram_core.SysInfo()))) )
async def log_info(bot: Bot, log_msg: str): logger.info(log_msg) await bot.send_message(config.LOG_CHAT_ID, quote_html(log_msg), parse_mode=types.ParseMode.HTML)
async def update(tg_chatid, ig_profile): write(f"\033[2K\rchecking @{ig_profile}…") await bot.send_chat_action(tg_chatid, types.ChatActions.TYPING) try: pl = ProfileLooter(ig_profile) except Exception as e: write(f"\033[2K\r\033[31munable to get profile @{ig_profile}\033[0m\n") print(tb.format_exc()) return False with open(sent_fp, "r") as f: sent = json.load(f) sent_something = False for j, media in enumerate(pl.medias()): i = media["id"] sc = media["shortcode"] write(f"\033[2K\rchecking @{ig_profile} ({j}|{i}|{sc})") if i not in sent: write(": \033[sgetting post…") _pl = PostLooter(sc) try: info = _pl.get_post_info(sc) except Exception as e: #because the library I use can randomly throw errors while getting stuff… write("\033[u\033[0K\033[31munable to get post\033[0m\n") print(tb.format_exc()) continue caption = "\n".join( edge["node"]["text"] for edge in info["edge_media_to_caption"]["edges"]) with MemoryFS() as fs: if media["is_video"]: await bot.send_chat_action(tg_chatid, types.ChatActions.RECORD_VIDEO) _pl.download_videos(fs, media_count=1) func = bot.send_video fn = fs.listdir("./")[0] await bot.send_chat_action(tg_chatid, types.ChatActions.UPLOAD_VIDEO) elif media["__typename"].lower() == "graphimage": await bot.send_chat_action(tg_chatid, types.ChatActions.UPLOAD_PHOTO) _pl.download_pictures(fs, media_count=1) func = bot.send_photo fn = fs.listdir("./")[0] elif media["__typename"].lower() == "graphsidecar": await bot.send_chat_action(tg_chatid, types.ChatActions.UPLOAD_PHOTO) _pl.download_pictures(fs) fn = tuple(fs.listdir("./")) if len(fn) == 1: func = bot.send_photo fn = fn[0] else: func = bot.send_media_group else: await bot.send_message( tg_chatid, f"Oh-oh. I've encountered a new post type!\nPlease tell my developer, so he can tell me what I should do with a {media}." ) print("\n\033[31mUNKNOWN MEDIA TYPE AAAAA\033[0m", media) break if isinstance(fn, tuple): write("\033[u\033[0Ksending album…") f = [fs.openbin(_fn) for _fn in fn] _media = types.input_media.MediaGroup() for _f in f: _media.attach_photo(_f) else: write("\033[u\033[0Ksending file…") _media = f = fs.openbin(fn) if len( caption ) > 100: #telegram media captions have a character limit of 200 chars & I want to have a buffer caption = caption[:100] + "[…]" markdown.quote_html(caption) text = f"{caption}\n→<a href=\"https://www.instagram.com/p/{sc}\">original post</a>" try: if isinstance(fn, tuple): msg_id = (await func(tg_chatid, _media))[-1]["message_id"] await bot.send_message(tg_chatid, text, reply_to_message_id=msg_id, parse_mode=types.ParseMode.HTML) else: await func(tg_chatid, _media, caption=text, parse_mode=types.ParseMode.HTML) except exceptions.BadRequest as e: write( "\033[u\033[0K\033[31mskipped\033[0m\nGot Bad Request while trying to send message.\n" ) except exceptions.RetryAfter as e: write( "\nMEEP MEEP FLOOD CONTROL - YOU'RE FLOODING TELEGRAM\nstopping sending messages & waiting for next cycle…\n" ) break else: sent.append(i) write("\033[u\033[0Ksaving sent messages…\033[0m") with open(sent_fp, "w+") as f: json.dump(sent, f) write("\033[u\033[0K\033[32msent\033[0m\n") if isinstance(f, list): for _f in f: _f.close() else: f.close() sent_something = True # sometimes the page has to be reloaded, which would prolong the time the checking post… # message would be displayed if I didn't do this write(f"\033[2K\rchecking @{ig_profile}…") return sent_something
def mention(self): return hlink(self.title, f"t.me/{self.username}") if self.username else quote_html( self.title)
async def translate( message: typing.Union[types.CallbackQuery, types.Message], user: User, string=None ): if isinstance(message, types.Message): message_id = message.message_id chat_id = message.chat.id else: message_id = message.message.message_id chat_id = message.message.chat.id await bot.send_chat_action(chat_id, "typing") if user.state != "idle": user.cancel_translation() await user.update() if not string: string = await random_string( user.language_code, user.project, translated=False, max_size=30, ) user.translating_string = string await user.update() message_1 = f"```\n{string.source}\n```" message_2 = await render_template( user.id, "translate", docsurl=docsurl(string.resource).replace("__", "\_\_"), ) message_2 = quote_html(message_2) if user.is_beta: keyboard_markup = make_keyboard( ("Traduzir no Telegram", "init-translation"), ("Traduzir no Transifex", string.url), ("Escolher outro trecho", "translate"), ) else: keyboard_markup = make_keyboard( ("Traduzir no Transifex", string.url), ("Traduzir no Telegram", "init-translation"), [("⏭ Próximo", "translate"), ("❌ Cancelar", "cancel-translating"),], ) options = { "disable_web_page_preview": True, "parse_mode": "markdown", "reply_markup": keyboard_markup, } try: if isinstance(message, types.Message): await bot.send_message(user.id, message_1, parse_mode="markdown") await bot.send_message(user.id, message_2, **options) else: await bot.edit_message_text( message_1, chat_id, message_id - 1, parse_mode="markdown", ) await bot.edit_message_text( message_2, chat_id, message_id, **options, ) except BotBlocked: logger.exception("i17obot blocked by user. userid=%r", user.id) except TelegramAPIError: logger.exception( "Telegram API Error while sending message. message_1=%r, message_2=%r", message_1, message_2, )