Exemple #1
0
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)
    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)
Exemple #2
0
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
Exemple #3
0
def share_with_moderator(bot, update, bot_in_question, moderator):
    user = User.from_update(update)
    answer_text = mdformat.success(
        "I will ask {} to have a look at this submission.".format(
            moderator.plaintext))
    if update.callback_query:
        update.callback_query.answer(text=answer_text)

    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)
    util.send_md_message(bot,
                         moderator.chat_id,
                         text,
                         reply_markup=reply_markup,
                         disable_web_page_preview=True)
    Statistic.of(
        update, 'share',
        'submission {} with {}'.format(bot_in_question.username,
                                       moderator.plaintext))
Exemple #4
0
    def add_or_update(user, action, subject, value):
        from model 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
Exemple #5
0
    def add_or_update(user, action, subject, value):
        from model import Statistic
        # value may be None
        already_exists = Suggestion.get_pending(action, subject, user)
        if already_exists:
            # Does the new suggestion reset the value?
            if 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: ',
                         already_exists._md_plaintext())

            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
Exemple #6
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
Exemple #7
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')
        return
    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')
        return
    user.banned = ban_state
    if ban_state is True:
        with db.atomic():
            users_bots = Bot.select().where((Bot.approved == False)
                                            & (Bot.submitted_by == user))
            for b in users_bots:
                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()
Exemple #8
0
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)
Exemple #9
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)
Exemple #10
0
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
Exemple #11
0
def send_category(bot, update, chat_data, category=None):
    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_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)
Exemple #12
0
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)
Exemple #13
0
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
Exemple #14
0
def add_keyword(bot, update, chat_data):
    kw = update.message.text
    bot_to_edit = chat_data.get('edit_bot')
    kw = helpers.format_keyword(kw)
    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
    kw_obj = Keyword(name=kw, entity=bot_to_edit)
    kw_obj.save()
    set_keywords(bot, update, chat_data, bot_to_edit)
    Statistic.of(update, 'added keyword to'.format(kw), bot_to_edit.username)
Exemple #15
0
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)
Exemple #16
0
def send_activity_logs(bot, update, args=None, level=Statistic.INFO):
    num = 200
    if args:
        try:
            num = int(args[0])
            num = min(num, 500)
        except:
            pass
    uid = update.effective_user.id
    recent_statistic = Statistic.select().order_by(
        Statistic.date.desc()).limit(num)
    recent_statistic = list(reversed(recent_statistic))

    step_size = 30
    for i in range(0, len(recent_statistic), step_size):
        items = recent_statistic[i:i + step_size]
        text = '\n'.join(x.md_str() for x in items)

        bot.formatter.send_message(uid, text)
Exemple #17
0
def send_statistic(bot, update):
    interesting_actions = [
        'explore',
        'menu',
        'command',
        'request',
        'made changes to their suggestion:',
        'issued deletion of conversation in BotListChat',
    ]
    stats = Statistic.select(
        Statistic,
        fn.COUNT(Statistic.entity).alias('count')).where(
            Statistic.action << interesting_actions).group_by(
                Statistic.action, Statistic.entity)
    maxlen = max(len(str(x.count)) for x in stats)
    text = '\n'.join("`{}▪️` {} {}".format(
        str(s.count).ljust(maxlen), s.action.title(), s.entity) for s in stats)
    bot.formatter.send_message(update.effective_chat.id,
                               text,
                               parse_mode='markdown')
Exemple #18
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)
    first_row = list()

    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)

        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

    if 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})
        first_row.insert(0, btn)
        first_row.append(
            InlineKeyboardButton(captions.SHARE,
                                 switch_inline_query=item.username))

        # if cid in settings.MODERATORS:
        first_row.append(
            InlineKeyboardButton("📝 Edit",
                                 callback_data=util.callback_for_action(
                                     CallbackActions.EDIT_BOT,
                                     {'id': item.id})))
    else:
        txt = '{} is currently pending to be accepted for the @BotList.'.format(
            item)
        if cid in settings.MODERATORS:
            first_row.append(
                InlineKeyboardButton("🛃 Accept / Reject",
                                     callback_data=util.callback_for_action(
                                         CallbackActions.APPROVE_REJECT_BOTS,
                                         {'id': item.id})))

    if is_group:
        reply_markup = InlineKeyboardMarkup([])
    else:
        buttons = [first_row]
        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
                                         }))
            ])
        reply_markup = InlineKeyboardMarkup(buttons)
    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
Exemple #19
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)
    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)
Exemple #20
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)
Exemple #21
0
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)