Exemple #1
0
def show_vote_menu(session, context, poll):
    """Show the vote keyboard in the management interface."""
    if poll.is_priority():
        poll.init_votes(session, context.user)
        session.commit()

    text, keyboard = get_poll_text_and_vote_keyboard(session, poll, user=context.user, show_back=True)
    # Set the expected_input to votes, since the user might want to vote multiple times
    context.user.expected_input = ExpectedInput.votes.name
    context.query.message.edit_text(
        text,
        parse_mode='markdown',
        reply_markup=keyboard,
        disable_web_page_preview=True,
    )
Exemple #2
0
def start(bot, update, session, user):
    """Send a start text."""
    # Truncate the /start command
    text = update.message.text[6:].strip()
    user.started = True

    poll = None
    action = None
    try:
        poll_uuid = UUID(text.split("-")[0])
        action = StartAction(int(text.split("-")[1]))

        poll = session.query(Poll).filter(Poll.uuid == poll_uuid).one()
    except:
        text = ""

    # We got an empty text, just send the start message
    if text == "":
        update.message.chat.send_message(
            i18n.t("misc.start", locale=user.locale),
            parse_mode="markdown",
            reply_markup=get_main_keyboard(user),
            disable_web_page_preview=True,
        )

        return

    if poll is None:
        return "This poll no longer exists."

    if action == StartAction.new_option and poll.allow_new_options:
        # Update the expected input and set the current poll
        user.expected_input = ExpectedInput.new_user_option.name
        user.current_poll = poll
        session.commit()

        update.message.chat.send_message(
            i18n.t("creation.option.first", locale=poll.locale),
            parse_mode="markdown",
            reply_markup=get_external_add_option_keyboard(poll),
        )
    elif action == StartAction.show_results:
        # Get all lines of the poll
        lines = compile_poll_text(session, poll)
        # Now split the text into chunks of max 4000 characters
        chunks = split_text(lines)

        for chunk in chunks:
            message = "\n".join(chunk)
            try:
                update.message.chat.send_message(
                    message,
                    parse_mode="markdown",
                    disable_web_page_preview=True,
                )
            # Retry for Timeout error (happens quite often when sending large messages)
            except TimeoutError:
                time.sleep(2)
                update.message.chat.send_message(
                    message,
                    parse_mode="markdown",
                    disable_web_page_preview=True,
                )
            time.sleep(1)

        update.message.chat.send_message(
            i18n.t("misc.start_after_results", locale=poll.locale),
            parse_mode="markdown",
            reply_markup=get_main_keyboard(user),
        )
        increase_stat(session, "show_results")

    elif action == StartAction.share_poll and poll.allow_sharing:
        update.message.chat.send_message(
            i18n.t("external.share_poll", locale=poll.locale),
            reply_markup=get_external_share_keyboard(poll),
        )
        increase_stat(session, "externally_shared")

    elif action == StartAction.vote:
        if not config["telegram"][
                "allow_private_vote"] and not poll.is_priority():
            return

        if poll.is_priority():
            init_votes(session, poll, user)
            session.commit()

        text, keyboard = get_poll_text_and_vote_keyboard(
            session,
            poll,
            user=user,
        )

        sent_message = update.message.chat.send_message(
            text,
            reply_markup=keyboard,
            parse_mode="markdown",
            disable_web_page_preview=True,
        )

        reference = Reference(
            poll,
            ReferenceType.private_vote.name,
            user=user,
            message_id=sent_message.message_id,
        )
        session.add(reference)
        session.commit()
