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)
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), )
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)
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), )
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"
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)
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)
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)
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))
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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()
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)
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()