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
Beispiel #4
0
    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)
Beispiel #6
0
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)
Beispiel #7
0
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
Beispiel #11
0
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)
Beispiel #13
0
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),
    )
Beispiel #14
0
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
Beispiel #15
0
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)
Beispiel #16
0
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)