Beispiel #1
0
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,
    )
Beispiel #2
0
def notify_submittant_rejected(bot, admin_user, notify_submittant, reason, to_reject):
    notification_successful = False
    msg = "{} rejected by {}.".format(to_reject.username, admin_user)
    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)
                    ),
                )
            msg += "\nUser {} was notified.".format(str(to_reject.submitted_by))
            notification_successful = True
        except TelegramError:
            msg += "\nUser {} could NOT be contacted/notified in private.".format(
                str(to_reject.submitted_by)
            )
            notification_successful = False

    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:
        try:
            text += " " + mdformat.failure(
                "Could not contact {}.".format(to_reject.submitted_by.plaintext)
            )
        except:
            pass
    else:
        text += " No notification sent."
    return msg
Beispiel #3
0
def add_favorite_handler(bot, update, args=None):
    uid = util.uid_from_update(update)
    from botlistbot.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
Beispiel #4
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)
Beispiel #5
0
def send_bot_details(
    bot, update, chat_data, item=None, header_msg: Optional[str] = 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 = f"{header_msg}\n\n{item.detail_text}" if header_msg else item.detail_text

        if (
            item.description is None
            and not Keyword.select().where(Keyword.entity == item).exists()
        ):
            txt += " is in the @BotList, but has no description or keywords yet."
        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_restricted_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 #6
0
 def notify_admin_err(self, txt):
     self.bot.formatter.send_or_edit(self.chat_id,
                                     util.failure(txt),
                                     to_edit=self.message_id,
                                     disable_web_page_preview=True,
                                     disable_notification=False)
Beispiel #7
0
def notify_bot_offline(bot, update, args=None):
    tg_user = update.message.from_user
    user = User.from_telegram_object(tg_user)
    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() == "/offline@botlistbot"
        )
        if command_no_args:
            update.message.reply_text(
                util.action_hint(
                    "Please use this command with an argument. For example:\n/offline @mybot"
                ),
                reply_to_message_id=reply_to,
            )
            return

    # `#offline` is already checked by handler
    try:
        username = re.match(settings.REGEX_BOT_IN_TEXT, text).groups()[0]
        if username == "@" + settings.SELF_BOT_NAME:
            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,
            )
        else:
            log.info("Ignoring {}".format(text))
            # no bot username, ignore update
            pass
        return

    try:
        offline_bot = Bot.get(
            fn.lower(Bot.username) ** username.lower(), Bot.approved == True
        )
        try:
            Suggestion.get(action="offline", subject=offline_bot)
        except Suggestion.DoesNotExist:
            suggestion = Suggestion(
                user=user,
                action="offline",
                date=datetime.date.today(),
                subject=offline_bot,
            )
            suggestion.save()
        update.message.reply_text(
            util.success(
                "Thank you! We will review your suggestion and set the bot offline."
            ),
            reply_to_message_id=reply_to,
        )
    except Bot.DoesNotExist:
        update.message.reply_text(
            util.action_hint("The bot you sent me is not in the @BotList."),
            reply_to_message_id=reply_to,
        )
    return ConversationHandler.END
Beispiel #8
0
def new_bot_submission(bot, update, chat_data, args=None, bot_checker=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, include_disabled=True)
        if new_bot.disabled:
            update.message.reply_text(
                util.failure(
                    "{} is banned from the @BotList.".format(new_bot.username)
                ),
                reply_to_message_id=reply_to,
            )
        elif 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 botlistbot.components.explore import send_bot_details

        send_bot_details(bot, update, chat_data, new_bot)
    else:
        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,
        )

        # Ask the user to fill in the bot details
        util.send_md_message(
            bot,
            update.effective_user.id,
            "Congratulations, you just submitted a bot to the @BotList. Please help us fill in the details below:",
        )
        edit_bot(bot, update, chat_data, to_edit=new_bot)

    try:
        check_submission(bot, bot_checker, new_bot)
    except Exception as e:
        log.exception(e)

    return ConversationHandler.END
