Ejemplo n.º 1
0
def toggle_summerization(session, context, poll):
    """Toggle summarization of votes of a poll."""
    poll.summarize = not poll.summarize

    session.commit()
    update_poll_messages(session, context.bot, poll)
    send_styling_message(session, context)
Ejemplo n.º 2
0
def delete_user(session, context):
    """Delete everything of a user and ban them forever."""
    user = context.user

    for poll in context.user.polls:
        if poll.delete is None:
            poll.delete = PollDeletionMode.DB_ONLY.name
    session.commit()

    polls_for_update = []
    # Delete all votes, but only update non-closed polls
    for vote in user.votes:
        if vote.poll not in polls_for_update and not vote.poll.closed:
            polls_for_update.append(vote.poll)
        session.delete(vote)
    session.flush()

    for poll in polls_for_update:
        update_poll_messages(session, context.bot, poll)
    session.flush()

    user.delete()
    session.commit()

    context.query.message.chat.send_message(
        i18n.t("settings.user.deleted", locale=user.locale), )
Ejemplo n.º 3
0
def toggle_compact_buttons(session, context, poll):
    """Toggle the doodle poll button style."""
    poll.compact_buttons = not poll.compact_buttons

    session.commit()
    update_poll_messages(session, context.bot, poll)
    send_styling_message(session, context)
Ejemplo n.º 4
0
def delete_user(session, context):
    """Delete everything of a user and ban them forever."""
    user = context.user

    for poll in user.polls:
        remove_poll_messages(session, context.bot, poll)
        session.delete(poll)
        session.commit()

    polls_for_update = []
    for vote in user.votes:
        if vote.poll not in polls_for_update:
            polls_for_update.append(vote.poll)
        if not vote.poll.closed:
            session.delete(vote)
        else:
            vote.user = None
    session.commit()

    for poll in polls_for_update:
        update_poll_messages(session, context.bot, poll)
    session.commit()

    user.delete()
    session.commit()

    context.query.message.chat.send_message(
        i18n.t("settings.user.deleted", locale=user.locale), )
Ejemplo n.º 5
0
def update_all(session, context):
    """Update all polls."""
    chat = context.query.message.chat

    updated = 0
    poll_count = session.query(Poll).filter(Poll.created.is_(True)).count()
    chat.send_message(f"Updating {poll_count} polls")
    print(f"Updating {poll_count} polls")

    while updated < poll_count:
        polls = (
            session.query(Poll)
            .filter(Poll.created.is_(True))
            .order_by(Poll.id.desc())
            .offset(updated)
            .limit(100)
            .all()
        )

        if updated % 500 == 0:
            chat.send_message(f"Updated {updated} polls")
        updated += len(polls)

        for poll in polls:
            update_poll_messages(session, context.bot, poll)
            time.sleep(0.2)

    return "Done"
Ejemplo n.º 6
0
def toggle_allow_sharing(session, context, poll):
    """Toggle the visibility of the percentage bar."""
    poll.allow_sharing = not poll.allow_sharing

    session.commit()
    update_poll_messages(session, context.bot, poll)
    send_settings_message(context)
Ejemplo n.º 7
0
def decrease_option_index(session, context, poll):
    """Decrease the index of a specific option."""
    option_id = context.action

    prev_option = None
    for option in poll.options:
        # Find the option we're looking for
        if option.id == option_id:
            if prev_option is None:
                return

            # Switch index with the previous option
            current_index = option.index
            target_index = prev_option.index

            # Change indices to allow changing the index
            # In combination with unique constraints
            # This also acts as a lock, which prevents other
            # requests to change order at the same time
            option.index = -2
            prev_option.index = -1
            session.commit()

            # Upate indices
            prev_option.index = current_index
            option.index = target_index
            session.commit()

            break

        prev_option = option

    update_poll_messages(session, context.bot, poll)
    send_option_order_message(session, context)