def update_reference(session, bot, poll, reference, show_warning=False):
    try:
        # Admin poll management interface
        if reference.type == ReferenceType.admin.name and not poll.in_settings:
            text, keyboard = get_poll_text_and_vote_keyboard(
                session,
                poll,
                user=poll.user,
                show_warning=show_warning,
                show_back=True)

            if poll.user.expected_input != ExpectedInput.votes.name:
                keyboard = get_management_keyboard(poll)

            bot.edit_message_text(
                text,
                chat_id=reference.user.id,
                message_id=reference.message_id,
                reply_markup=keyboard,
                parse_mode="markdown",
                disable_web_page_preview=True,
            )

        # User that votes in private chat (priority vote)
        elif reference.type == ReferenceType.private_vote.name:
            text, keyboard = get_poll_text_and_vote_keyboard(
                session,
                poll,
                user=reference.user,
                show_warning=show_warning,
            )

            bot.edit_message_text(
                text,
                chat_id=reference.user.id,
                message_id=reference.message_id,
                reply_markup=keyboard,
                parse_mode="markdown",
                disable_web_page_preview=True,
            )

        # Edit message created via inline query
        elif reference.type == ReferenceType.inline.name:
            # Create text and keyboard
            text, keyboard = get_poll_text_and_vote_keyboard(
                session, poll, show_warning=show_warning)

            bot.edit_message_text(
                text,
                inline_message_id=reference.bot_inline_message_id,
                reply_markup=keyboard,
                parse_mode="markdown",
                disable_web_page_preview=True,
            )

    except BadRequest as e:
        if (e.message.startswith("Message_id_invalid")
                or e.message.startswith("Message can't be edited")
                or e.message.startswith("Message to edit not found")
                or e.message.startswith("Chat not found")
                or e.message.startswith("Can't access the chat")):
            session.delete(reference)
            session.commit()
        elif e.message.startswith("Message is not modified"):
            pass
        else:
            raise

    except Unauthorized:
        session.delete(reference)
        session.commit()
    except TimedOut:
        # Ignore timeouts during updates for now
        pass
def search(bot, update, session, user):
    """Handle inline queries for sticker search."""
    query = update.inline_query.query.strip()

    # Also search for closed polls if the `closed_polls` keyword is found
    closed = False
    if 'closed_polls' in query:
        closed = True
        query = query.replace('closed_polls', '').strip()

    offset = update.inline_query.offset

    if offset == '':
        offset = 0
    else:
        offset = int(offset)

    if query == '':
        # Just display all polls
        polls = session.query(Poll) \
            .filter(Poll.user == user) \
            .filter(Poll.closed.is_(closed)) \
            .filter(Poll.created.is_(True)) \
            .order_by(Poll.created_at.desc()) \
            .limit(10) \
            .offset(offset) \
            .all()

    else:
        # Find polls with search parameter in name or description
        polls = session.query(Poll) \
            .filter(Poll.user == user) \
            .filter(Poll.closed.is_(closed)) \
            .filter(Poll.created.is_(True)) \
            .filter(or_(
                Poll.name.ilike(f'%{query}%'),
                Poll.description.ilike(f'%{query}%'),
            )) \
            .order_by(Poll.created_at.desc()) \
            .limit(10) \
            .offset(offset) \
            .all()

    # Try to find polls that are shared by external people via uuid
    if len(polls) == 0 and len(query) == 36:
        try:
            poll_uuid = uuid.UUID(query)
            polls = session.query(Poll) \
                .filter(Poll.uuid == poll_uuid) \
                .all()
        except ValueError:
            pass

    if len(polls) == 0:
        update.inline_query.answer(
            [],
            cache_time=0,
            is_personal=True,
            switch_pm_text=i18n.t('inline_query.create_first',
                                  locale=user.locale),
            switch_pm_parameter='inline',
        )
    else:
        results = []
        for poll in polls:
            text, keyboard = get_poll_text_and_vote_keyboard(session,
                                                             poll,
                                                             user=user,
                                                             inline_query=True)
            keyboard = InlineKeyboardMarkup([[
                InlineKeyboardButton('Please ignore this',
                                     callback_data='100:0:0')
            ]])

            content = InputTextMessageContent(
                text,
                parse_mode='markdown',
                disable_web_page_preview=True,
            )
            description = poll.description[:
                                           100] if poll.description is not None else None
            results.append(
                InlineQueryResultArticle(
                    poll.id,
                    poll.name,
                    description=description,
                    input_message_content=content,
                    reply_markup=keyboard,
                ))

        update.inline_query.answer(
            results,
            cache_time=0,
            is_personal=True,
            switch_pm_text=i18n.t('inline_query.create_poll',
                                  locale=user.locale),
            switch_pm_parameter='inline',
            next_offset=str(offset + 10),
        )