Beispiel #9
0
def search_query(bot, update: Update, chat_data, query, send_errors=True):
    cid: int = update.effective_chat.id
    user: User = User.from_update(update)
    is_admin: bool = cid in settings.MODERATORS
    replied_to_message_id: Optional[int] = util.original_reply_id(update)
    is_suggestion_by_other: bool = (update.effective_chat
                                    and update.effective_chat.id
                                    == settings.BOTLISTCHAT_ID)

    results = search.search_bots(query)

    reply_markup = (ReplyKeyboardMarkup(basic.main_menu_buttons(is_admin),
                                        resize_keyboard=True)
                    if util.is_private_message(update) else None)
    if results:
        if len(results) == 1:
            update.effective_message.delete()
            if is_suggestion_by_other:
                header = f"{user.markdown_short} found the following bot for you:"
            else:
                header = "I found the following bot for you:"
            return send_bot_details(bot,
                                    update,
                                    chat_data,
                                    results[0],
                                    header_msg=header)
        too_many_results = len(results) > settings.MAX_SEARCH_RESULTS

        bots_list = ""
        if cid in settings.MODERATORS:  # private chat with moderator
            # append edit buttons
            bots_list += "\n".join([
                "{} — /edit{} 🛃".format(b, b.id)
                for b in list(results)[:100]
            ])
        else:
            bots_list += "\n".join(
                [str(b) for b in list(results)[:settings.MAX_SEARCH_RESULTS]])
        bots_list += "\n…" if too_many_results else ""
        bots_list = messages.SEARCH_RESULTS.format(
            bots=bots_list,
            num_results=len(results),
            plural="s" if len(results) > 1 else "",
            query=query,
        )

        if util.is_group_message(
                update) and not update.message.reply_to_message:
            try:
                bot.formatter.send_message(
                    update.effective_user.id,
                    bots_list,
                    reply_markup=reply_markup,
                    disable_web_page_preview=True,
                )
                reply_markup, callback = botlistchat.append_restricted_delete_button(
                    update, chat_data, InlineKeyboardMarkup([[]]))
                msg = bot.formatter.send_message(
                    update.effective_chat.id,
                    f"Hey {user.plaintext}, let's not annoy the others. I sent you the search results "
                    f"[in private chat](https://t.me/{settings.SELF_BOT_NAME}).",
                    disable_web_page_preview=True,
                    reply_markup=reply_markup,
                )
                callback(msg)
                update.effective_message.delete()
            except TelegramError:
                hint_msg, hint_reply_markup, _ = get_hint_data("#private")
                bot.formatter.send_message(
                    update.effective_chat.id,
                    hint_msg,
                    reply_markup=hint_reply_markup,
                    reply_to_message_id=update.effective_message.id,
                    disable_web_page_preview=True,
                )
            return ConversationHandler.END

        if is_suggestion_by_other and replied_to_message_id:
            bots_list = f"{user.markdown_short} suggests to search and {bots_list}"

        bot.formatter.send_message(
            update.effective_chat.id,
            bots_list,
            parse_mode=ParseMode.MARKDOWN,
            reply_markup=reply_markup,
            disable_web_page_preview=True,
            reply_to_message_id=replied_to_message_id,
        )
        update.effective_message.delete()
    else:
        if send_errors:
            callback = None
            if util.is_group_message(update):
                reply_markup, callback = botlistchat.append_restricted_delete_button(
                    update, chat_data, InlineKeyboardMarkup([[]]))
            msg = update.message.reply_text(
                util.failure("Sorry, I couldn't find anything related "
                             "to *{}* in the @BotList. /search".format(
                                 util.escape_markdown(query))),
                parse_mode=ParseMode.MARKDOWN,
                reply_markup=reply_markup,
            )
            if callback:
                callback(msg)

    return ConversationHandler.END