Ejemplo n.º 8
0
def increase_option_index(session, context, poll):
    """Increase the index of a specific option."""
    option_id = context.action

    target_option = None
    for option in poll.options:
        # Find the option we're looking for
        if option.id == option_id:
            target_option = option

            continue

        # Switch index with the next option
        if target_option is not None:
            target_index = option.index
            current_index = target_option.index

            # Change indices to allow changing the index
            # In combination with unique constraints
            # This also acts as a lock, which prevents other
            # requests to change order at the same time
            option.index = -1
            target_option.index = -2
            session.commit()

            # Upate indices
            target_option.index = target_index
            option.index = current_index
            session.commit()

            break

    update_poll_messages(session, context.bot, poll)
    send_option_order_message(session, context)
Ejemplo n.º 9
0
def handle_user_option_addition(bot, update, session, user, text, poll, chat):
    """Handle the addition of options from and arbitrary user."""
    if not poll.allow_new_options:
        user.current_poll = None
        user.expected_input = None
        chat.send_message(i18n.t("creation.not_allowed", locale=user.locale))

    added_options = add_options_multiline(session, poll, text)

    if len(added_options) > 0:
        # Reset user
        user.current_poll = None
        user.expected_input = None

        session.commit()

        # Send success message
        text = i18n.t("creation.option.multiple_added",
                      locale=user.locale) + "\n"
        for option in added_options:
            text += f"\n*{option}*"
        chat.send_message(text, parse_mode="markdown")

        # Update all polls
        update_poll_messages(session, bot, poll)
    else:
        chat.send_message(i18n.t("creation.option.no_new", locale=user.locale))
Ejemplo n.º 10
0
def owner_pick_date_option(session, context, poll, datepicker_context):
    """Owner adds or removes a date option."""
    picked_date = date.fromisoformat(context.data[2])

    # Check if we already have this date as option
    existing_option = poll.get_date_option(picked_date)
    # If that's the case, delete it
    if existing_option is not None:
        session.delete(existing_option)
        session.commit()
        message = i18n.t("callback.date_removed",
                         locale=poll.locale,
                         date=picked_date.isoformat())
    else:
        add_single_option(session, poll, context.data[2], True)
        session.commit()
        message = i18n.t("callback.date_picked",
                         locale=poll.locale,
                         date=picked_date.isoformat())

    context.query.answer(message)

    update_datepicker(context, poll, datepicker_context,
                      picked_date.replace(day=1))
    if poll.created:
        update_poll_messages(session, context.bot, poll)
Ejemplo n.º 11
0
def toggle_date_format(session, context, poll):
    """Switch between european and US date format."""
    poll.european_date_format = not poll.european_date_format
    poll.user.european_date_format = poll.european_date_format

    session.commit()
    update_poll_messages(session, context.bot, poll)
    send_styling_message(session, context)
Ejemplo n.º 12
0
def set_option_order(session, context, poll):
    """Set the order in which options are listed."""
    option_sorting = OptionSorting(context.action)
    poll.option_sorting = option_sorting.name

    session.commit()
    update_poll_messages(session, context.bot, poll)
    send_styling_message(session, context)
Ejemplo n.º 13
0
def close_poll(session, context, poll):
    """Close this poll."""
    poll.closed = True
    session.commit()
    update_poll_messages(session, context.bot, poll,
                         context.query.message.message_id, poll.user)

    return i18n.t("callback.closed", locale=poll.user.locale)
Ejemplo n.º 14
0
def set_user_order(session, context, poll):
    """Set the order in which user are listed."""
    user_sorting = UserSorting(context.action)
    poll.user_sorting = user_sorting.name

    session.commit()
    update_poll_messages(session, context.bot, poll)
    send_styling_message(session, context)
Ejemplo n.º 15
0
def reset_poll(session, context, poll):
    """Reset this poll."""
    for vote in poll.votes:
        session.delete(vote)
    session.commit()

    update_poll_messages(session, context.bot, poll,
                         context.query.message.message_id, poll.user)
    return i18n.t("callback.votes_removed", locale=poll.user.locale)
