def set_keywords(bot, update, chat_data, to_edit): chat_id = util.uid_from_update(update) keywords = Keyword.select().where(Keyword.entity == to_edit) chat_data['edit_bot'] = to_edit set_keywords_msgid = chat_data.get('set_keywords_msg') kw_remove_buttons = [ InlineKeyboardButton('{} ✖️'.format(x), callback_data=util.callback_for_action( CallbackActions.REMOVE_KEYWORD, { 'id': to_edit.id, 'kwid': x.id })) for x in keywords ] buttons = util.build_menu( kw_remove_buttons, 2, header_buttons=[ InlineKeyboardButton(captions.DONE, callback_data=util.callback_for_action( CallbackActions.ABORT_SETTING_KEYWORDS, {'id': to_edit.id})) ]) reply_markup = InlineKeyboardMarkup(buttons) msg = util.send_or_edit_md_message( bot, chat_id, util.action_hint( 'Send me the keywords for {} one by one...\n\n{}'.format( util.escape_markdown(to_edit.username), messages.KEYWORD_BEST_PRACTICES)), to_edit=set_keywords_msgid, reply_markup=reply_markup) chat_data['set_keywords_msg'] = msg.message_id return BotStates.SENDING_KEYWORDS
def edit_bot_category(bot, update, for_bot, callback_action=None): if callback_action is None: callback_action = CallbackActions.EDIT_BOT_CAT_SELECTED uid = util.uid_from_update(update) categories = Category.select().order_by(Category.name.asc()).execute() buttons = util.build_menu( [ InlineKeyboardButton( "{}{}".format(emoji.emojize(c.emojis, use_aliases=True), c.name), callback_data=util.callback_for_action(callback_action, { "cid": c.id, "bid": for_bot.id }), ) for c in categories ], 2, ) return bot.formatter.send_or_edit( uid, util.action_hint("Please select a category" + (" for {}".format(for_bot) if for_bot else "")), to_edit=util.mid_from_update(update), reply_markup=InlineKeyboardMarkup(buttons), )
def send_category(bot, update, chat_data, category): uid = util.uid_from_update(update) cid = update.effective_chat.id bots = Bot.of_category_without_new( category)[:settings.MAX_BOTS_PER_MESSAGE] bots_with_description = [b for b in bots if b.description is not None] detailed_buttons_enabled = len( bots_with_description) > 0 and util.is_private_message(update) callback = CallbackActions.SEND_BOT_DETAILS if detailed_buttons_enabled: buttons = [ InlineKeyboardButton( x.username, callback_data=util.callback_for_action(callback, {"id": x.id}), ) for x in bots_with_description ] else: buttons = [] menu = util.build_menu(buttons, 2) menu.insert( 0, [ InlineKeyboardButton( captions.BACK, callback_data=util.callback_for_action( CallbackActions.SELECT_CATEGORY), ), InlineKeyboardButton( "Show in BotList", url="http://t.me/botlist/{}".format( category.current_message_id), ), InlineKeyboardButton("Share", switch_inline_query=category.name), ], ) txt = "There are *{}* bots in the category *{}*:\n\n".format( len(bots), str(category)) if uid in settings.MODERATORS and util.is_private_message(update): # append admin edit buttons txt += "\n".join(["{} — /edit{} 🛃".format(b, b.id) for b in bots]) else: txt += "\n".join([str(b) for b in bots]) if detailed_buttons_enabled: txt += "\n\n" + util.action_hint( "Press a button below to get a detailed description.") reply_markup = InlineKeyboardMarkup(menu) reply_markup, callback = botlistchat.append_restricted_delete_button( update, chat_data, reply_markup) msg = bot.formatter.send_or_edit(cid, txt, to_edit=util.mid_from_update(update), reply_markup=reply_markup) callback(msg) Statistic.of(update, "menu", "of category {}".format(str(category)), Statistic.ANALYSIS)
def set_country_menu(bot, update, to_edit): uid = util.uid_from_update(update) countries = Country.select().order_by(Country.name).execute() buttons = util.build_menu([ InlineKeyboardButton('{} {}'.format(c.emojized, c.name), callback_data=util.callback_for_action( CallbackActions.SET_COUNTRY, { 'cid': c.id, 'bid': to_edit.id })) for c in countries ], 3) buttons.insert(0, [ InlineKeyboardButton(captions.BACK, callback_data=util.callback_for_action( CallbackActions.EDIT_BOT, {'id': to_edit.id})), InlineKeyboardButton("None", callback_data=util.callback_for_action( CallbackActions.SET_COUNTRY, { 'cid': 'None', 'bid': to_edit.id })), ]) return bot.formatter.send_or_edit( uid, util.action_hint( "Please select a country/language for {}".format(to_edit)), to_edit=util.mid_from_update(update), reply_markup=InlineKeyboardMarkup(buttons))
def prepare_transmission(bot, update, chat_data): chat_id = util.uid_from_update(update) text = mdformat.action_hint("Notify subscribers about this update?") reply_markup = InlineKeyboardMarkup( [[ InlineKeyboardButton("☑ Notifications", callback_data=util.callback_for_action( CallbackActions.SEND_BOTLIST, {'silent': False})), InlineKeyboardButton("Silent", callback_data=util.callback_for_action( CallbackActions.SEND_BOTLIST, {'silent': True})) ], [ InlineKeyboardButton("Re-send all Messages", callback_data=util.callback_for_action( CallbackActions.SEND_BOTLIST, { 'silent': True, 're': True })) ]]) # # TODO # text = "Temporarily disabled" # reply_markup = None util.send_md_message(bot, chat_id, text, reply_markup=reply_markup)
def add_favorite(bot, update, item: Bot, callback_alert=None): user = User.from_update(update) uid = util.uid_from_update(update) mid = util.mid_from_update(update) from components.basic import main_menu_buttons main_menu_markup = ReplyKeyboardMarkup(main_menu_buttons(uid in settings.MODERATORS)) fav, created = Favorite.add(user=user, item=item) if created: Statistic.of(user, 'add-favorite', item.username) text = mdformat.love("{} added to your {}favorites.".format(fav.bot, '' if callback_alert else '/')) if callback_alert: update.callback_query.answer(text=text, show_alert=False) else: msg = util.send_md_message(bot, uid, text, to_edit=mid, reply_markup=main_menu_markup) mid = msg.message_id util.wait(bot, update) send_favorites_list(bot, update, to_edit=mid) else: text = mdformat.none_action( "{} is already a favorite of yours.{}".format(fav.bot, '' if callback_alert else ' /favorites')) if callback_alert: update.callback_query.answer(text=text, show_alert=False) else: util.send_md_message(bot, uid, text, reply_markup=main_menu_markup) return ConversationHandler.END
def _input_failed(bot, update, chat_data, text): chat_id = util.uid_from_update(update) bot.formatter.send_failure(chat_id, text) Statistic.of(update, 'error', 'input failed in admin menu for {}'.format(text), Statistic.ANALYSIS) chat_data['add_bot_message'] = None
def set_text_property(bot, update, chat_data, property_name, to_edit=None): uid = util.uid_from_update(update) user = User.from_update(update) if check_suggestion_limit(bot, update, user): return if to_edit: text = (util.escape_markdown(getattr(to_edit, property_name)) + "\n\n" if getattr(to_edit, property_name) else '') text += mdformat.action_hint( messages.SET_BOTPROPERTY.format( property_name, util.escape_markdown(to_edit.username), CLEAR_QUERY)) if property_name == 'description': text += ', markdown enabled.' update.effective_message.reply_text( text, reply_markup=ForceReply(selective=True), parse_mode=ParseMode.MARKDOWN) chat_data['edit_bot'] = to_edit elif update.message: value = None text = update.message.text to_edit = chat_data.get('edit_bot', None) def too_long(n): bot.formatter.send_failure( uid, "Your {} text is too long, it must be shorter " "than {} characters. Please try again.".format( property_name, n)) util.wait(bot, update) return admin.edit_bot(bot, update, chat_data, to_edit) # Validation if property_name == 'description' and len(text) > 300: return too_long(300) if property_name == 'username': value = helpers.validate_username(text) if value: to_edit = chat_data.get('edit_bot', None) else: bot.formatter.send_failure( uid, "The username you entered is not valid. Please try again..." ) return admin.edit_bot(bot, update, chat_data, to_edit) if not value: value = text if to_edit: if _is_clear_query(text): Suggestion.add_or_update(user, property_name, to_edit, None) else: Suggestion.add_or_update(user, property_name, to_edit, value) admin.edit_bot(bot, update, chat_data, to_edit) else: bot.formatter.send_failure(uid, "An unexpected error occured.")
def delete_bot(bot, update, to_edit): chat_id = util.uid_from_update(update) username = to_edit.username to_edit.delete_instance() bot.formatter.send_or_edit(chat_id, "Bot has been deleted.", to_edit=util.mid_from_update(update)) Statistic.of(update, 'delete', username, Statistic.IMPORTANT)
def edit_bot(bot, update, chat_data, to_edit=None): uid = util.uid_from_update(update) message_id = util.mid_from_update(update) user = User.from_update(update) if not to_edit: if update.message: command = update.message.text if "edit" in command: b_id = re.match(r"^/edit(\d+)$", command).groups()[0] elif "approve" in command: b_id = re.match(r"^/approve(\d+)$", command).groups()[0] else: raise ValueError("No 'edit' or 'approve' in command.") try: to_edit = Bot.get(id=b_id) except Bot.DoesNotExist: update.message.reply_text( util.failure("No bot exists with this id.")) return else: bot.formatter.send_failure(uid, "An unexpected error occured.") return # if not to_edit.approved: # return approve_bots(bot, update, override_list=[to_edit]) pending_suggestions = Suggestion.pending_for_bot(to_edit, user) reply_markup = InlineKeyboardMarkup( _edit_bot_buttons(to_edit, pending_suggestions, uid in settings.MODERATORS)) pending_text = ("\n\n{} Some changes are pending approval{}.".format( captions.SUGGESTION_PENDING_EMOJI, "" if user.chat_id in settings.MODERATORS else " by a moderator", ) if pending_suggestions else "") meta_text = ("\n\nDate added: {}\nMember since revision {}\n" "Submitted by {}\nApproved by {}".format( to_edit.date_added, to_edit.revision, to_edit.submitted_by, to_edit.approved_by, )) bot.formatter.send_or_edit( uid, "🛃 Edit {}{}{}".format( to_edit.detail_text, meta_text if user.id in settings.MODERATORS else "", pending_text, ), to_edit=message_id, reply_markup=reply_markup, )
def send_next(bot, update, args=None): uid = util.uid_from_update(update) rows = None if args: try: rows = int(args[0]) except: rows = None reply_markup = ReplyKeyboardMarkup(_crapPy_Tr0ll_kbmarkup(rows), one_time_keyboard=True, per_user=True) text = 'ɹoʇɐɹǝuǝb ǝɯɐuɹǝsn ɯɐɹbǝןǝʇ' util.send_md_message(bot, uid, text, reply_markup=reply_markup)
def _too_many_favorites_handler(bot, update, user): uid = util.uid_from_update(update) any_removed = False while too_many_favorites(user): oldest = Favorite.get_oldest(user) oldest.delete_instance() any_removed = True Statistic.of(update, 'had to lose a favorite because HE HAD TOO F****N MANY 😬') if any_removed: txt = "You have too many favorites, _they do not fit into a single message_. That's why I removed your " \ "oldest bot, *{}*, from your list of favorites.".format(oldest.bot if oldest.bot else oldest.custom_bot) util.send_md_message(bot, uid, txt)
def accept_bot_submission(bot, update, of_bot: Bot, category): uid = util.uid_from_update(update) message_id = util.mid_from_update(update) user = User.from_update(update) try: of_bot.category = category of_bot.date_added = datetime.date.today() of_bot.approved = True of_bot.approved_by = user of_bot.save() buttons = [[ InlineKeyboardButton( "Edit {} details".format(of_bot.username), callback_data=util.callback_for_action( CallbackActions.EDIT_BOT, {"id": of_bot.id}), ) ]] reply_markup = InlineKeyboardMarkup(buttons) bot.formatter.send_or_edit( uid, "{} has been accepted to the Botlist. ".format( of_bot, settings.BOT_ACCEPTED_IDLE_TIME), to_edit=message_id, reply_markup=reply_markup, ) log_msg = "{} accepted by {}.".format(of_bot.username, uid) # notify submittant if of_bot.submitted_by != user: try: bot.sendMessage( of_bot.submitted_by.chat_id, util.success( messages.ACCEPTANCE_PRIVATE_MESSAGE.format( of_bot.username, of_bot.category)), ) log_msg += "\nUser {} was notified.".format( str(of_bot.submitted_by)) except TelegramError: log_msg += "\nUser {} could NOT be contacted/notified in private.".format( str(of_bot.submitted_by)) log.info(log_msg) except: bot.formatter.send_failure(uid, "An error has occured. Bot not added.")
def delete_bot_confirm(bot, update, to_edit): chat_id = util.uid_from_update(update) reply_markup = InlineKeyboardMarkup([[ InlineKeyboardButton("Yes, delete it!", callback_data=util.callback_for_action( CallbackActions.DELETE_BOT, {'id': to_edit.id})), InlineKeyboardButton(captions.BACK, callback_data=util.callback_for_action( CallbackActions.EDIT_BOT, {'id': to_edit.id})) ]]) bot.formatter.send_or_edit(chat_id, "Are you sure?", to_edit=util.mid_from_update(update), reply_markup=reply_markup)
def remove_favorite_menu(bot, update): uid = util.uid_from_update(update) user = User.from_update(update) favorites = Favorite.select_all(user) fav_remove_buttons = [InlineKeyboardButton( '✖️ {}'.format(str(f.bot.username)), callback_data=util.callback_for_action(CallbackActions.REMOVE_FAVORITE, {'id': f.id})) for f in favorites] buttons = util.build_menu(fav_remove_buttons, 2, header_buttons=[ InlineKeyboardButton(captions.DONE, callback_data=util.callback_for_action(CallbackActions.SEND_FAVORITES_LIST)) ]) reply_markup = InlineKeyboardMarkup(buttons) bot.formatter.send_or_edit(uid, util.action_hint("Select favorites to remove"), to_edit=util.mid_from_update(update), reply_markup=reply_markup)
def send_offline(bot, update): chat_id = util.uid_from_update(update) offline = Bot.select(Bot, Ping).join(Ping, JOIN.LEFT_OUTER).where( Bot.offline == True).order_by(Ping.last_response.asc()) def offline_since(b): if not b.ping.last_response: return 'a long time' slanged_time = helpers.slang_datetime(b.ping.last_response) return slanged_time.replace(' ago', '') if len(offline) > 0: text = "Offline Bots:\n\n" text += '\n'.join([ "{}{} — /edit{}".format(str(b), " (for {})".format(offline_since(b)), b.id) for b in offline ]) else: text = "No bots are offline." bot.formatter.send_message(chat_id, text)
def send_next(bot, update, job_queue: JobQueue, args=None): uid = util.uid_from_update(update) num_rows = None if args: try: num_rows = int(args[0]) except: num_rows = None reply_markup = ReplyKeyboardMarkup(_crapPy_Tr0ll_kbmarkup(num_rows), one_time_keyboard=False, per_user=True) text = "ɹoʇɐɹǝuǝb ǝɯɐuɹǝsn ɯɐɹbǝןǝʇ" util.send_md_message(bot, uid, text, reply_markup=reply_markup) if util.is_group_message(update): del_msg = bot.formatter.send_message( update.effective_chat.id, "Have fun in private ;)\n/easteregg") update.effective_message.delete() job_queue.run_once(lambda *_: del_msg.delete(safe=True), 4, name="delete easteregg hint")
def add_favorite_handler(bot, update, args=None): uid = util.uid_from_update(update) from components.basic import main_menu_buttons main_menu_markup = ReplyKeyboardMarkup(main_menu_buttons(uid in settings.MODERATORS)) if args: query = ' '.join(args) if isinstance(args, list) else args try: # TODO: add multiple username = re.match(settings.REGEX_BOT_IN_TEXT, query).groups()[0] try: # TODO: get exact database matches for input without `@` item = Bot.by_username(username, include_disabled=True) return add_favorite(bot, update, item) except Bot.DoesNotExist: buttons = [ InlineKeyboardButton( "Yai!", callback_data=util.callback_for_action(CallbackActions.ADD_ANYWAY, {'u': username})), InlineKeyboardButton("Nay...", callback_data=util.callback_for_action(CallbackActions.ADD_FAVORITE)) ] reply_markup = InlineKeyboardMarkup([buttons]) util.send_md_message(bot, uid, "{} is not in the @BotList. Do you want to add it to your {} anyway?".format( username, captions.FAVORITES), reply_markup=reply_markup) except AttributeError: # invalid bot username # TODO when does this happen? update.message.reply_text( util.failure("Sorry, but that is not a valid username. Please try again. /addfav")) else: buttons = [ InlineKeyboardButton("Search inline", switch_inline_query_current_chat='') ] reply_markup = InlineKeyboardMarkup([buttons]) bot.sendMessage(uid, messages.ADD_FAVORITE, reply_markup=ForceReply(selective=True)) return ConversationHandler.END
def send_offline(bot, update): chat_id = util.uid_from_update(update) offline = (Bot.select().where(Bot.offline == True, Bot.disabled == False).order_by( Bot.last_response.asc())) def offline_since(b): if not b.last_response: return "a long time" slanged_time = helpers.slang_datetime(b.last_response) return slanged_time.replace(" ago", "") if len(offline) > 0: text = "Offline Bots:\n\n" text += "\n".join([ "{}{} — /edit{}".format(str(b), " (for {})".format(offline_since(b)), b.id) for b in offline ]) else: text = "No bots are offline." bot.formatter.send_message(chat_id, text)
def send_favorites_list(bot, update, to_edit=None): uid = util.uid_from_update(update) user = User.from_update(update) t = threading.Thread(target=_too_many_favorites_handler, args=(bot, update, user)) t.start() favorites = Favorite.select_all(user) buttons = [ [ InlineKeyboardButton(captions.ADD_FAVORITE, callback_data=util.callback_for_action(CallbackActions.ADD_FAVORITE)), InlineKeyboardButton(captions.REMOVE_FAVORITE, callback_data=util.callback_for_action(CallbackActions.REMOVE_FAVORITE_MENU)) ], [ InlineKeyboardButton('Layout: ' + Layouts.get_caption(user.favorites_layout), callback_data=util.callback_for_action( CallbackActions.TOGGLE_FAVORITES_LAYOUT, {'v': Layouts.get_next(user.favorites_layout)})), ], [ InlineKeyboardButton(captions.SHARE, switch_inline_query=DeepLinkingActions.FAVORITES), ] ] reply_markup = InlineKeyboardMarkup(buttons) if to_edit is None: to_edit = util.mid_from_update(update) if len(favorites) == 0: text = "You have no favorites yet." else: text = _favorites_categories_md(favorites, user.favorites_layout) bot.formatter.send_or_edit(uid, text, to_edit=to_edit, reply_markup=reply_markup)
def add_custom(bot, update, username): uid = util.uid_from_update(update) user = User.from_update(update) mid = util.mid_from_update(update) from components.basic import main_menu_buttons main_menu_markup = ReplyKeyboardMarkup(main_menu_buttons(uid in settings.MODERATORS)) try: fav = Favorite.get(custom_bot=username) util.send_or_edit_md_message( bot, uid, mdformat.none_action( "{} is already a favorite of yours. /favorites".format(fav.custom_bot)), to_edit=mid, reply_markup=main_menu_markup) except Favorite.DoesNotExist: fav = Favorite(user=user, custom_bot=username, date_added=datetime.date.today()) fav.save() msg = bot.formatter.send_or_edit(uid, mdformat.love("{} added to your /favorites.".format(fav.custom_bot)), to_edit=mid) mid = msg.message_id util.wait(bot, update) send_favorites_list(bot, update, to_edit=mid) return ConversationHandler.END
def reject_bot_submission( bot, update, args=None, to_reject=None, verbose=True, notify_submittant=True, reason=None, ): uid = util.uid_from_update(update) user = User.from_update(update) if to_reject is None: if not update.message.reply_to_message: bot.send_message( update.effective_user.id, util.failure("You must reply to a message of mine."), ) return text = update.message.reply_to_message.text reason = reason if reason else (" ".join(args) if args else None) try: update.message.delete() except: pass username = helpers.find_bots_in_text(text, first=True) if not username: bot.send_message( update.effective_user.id, util.failure( "No username in the message that you replied to."), ) return try: to_reject = Bot.by_username(username) except Bot.DoesNotExist: bot.send_message( update.effective_user.id, util.failure("Rejection failed: {} is not present in the " "database.".format(username)), ) return if to_reject.approved is True: msg = "{} has already been accepted, so it cannot be rejected anymore.".format( username) bot.sendMessage(uid, util.failure(msg)) return Statistic.of(update, "reject", to_reject.username) text = notify_submittant_rejected(bot, user, notify_submittant, reason, to_reject) to_reject.delete_instance() if verbose: bot.sendMessage(uid, text) if update.callback_query: update.callback_query.answer(text=text)
def reject_bot_submission(bot, update, args=None, to_reject=None, verbose=True, notify_submittant=True, reason=None): uid = util.uid_from_update(update) user = User.from_update(update) if to_reject is None: if not update.message.reply_to_message: bot.send_message( update.effective_user.id, util.failure("You must reply to a message of mine.")) return text = update.message.reply_to_message.text reason = reason if reason else (" ".join(args) if args else None) try: update.message.delete() except: pass username = helpers.find_bots_in_text(text, first=True) if not username: bot.send_message( update.effective_user.id, util.failure( "No username in the message that you replied to.")) return try: to_reject = Bot.by_username(username) except Bot.DoesNotExist: bot.send_message(update.effective_user.id, util.failure("Rejection failed: {} is not present in the " \ "database.".format(username))) return if to_reject.approved is True: msg = "{} has already been accepted, so it cannot be rejected anymore.".format( username) bot.sendMessage(uid, util.failure(msg)) return Statistic.of(update, 'reject', to_reject.username) log_msg = "{} rejected by {}.".format(to_reject.username, user) notification_successful = None if notify_submittant or reason: try: if reason: bot.send_message( to_reject.submitted_by.chat_id, util.failure( messages.REJECTION_WITH_REASON.format( to_reject.username, reason=reason))) else: bot.sendMessage( to_reject.submitted_by.chat_id, util.failure( messages.REJECTION_PRIVATE_MESSAGE.format( to_reject.username))) log_msg += "\nUser {} was notified.".format( str(to_reject.submitted_by)) notification_successful = True except TelegramError: log_msg += "\nUser {} could NOT be contacted/notified in private.".format( str(to_reject.submitted_by)) notification_successful = False to_reject.delete_instance() log.info(log_msg) text = util.success("{} rejected.".format(to_reject.username)) if notification_successful is True: text += " User {} was notified.".format( to_reject.submitted_by.plaintext) elif notification_successful is False: text += ' ' + mdformat.failure("Could not contact {}.".format( to_reject.submitted_by.plaintext)) else: text += " No notification sent." if verbose: bot.sendMessage(uid, text) if update.callback_query: update.callback_query.answer(text=text)
def restart(bot, update): chat_id = util.uid_from_update(update) os.kill(os.getpid(), signal.SIGINT) bot.formatter.send_success(chat_id, "Bot is restarting...") time.sleep(0.3) os.execl(sys.executable, sys.executable, *sys.argv)
def approve_bots(bot, update, page=0, override_list=None): chat_id = util.uid_from_update(update) if override_list: unapproved = override_list else: unapproved = Bot.select().where(Bot.approved == False).order_by( Bot.date_added) if page < 0: page = 0 last_page = int((len(unapproved) - 1) / settings.PAGE_SIZE_BOT_APPROVAL) if page * settings.PAGE_SIZE_BOT_APPROVAL >= len(unapproved): # old item deleted, list now too small page = last_page start = page * settings.PAGE_SIZE_BOT_APPROVAL end = start + settings.PAGE_SIZE_BOT_APPROVAL has_prev_page = page > 0 has_next_page = (page + 1) * settings.PAGE_SIZE_BOT_APPROVAL < len(unapproved) unapproved = unapproved[start:end] if len(unapproved) == 0: bot.formatter.send_or_edit(chat_id, "No more unapproved bots available. " "Good job! (Is this the first time? 😂)", to_edit=util.mid_from_update(update)) return buttons = list() for x in unapproved: first_row = [ InlineKeyboardButton(x.username, url="http://t.me/{}".format(x.username[1:])) ] second_row = [ InlineKeyboardButton('👍', callback_data=util.callback_for_action( CallbackActions.ACCEPT_BOT, {'id': x.id})), InlineKeyboardButton('👎', callback_data=util.callback_for_action( CallbackActions.REJECT_BOT, { 'id': x.id, 'page': page, 'ntfc': True })), InlineKeyboardButton('🗑', callback_data=util.callback_for_action( CallbackActions.REJECT_BOT, { 'id': x.id, 'page': page, 'ntfc': False })), InlineKeyboardButton('👥🔀', callback_data=util.callback_for_action( CallbackActions.RECOMMEND_MODERATOR, { 'id': x.id, 'page': page })) ] if len(unapproved) > 1: buttons.append(first_row) buttons.append(second_row) page_arrows = list() if has_prev_page: page_arrows.append( InlineKeyboardButton('⏮', callback_data=util.callback_for_action( CallbackActions.SWITCH_APPROVALS_PAGE, {'page': -1}))) page_arrows.append( InlineKeyboardButton(Emoji.LEFTWARDS_BLACK_ARROW, callback_data=util.callback_for_action( CallbackActions.SWITCH_APPROVALS_PAGE, {'page': page - 1}))) if has_prev_page or has_next_page: page_arrows.append( InlineKeyboardButton('·{}·'.format(page + 1), callback_data=util.callback_for_action( CallbackActions.SWITCH_APPROVALS_PAGE, {'page': page}))) if has_next_page: page_arrows.append( InlineKeyboardButton(Emoji.BLACK_RIGHTWARDS_ARROW, callback_data=util.callback_for_action( CallbackActions.SWITCH_APPROVALS_PAGE, {'page': page + 1}))) page_arrows.append( InlineKeyboardButton('⏭', callback_data=util.callback_for_action( CallbackActions.SWITCH_APPROVALS_PAGE, {'page': last_page}))) buttons.append(page_arrows) reply_markup = InlineKeyboardMarkup(buttons) text = "What to do with {}?".format( util.escape_markdown(unapproved[0].username)) if len( unapproved ) == 1 else "Please select a bot you want to accept for the BotList" bot.formatter.send_or_edit(chat_id, util.action_hint(text), reply_markup=reply_markup, to_edit=util.mid_from_update(update)) return CallbackStates.APPROVING_BOTS
def approve_suggestions(bot, update, page=0): uid = util.uid_from_update(update) suggestions = Suggestion.select_all() if page * settings.PAGE_SIZE_SUGGESTIONS_LIST >= len(suggestions): # old item deleted, list now too small page = page - 1 if page > 0 else 0 start = page * settings.PAGE_SIZE_SUGGESTIONS_LIST end = start + settings.PAGE_SIZE_SUGGESTIONS_LIST has_prev_page = page > 0 has_next_page = (page + 1) * settings.PAGE_SIZE_SUGGESTIONS_LIST < len( suggestions) suggestions = suggestions[start:end] if len(suggestions) == 0: bot.formatter.send_or_edit(uid, "No more suggestions available.", to_edit=util.mid_from_update(update)) return buttons = [] count = 1 text = "Please choose suggestions to accept.\n" for x in suggestions: number = str(count) + '.' text += "\n{} {}".format(number, str(x)) row = [] # Should the suggestion be editable and is it too long? if x.action in Suggestion.TEXTUAL_ACTIONS: row.append( InlineKeyboardButton("{} {}📝".format( number, Emoji.WHITE_HEAVY_CHECK_MARK), callback_data=util.callback_for_action( CallbackActions.CHANGE_SUGGESTION, { 'id': x.id, 'page': page }))) else: row.append( InlineKeyboardButton("{} {}".format( number, Emoji.WHITE_HEAVY_CHECK_MARK), callback_data=util.callback_for_action( CallbackActions.ACCEPT_SUGGESTION, { 'id': x.id, 'page': page }))) row.append( InlineKeyboardButton("{} {}".format(number, Emoji.CROSS_MARK), callback_data=util.callback_for_action( CallbackActions.REJECT_SUGGESTION, { 'id': x.id, 'page': page }))) buttons.append(row) count += 1 page_arrows = list() if has_prev_page: page_arrows.append( InlineKeyboardButton(Emoji.LEFTWARDS_BLACK_ARROW, callback_data=util.callback_for_action( CallbackActions.SWITCH_SUGGESTIONS_PAGE, {'page': page - 1}))) if has_next_page: page_arrows.append( InlineKeyboardButton(Emoji.BLACK_RIGHTWARDS_ARROW, callback_data=util.callback_for_action( CallbackActions.SWITCH_SUGGESTIONS_PAGE, {'page': page + 1}))) buttons.append(page_arrows) reply_markup = InlineKeyboardMarkup(buttons) bot.formatter.send_or_edit(uid, util.action_hint(text), reply_markup=reply_markup, to_edit=util.mid_from_update(update), disable_web_page_preview=True) return CallbackStates.APPROVING_BOTS
def toggle_favorites_layout(bot, update, value): uid = util.uid_from_update(update) user = User.from_update(update) user.favorites_layout = value user.save() send_favorites_list(bot, update)
def reject_bot_submission(bot, update, to_reject=None, verbose=True, notify_submittant=True): uid = util.uid_from_update(update) user = User.from_update(update) if to_reject is None: if not update.message.reply_to_message: update.message.reply_text( util.failure("You must reply to a message of mine.")) return text = update.message.reply_to_message.text try: username = re.match(settings.REGEX_BOT_IN_TEXT, text).groups()[0] except AttributeError: log.info("No username in the message that was replied to.") # no bot username, ignore update return try: to_reject = Bot.by_username(username) except Bot.DoesNotExist: log.info("Rejection failed: could not find {}".format(username)) return if to_reject.approved is True: msg = "{} has already been accepted, so it cannot be rejected anymore.".format( username) log.warning("Race condition detected: " + msg) bot.sendMessage(uid, util.failure(msg)) return Statistic.of(update, 'reject', to_reject.username) log_msg = "{} rejected by {}.".format(to_reject.username, user) notification_successful = None if notify_submittant: try: bot.sendMessage( to_reject.submitted_by.chat_id, util.failure( messages.REJECTION_PRIVATE_MESSAGE.format( to_reject.username))) log_msg += "\nUser {} was notified.".format( str(to_reject.submitted_by)) notification_successful = True except TelegramError: log_msg += "\nUser {} could NOT be contacted/notified in private.".format( str(to_reject.submitted_by)) notification_successful = False to_reject.delete_instance() log.info(log_msg) text = util.success("{} rejected.".format(to_reject.username)) if notification_successful is True: text += " User {} was notified.".format( to_reject.submitted_by.plaintext) elif notification_successful is False: text += ' ' + mdformat.failure("Could not contact {}.".format( to_reject.submitted_by.plaintext)) else: text += " No notification sent." if verbose: bot.sendMessage(uid, text) if update.callback_query: update.callback_query.answer(text=text)
def new_bot_submission(bot, update, chat_data, args=None): tg_user = update.message.from_user user = User.from_telegram_object(tg_user) if util.stop_banned(update, user): return reply_to = util.original_reply_id(update) if args: text = ' '.join(args) else: text = update.message.text command_no_args = len( re.findall(r'^/new\s*$', text)) > 0 or text.lower().strip() == '/new@botlistbot' if command_no_args: update.message.reply_text(util.action_hint( "Please use this command with an argument. For example:\n/new @mybot 🔎" ), reply_to_message_id=reply_to) return # `#new` is already checked by handler try: username = re.match(settings.REGEX_BOT_IN_TEXT, text).groups()[0] if username.lower() == '@' + settings.SELF_BOT_NAME.lower(): log.info("Ignoring {}".format(text)) return except AttributeError: if args: update.message.reply_text(util.failure( "Sorry, but you didn't send me a bot `@username`."), quote=True, parse_mode=ParseMode.MARKDOWN, reply_to_message_id=reply_to) log.info("Ignoring {}".format(text)) # no bot username, ignore update return try: new_bot = Bot.by_username(username) if new_bot.approved: update.message.reply_text(util.action_hint( "Sorry fool, but {} is already in the @BotList 😉".format( new_bot.username)), reply_to_message_id=reply_to) else: update.message.reply_text(util.action_hint( "{} has already been submitted. Please have patience...". format(new_bot.username)), reply_to_message_id=reply_to) return except Bot.DoesNotExist: new_bot = Bot(revision=Revision.get_instance().next, approved=False, username=username, submitted_by=user) new_bot.inlinequeries = "🔎" in text new_bot.official = "🔹" in text # find language languages = Country.select().execute() for lang in languages: if lang.emoji in text: new_bot.country = lang new_bot.date_added = datetime.date.today() description_reg = re.match(settings.REGEX_BOT_IN_TEXT + ' -\s?(.*)', text) description_notify = '' if description_reg: description = description_reg.group(2) new_bot.description = description description_notify = ' Your description was included.' new_bot.save() if util.is_private_message(update) and util.uid_from_update( update) in settings.MODERATORS: from components.explore import send_bot_details send_bot_details(bot, update, chat_data, new_bot) else: msg = update.message.reply_text(util.success( "You submitted {} for approval.{}".format(new_bot, description_notify)), parse_mode=ParseMode.MARKDOWN, reply_to_message_id=reply_to) return ConversationHandler.END
def set_keywords(bot, update, chat_data, to_edit): chat_id = util.uid_from_update(update) keywords = Keyword.select().where(Keyword.entity == to_edit) chat_data['edit_bot'] = to_edit set_keywords_msgid = chat_data.get('set_keywords_msg') pending = Suggestion.select().where( Suggestion.executed == False, Suggestion.subject == to_edit, Suggestion.action << ['add_keyword', 'remove_keyword']) pending_removal = [y for y in pending if y.action == 'remove_keyword'] # Filter keywords by name to not include removal suggestions # We don't need to do this for add_keyword suggestions, because duplicates are not allowed. keywords = [ k for k in keywords if k.name not in [s.value for s in pending_removal] ] kw_remove_buttons = [ InlineCallbackButton('{} ✖️'.format(x), callback_action=CallbackActions.REMOVE_KEYWORD, params={ 'id': to_edit.id, 'kwid': x.id }) for x in keywords ] kw_remove_buttons.extend([ InlineKeyboardButton('#{} 👓✖️'.format(x.value), callback_data=util.callback_for_action( CallbackActions.DELETE_KEYWORD_SUGGESTION, { 'id': to_edit.id, 'suggid': x.id })) for x in [y for y in pending if y.action == 'add_keyword'] ]) kw_remove_buttons.extend([ InlineKeyboardButton('#{} 👓❌'.format(x.value), callback_data=util.callback_for_action( CallbackActions.DELETE_KEYWORD_SUGGESTION, { 'id': to_edit.id, 'suggid': x.id })) for x in pending_removal ]) buttons = util.build_menu( kw_remove_buttons, 2, header_buttons=[ InlineKeyboardButton(captions.DONE, callback_data=util.callback_for_action( CallbackActions.ABORT_SETTING_KEYWORDS, {'id': to_edit.id})) ]) reply_markup = InlineKeyboardMarkup(buttons) msg = util.send_or_edit_md_message( bot, chat_id, util.action_hint( 'Send me the keywords for {} one by one...\n\n{}'.format( util.escape_markdown(to_edit.username), messages.KEYWORD_BEST_PRACTICES)), to_edit=set_keywords_msgid, reply_markup=reply_markup) if msg: # message might not have been edited if the user adds an already-existing keyword # TODO: should the user be notified about this? chat_data['set_keywords_msg'] = msg.message_id return BotStates.SENDING_KEYWORDS