Exemple #5
0
def update_reference(
    session, bot, poll, reference, show_warning=False, first_try=False
):
    try:
        # Admin poll management interface
        if reference.type == ReferenceType.admin.name and not poll.in_settings:
            text, keyboard = get_poll_text_and_vote_keyboard(
                session, poll, user=poll.user, show_warning=show_warning, show_back=True
            )

            if poll.user.expected_input != ExpectedInput.votes.name:
                keyboard = get_management_keyboard(poll)

            bot.edit_message_text(
                text,
                chat_id=reference.user.id,
                message_id=reference.message_id,
                reply_markup=keyboard,
                parse_mode="markdown",
                disable_web_page_preview=True,
            )

        # User that votes in private chat (priority vote)
        elif reference.type == ReferenceType.private_vote.name:
            text, keyboard = get_poll_text_and_vote_keyboard(
                session,
                poll,
                user=reference.user,
                show_warning=show_warning,
            )

            bot.edit_message_text(
                text,
                chat_id=reference.user.id,
                message_id=reference.message_id,
                reply_markup=keyboard,
                parse_mode="markdown",
                disable_web_page_preview=True,
            )

        # Edit message created via inline query
        elif reference.type == ReferenceType.inline.name:
            # Create text and keyboard
            text, keyboard = get_poll_text_and_vote_keyboard(
                session, poll, show_warning=show_warning
            )

            bot.edit_message_text(
                text,
                inline_message_id=reference.bot_inline_message_id,
                reply_markup=keyboard,
                parse_mode="markdown",
                disable_web_page_preview=True,
            )

    except BadRequest as e:
        if (
            e.message.startswith("Message_id_invalid")
            or e.message.startswith("Message can't be edited")
            or e.message.startswith("Message to edit not found")
            or e.message.startswith("Chat not found")
            or e.message.startswith("Can't access the chat")
        ):
            # This is just a hunch
            # It feels like we're too fast and the message isn't synced between Telegram's servers yet.
            # If this happens, allow the first try to fail and schedule an update
            # If it happens again, we'll fail on the second try
            if first_try:
                update = Update(poll, datetime.now() + timedelta(seconds=2))
                session.add(update)
                sentry.capture_exception(
                    extra={
                        "context": "update_reference",
                    },
                )

                return

            session.delete(reference)
            session.commit()
        elif e.message.startswith("Message is not modified"):
            pass
        else:
            raise e

    except Unauthorized:
        session.delete(reference)
        session.commit()
    except TimedOut:
        # Ignore timeouts during updates for now
        pass