Ejemplo n.º 16
0
def make_anonymous(session, context, poll):
    """Change the anonymity settings of a poll."""
    poll.anonymous = True
    if not poll.show_percentage and not poll.show_option_votes:
        poll.show_percentage = True

    session.commit()
    update_poll_messages(session, context.bot, poll)
    send_settings_message(context)
Ejemplo n.º 17
0
def toggle_percentage(session, context, poll):
    """Toggle the visibility of the percentage bar."""
    if poll.anonymous and not poll.show_option_votes:
        context.query.message.chat.send_message(text=i18n.t(
            "settings.anonymity_warning", locale=context.user.locale), )
        return
    poll.show_percentage = not poll.show_percentage

    session.commit()
    update_poll_messages(session, context.bot, poll)
    send_styling_message(session, context)
Ejemplo n.º 18
0
def remove_option(session, context, poll):
    """Remove the option."""
    session.query(Option).filter(Option.id == context.action).delete()

    if poll.is_priority():
        reorder_votes_after_option_delete(session, poll)

    session.commit()

    keyboard = get_remove_option_keyboard(poll)
    context.query.message.edit_reply_markup(reply_markup=keyboard)

    update_poll_messages(session, context.bot, poll)
Ejemplo n.º 19
0
def reopen_poll(session, context, poll):
    """Reopen this poll."""
    if not poll.results_visible:
        return i18n.t("callback.cannot_reopen", locale=poll.user.locale)
    poll.closed = False

    # Remove the due date if it's in the past
    # If the due date is still valid, recalculate the next_notification date
    if poll.due_date is not None and poll.due_date <= datetime.now():
        poll.due_date = None
        poll.next_notification = None
    else:
        poll.set_due_date(poll.due_date)

    session.commit()
    update_poll_messages(session, context.bot, poll,
                         context.query.message.message_id, poll.user)
Ejemplo n.º 20
0
def pick_external_date(session, context, poll):
    """Add or remove a date option during creation."""
    picked_date = date.fromisoformat(context.data[2])

    # Check if we already have this date as option
    existing_option = poll.get_date_option(picked_date)
    # If that's the case, delete it
    if existing_option is not None:
        return i18n.t("callback.date_already_picked", locale=poll.locale)

    add_single_option(session, poll, context.data[2], True)
    message = i18n.t("callback.date_picked",
                     locale=poll.locale,
                     date=picked_date.isoformat())
    context.query.answer(message)

    update_datepicker(context, poll, DatepickerContext.external_add_option,
                      picked_date.replace(day=1))
    update_poll_messages(session, context.bot, poll)
Ejemplo n.º 21
0
def send_notifications(context, session):
    """Notify the users about the poll being closed soon."""
    polls = (
        session.query(Poll)
        .filter(
            or_(
                Poll.next_notification <= datetime.now(),
                Poll.due_date <= datetime.now(),
            )
        )
        .filter(Poll.closed.is_(False))
        .all()
    )

    for poll in polls:
        time_step = poll.due_date - poll.next_notification

        if time_step == timedelta(days=7):
            send_notifications_for_poll(context, session, poll, "notification.one_week")
            poll.next_notification = poll.due_date - timedelta(days=1)

        # One day remaining reminder
        elif time_step == timedelta(days=1):
            send_notifications_for_poll(context, session, poll, "notification.one_day")
            poll.next_notification = poll.due_date - timedelta(hours=6)

        # Six hours remaining reminder
        elif time_step == timedelta(hours=6):
            send_notifications_for_poll(
                context, session, poll, "notification.six_hours"
            )
            poll.next_notification = poll.due_date

        # Send the closed notification, remove all notifications and close the poll
        elif poll.due_date <= datetime.now():
            poll.closed = True
            update_poll_messages(session, context.bot, poll)

            send_notifications_for_poll(context, session, poll, "notification.closed")
            for notification in poll.notifications:
                session.delete(notification)
            session.commit()
