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