Exemple #6
0
def send_updates(session, bot, poll, show_warning=False):
    """Actually update all messages."""
    for reference in poll.references:
        try:
            # Admin poll management interface
            if reference.admin_user is not None and not poll.in_settings:
                text, keyboard = get_poll_text_and_vote_keyboard(
                    session,
                    poll,
                    user=poll.user,
                    show_warning=show_warning,
                    show_back=True)

                if poll.user.expected_input != ExpectedInput.votes.name:
                    keyboard = get_management_keyboard(poll)

                try:
                    bot.edit_message_text(
                        text,
                        chat_id=reference.admin_user.id,
                        message_id=reference.admin_message_id,
                        reply_markup=keyboard,
                        parse_mode='markdown',
                        disable_web_page_preview=True,
                    )
                except Unauthorized as e:
                    if e.message == 'Forbidden: user is deactivated':
                        session.delete(reference)

            elif reference.vote_user is not None:
                text, keyboard = get_poll_text_and_vote_keyboard(
                    session,
                    poll,
                    user=reference.vote_user,
                    show_warning=show_warning,
                )

                try:
                    bot.edit_message_text(
                        text,
                        chat_id=reference.vote_user.id,
                        message_id=reference.vote_message_id,
                        reply_markup=keyboard,
                        parse_mode='markdown',
                        disable_web_page_preview=True,
                    )
                except Unauthorized as e:
                    if e.message == 'Forbidden: user is deactivated':
                        session.delete(reference)

            # Edit message via inline_message_id
            elif reference.inline_message_id is not None:
                # Create text and keyboard
                text, keyboard = get_poll_text_and_vote_keyboard(
                    session, poll, show_warning=show_warning)

                bot.edit_message_text(
                    text,
                    inline_message_id=reference.inline_message_id,
                    reply_markup=keyboard,
                    parse_mode='markdown',
                    disable_web_page_preview=True,
                )
        except BadRequest as e:
            if e.message.startswith('Message_id_invalid') or \
                   e.message.startswith("Message can't be edited") or \
                   e.message.startswith("Message to edit not found") or \
                   e.message.startswith("Chat not found"):
                session.delete(reference)
                session.commit()
            elif e.message.startswith('Message is not modified'):
                pass
            else:
                raise

        except Unauthorized as e:
            if e.message.startswith("Forbidden: MESSAGE_AUTHOR_REQUIRED"):
                session.delete(reference)
                session.commit()
            else:
                raise
Exemple #7
0
def start(bot, update, session, user):
    """Send a start text."""
    # Truncate the /start command
    text = ""
    if update.message is not None:
        text = update.message.text[6:].strip()
    user.started = True

    try:
        poll_uuid = UUID(text.split('-')[0])
        action = StartAction(int(text.split('-')[1]))

        poll = session.query(Poll).filter(Poll.uuid == poll_uuid).one()
    except:
        text = ''

    # We got an empty text, just send the start message
    if text == '':
        update.message.chat.send_message(
            i18n.t('misc.start', locale=user.locale),
            parse_mode='markdown',
            reply_markup=get_main_keyboard(user),
            disable_web_page_preview=True,
        )

        return

    if poll is None:
        return 'This poll no longer exists.'

    if action == StartAction.new_option:
        # Update the expected input and set the current poll
        user.expected_input = ExpectedInput.new_user_option.name
        user.current_poll = poll
        session.commit()

        update.message.chat.send_message(
            i18n.t('creation.option.first', locale=poll.locale),
            parse_mode='markdown',
            reply_markup=get_external_add_option_keyboard(poll))
    elif action == StartAction.show_results:
        # Get all lines of the poll
        lines = compile_poll_text(session, poll)
        # Now split the text into chunks of max 4000 characters
        chunks = split_text(lines)

        for chunk in chunks:
            message = '\n'.join(chunk)
            try:
                update.message.chat.send_message(
                    message,
                    parse_mode='markdown',
                    disable_web_page_preview=True,
                )
            # Retry for Timeout error (happens quite often when sending large messages)
            except TimeoutError:
                time.sleep(2)
                update.message.chat.send_message(
                    message,
                    parse_mode='markdown',
                    disable_web_page_preview=True,
                )
            time.sleep(1)

        update.message.chat.send_message(
            i18n.t('misc.start_after_results', locale=poll.locale),
            parse_mode='markdown',
            reply_markup=get_main_keyboard(user),
        )
        increase_stat(session, 'show_results')

    elif action == StartAction.share_poll:
        update.message.chat.send_message(
            i18n.t('external.share_poll', locale=poll.locale),
            reply_markup=get_external_share_keyboard(poll))
        increase_stat(session, 'externally_shared')

    elif action == StartAction.vote:
        if poll.is_priority():
            poll.init_votes(session, user)
            session.commit()

        text, keyboard = get_poll_text_and_vote_keyboard(
            session,
            poll,
            user=user,
        )

        sent_message = update.message.chat.send_message(
            text,
            reply_markup=keyboard,
            parse_mode='markdown',
            disable_web_page_preview=True,
        )

        reference = Reference(
            poll,
            vote_user=user,
            vote_message_id=sent_message.message_id,
        )
        session.add(reference)

        session.commit()