def add_keyword(bot, update, chat_data): user = User.from_telegram_object(update.effective_user) if check_suggestion_limit(bot, update, user): return kw = update.message.text bot_to_edit = chat_data.get('edit_bot') kw = helpers.format_keyword(kw) # Sanity checks if kw in settings.FORBIDDEN_KEYWORDS: update.message.reply_text('The keyword {} is forbidden.'.format(kw)) return if len(kw) <= 1: update.message.reply_text('Keywords must be longer than 1 character.') return if len(kw) >= 20: update.message.reply_text( 'Keywords must not be longer than 20 characters.') # Ignore duplicates try: Keyword.get((Keyword.name == kw) & (Keyword.entity == bot_to_edit)) return except Keyword.DoesNotExist: pass Suggestion.add_or_update(user=user, action='add_keyword', subject=bot_to_edit, value=kw) set_keywords(bot, update, chat_data, bot_to_edit) Statistic.of(update, 'added keyword to'.format(kw), bot_to_edit.username)
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 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 add_or_update(user, action, subject, value): from models import Statistic # value may be None already_exists = Suggestion.get_pending(action, subject, user, value) if already_exists: # Does the new suggestion reset the value? if action == 'remove_keyword': try: kw = Keyword.get(entity=subject, name=value) kw.delete_instance() except Keyword.DoesNotExist: pass elif action == 'add_keyword': return # TODO: is this right? elif value == getattr(already_exists.subject, action): already_exists.delete_instance() return None already_exists.value = value already_exists.save() Statistic.of(user, 'made changes to their suggestion: ', str(already_exists)) return already_exists else: new_suggestion = Suggestion(user=user, action=action, date=datetime.date.today(), subject=subject, value=value) new_suggestion.save() Statistic.of(user, 'suggestion', new_suggestion._md_plaintext()) return new_suggestion
def delete_bot(bot, update, to_edit: Bot): username = to_edit.username to_edit.disable(Bot.DisabledReason.banned) to_edit.save() bot.formatter.send_or_edit(update.effective_user.id, "Bot has been disabled and banned.", to_edit=util.mid_from_update(update)) Statistic.of(update, 'disable', username, Statistic.IMPORTANT)
def apply_all_changes(bot, update, chat_data, to_edit): user = User.from_update(update) user_suggestions = Suggestion.select_all_of_user(user) for suggestion in user_suggestions: suggestion.apply() refreshed_bot = Bot.get(id=to_edit.id) edit_bot(bot, update, chat_data, refreshed_bot) Statistic.of(update, "apply", refreshed_bot.username)
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 check_suggestion_limit(bot, update, user): cid = update.effective_chat.id if Suggestion.over_limit(user): bot.formatter.send_failure( cid, "You have reached the limit of {} suggestions. Please wait for " "the Moderators to approve of some of them.".format( settings.SUGGESTION_LIMIT)) Statistic.of(update, 'hit the suggestion limit') return True return False
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 set_notifications(bot, update, value: bool): cid = update.effective_chat.id try: notifications = Notifications.get(Notifications.chat_id == cid) except Notifications.DoesNotExist: notifications = Notifications(chat_id=cid) notifications.enabled = value notifications.save() Statistic.of(update, ('enabled' if value else 'disabled') + ' notifications for their group {}'.format(cid)) msg = util.success("Nice! Notifications enabled." ) if value else "Ok, notifications disabled." msg += '\nYou can always adjust this setting with the /subscribe command.' bot.formatter.send_or_edit(cid, msg, to_edit=util.mid_from_update(update)) return ConversationHandler.END
def ban_user(_bot, update, user: User, ban_state: bool): if user.banned and ban_state is True: update.message.reply_text( mdformat.none_action("User {} is already banned.".format(user)), parse_mode="markdown", ) raise DispatcherHandlerStop if not user.banned and ban_state is False: update.message.reply_text( mdformat.none_action("User {} is not banned.".format(user)), parse_mode="markdown", ) raise DispatcherHandlerStop user.banned = ban_state if ban_state is True: with db.atomic(): user_submissions = Bot.select().where( (Bot.approved == False) & (Bot.submitted_by == user) # TODO: does this need to include `Bot.deleted == True`? ) for b in user_submissions: b.delete_instance() users_suggestions = Suggestion.select().where( (Suggestion.executed == False) & (Suggestion.user == user)) for s in users_suggestions: s.delete_instance() update.message.reply_text( mdformat.success( "User {} banned, all bot submissions and suggestions removed.". format(user)), parse_mode="markdown", ) Statistic.of(update, "ban", user.markdown_short) else: update.message.reply_text(mdformat.success( "User {} unbanned.".format(user)), parse_mode="markdown") Statistic.of(update, "unban", user.markdown_short) user.save()
def send_botlist(bot, update, resend=False, silent=False): log.info("Re-sending BotList..." if resend else "Updating BotList...") channel = helpers.get_channel() revision = Revision.get_instance() revision.nr += 1 revision.save() all_categories = Category.select_all() botlist = BotList(bot, update, channel, resend, silent) if resend: botlist.delete_full_botlist() botlist.update_intro() botlist.update_categories(all_categories) botlist.update_new_bots_list() botlist.update_category_list() botlist.send_footer() botlist.finish() channel.save() Statistic.of(update, 'send', 'botlist (resend: {})'.format(str(resend)), Statistic.IMPORTANT)
def share_with_moderator(bot, update, bot_in_question, moderator): user = User.from_update(update) buttons = [[ InlineKeyboardButton( "Yea, let me take this one!", callback_data=util.callback_for_action( CallbackActions.APPROVE_REJECT_BOTS, {"id": bot_in_question.id}), ) ]] reply_markup = InlineKeyboardMarkup(buttons) text = "{} thinks that you have the means to inspect this bot submission:\n▶️ {}".format( user.markdown_short, bot_in_question) try: util.send_md_message( bot, moderator.chat_id, text, reply_markup=reply_markup, disable_web_page_preview=True, ) answer_text = mdformat.success( "I will ask {} to have a look at this submission.".format( moderator.plaintext)) except Exception as e: answer_text = mdformat.failure( f"Could not contact {moderator.plaintext}: {e}") if update.callback_query: update.callback_query.answer(text=answer_text) Statistic.of( update, "share", "submission {} with {}".format(bot_in_question.username, moderator.plaintext), )
def send_bot_details(bot, update, chat_data, item=None): is_group = util.is_group_message(update) cid = update.effective_chat.id user = User.from_update(update) if item is None: if is_group: return try: text = update.message.text bot_in_text = re.findall(settings.REGEX_BOT_IN_TEXT, text)[0] item = Bot.by_username(bot_in_text, include_disabled=True) except Bot.DoesNotExist: update.message.reply_text( util.failure( "This bot is not in the @BotList. If you think this is a " "mistake, see the /examples for /contributing.")) return header_buttons = [] buttons = [] if item.disabled: txt = '{} {} and thus removed from the @BotList.'.format( item, Bot.DisabledReason.to_str(item.disabled_reason)) elif item.approved: # bot is already in the botlist => show information txt = item.detail_text if item.description is None and not Keyword.select().where( Keyword.entity == item).exists(): txt += ' is in the @BotList.' btn = InlineCallbackButton(captions.BACK_TO_CATEGORY, CallbackActions.SELECT_BOT_FROM_CATEGORY, {'id': item.category.id}) header_buttons.insert(0, btn) header_buttons.append( InlineKeyboardButton(captions.SHARE, switch_inline_query=item.username)) # if cid in settings.MODERATORS: header_buttons.append( InlineKeyboardButton("📝 Edit", callback_data=util.callback_for_action( CallbackActions.EDIT_BOT, {'id': item.id}))) # Add favorite button favorite_found = Favorite.search_by_bot(user, item) if favorite_found: buttons.append( InlineKeyboardButton(captions.REMOVE_FAVORITE_VERBOSE, callback_data=util.callback_for_action( CallbackActions.REMOVE_FAVORITE, { 'id': favorite_found.id, 'details': True }))) else: buttons.append( InlineKeyboardButton(captions.ADD_TO_FAVORITES, callback_data=util.callback_for_action( CallbackActions.ADD_TO_FAVORITES, { 'id': item.id, 'details': True }))) else: txt = '{} is currently pending to be accepted for the @BotList.'.format( item) if cid in settings.MODERATORS: header_buttons.append( InlineKeyboardButton("🛃 Accept / Reject", callback_data=util.callback_for_action( CallbackActions.APPROVE_REJECT_BOTS, {'id': item.id}))) if buttons or header_buttons: reply_markup = InlineKeyboardMarkup( util.build_menu(buttons, n_cols=3, header_buttons=header_buttons)) else: reply_markup = None reply_markup, callback = botlistchat.append_delete_button( update, chat_data, reply_markup) # Should we ever decide to show thumbnails *shrug* # if os.path.exists(item.thumbnail_file): # preview = True # photo = '[\xad]({})'.format('{}/thumbnail/{}.jpeg'.format( # settings.API_URL, # item.username[1:] # )) # log.info(photo) # txt = photo + txt # else: # preview = False msg = bot.formatter.send_or_edit(cid, txt, to_edit=util.mid_from_update(update), reply_markup=reply_markup) callback(msg) Statistic.of(update, 'view-details', item.username, Statistic.ANALYSIS) return CallbackStates.SHOWING_BOT_DETAILS
def chosen_result(bot, update, chat_data): if update.chosen_inline_result.inline_message_id: chat_data[ 'sent_inlinequery'] = update.chosen_inline_result.inline_message_id Statistic.of(update, 'chosen-inlinequery-result', level=Statistic.ANALYSIS)
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)