Ejemplo n.º 22
0
def handle_new_option(bot, update, session, user, text, poll, chat):
    """Add a new option after poll creation."""
    added_options = add_options_multiline(session, poll, text)

    if len(added_options) > 0:
        text = i18n.t("creation.option.multiple_added",
                      locale=user.locale) + "\n"
        for option in added_options:
            text += f"\n*{option}*"
        chat.send_message(text, parse_mode="markdown")
    else:
        chat.send_message(i18n.t("creation.option.no_new", locale=user.locale))

    # Reset expected input
    user.current_poll = None
    user.expected_input = None

    text = get_settings_text(poll)
    keyboard = get_settings_keyboard(poll)
    message = chat.send_message(
        text,
        parse_mode="markdown",
        reply_markup=keyboard,
    )

    remove_old_references(session, bot, poll, user)

    # Create new reference
    reference = Reference(poll,
                          ReferenceType.admin.name,
                          user=user,
                          message_id=message.message_id)
    session.add(reference)
    session.commit()

    update_poll_messages(session, bot, poll, message.message_id, user)
Ejemplo n.º 23
0
def handle_vote(session, context, option):
    """Handle any clicks on vote buttons."""
    # Remove the poll, in case it got deleted, but we didn't manage to kill all references
    if option is None:
        if context.query.message is not None:
            context.query.message.edit_text(
                i18n.t("deleted.polls", locale=context.user.locale)
            )
        else:
            context.bot.edit_message_text(
                i18n.t("deleted.polls", locale=context.user.locale),
                inline_message_id=context.query.inline_message_id,
            )
        return

    poll = option.poll
    update_poll = False
    try:
        # Single vote
        if poll.poll_type == PollType.single_vote.name:
            update_poll = handle_single_vote(session, context, option)
        # Block vote
        elif poll.poll_type == PollType.block_vote.name:
            update_poll = handle_block_vote(session, context, option)
        # Limited vote
        elif poll.poll_type == PollType.limited_vote.name:
            update_poll = handle_limited_vote(session, context, option)
        # Cumulative vote
        elif poll.poll_type == PollType.cumulative_vote.name:
            update_poll = handle_cumulative_vote(session, context, option)
        elif poll.poll_type == PollType.count_vote.name:
            update_poll = handle_cumulative_vote(
                session, context, option, limited=False
            )
        elif poll.poll_type == PollType.doodle.name:
            update_poll = handle_doodle_vote(session, context, option)
        elif poll.poll_type == PollType.priority.name:
            update_poll = handle_priority_vote(session, context, option)
        else:
            raise Exception("Unknown poll type")
        session.commit()

    except IntegrityError:
        # Double vote. Rollback the transaction and ignore the second vote
        session.rollback()
        return
    except ObjectDeletedError:
        # Vote on already removed vote. Rollback the transaction and ignore
        session.rollback()
        return
    except StaleDataError:
        # Try to edit a vote that has already been deleted.
        # This happens, if users spam the vote buttons.
        # Rollback the transaction and ignore
        session.rollback()
        return
    except OperationalError:
        # This happens, when a deadlock is created.
        # That can be caused by users spamming the vote button.
        session.rollback()
        return
    except NoResultFound:
        # This can happen if a user concurrently upvotes and downvotes an option.
        # -> Downvote deletes the Vote, upvote tries to change the Vote.
        session.rollback()
        return

    # Update the reference depending on message type
    message = context.query.message
    inline_message_id = context.query.inline_message_id
    if update_poll:
        if message is not None:
            update_poll_messages(
                session, context.bot, poll, message.message_id, context.user
            )
        else:
            update_poll_messages(
                session, context.bot, poll, inline_message_id=inline_message_id
            )

    increase_stat(session, "votes")
    session.commit()