def orchestra(members, bot, chat_id, skip=None): if skip is None: skip = [] orchestra_ = Orchestra() for i in range(1, members): gender = random.choice([Gender.MALE, Gender.FEMALE]) phone_number_ = phone_number() if 'phone_number' not in skip else None first_name_ = first_name(gender) if 'first_name' not in skip else None last_name_ = last_name() if 'last_name' not in skip else None nickname_ = nickname() if 'nickname' not in skip else None date_of_birth_ = date_of_birth( ) if 'date_of_birth' not in skip else None instruments_ = instrument() if 'instruments' not in skip else None address_ = address() if 'address' not in skip else None photo_file_id_ = (PhotoFileID.photo_file_id(bot, chat_id) if 'photo_file_id' not in skip else None) orchestra_.register_member( Member( user_id=i, phone_number=phone_number_, first_name=first_name_, last_name=last_name_, nickname=nickname_, gender=gender, date_of_birth=date_of_birth_, instruments=instruments_, address=address_, photo_file_id=photo_file_id_, )) return orchestra_
def score_orchestra_anonymous(date): o = Orchestra() offset = dt.timedelta(weeks=100) m_1 = Member(1) m_1.user_score.add_to_score(8, 4, date) m_1.user_score.add_to_score(10, 10, date - offset) o.register_member(m_1) return o
def rebuild_orchestra(update: Update, context: CallbackContext) -> None: """ Builds a new orchestra by registering all members from the one in ``context.bot_data`` to a new instance of :class:`components.Orchestra`. Puts the new orchestra in ``bot_data``. Useful, if there have been changes to the attribute manager setup. Args: update: The update. context: The context as provided by the :class:`telegram.ext.Dispatcher`. """ members = context.bot_data[ORCHESTRA_KEY].members.values() orchestra = Orchestra() for member in members: orchestra.register_member(member.copy()) context.bot_data[ORCHESTRA_KEY] = orchestra update.message.reply_text('Orchester neu besetzt.')
def score_orchestra(date): o = Orchestra() offset = dt.timedelta(weeks=100) m_1 = Member(1, first_name='One') m_1.user_score.add_to_score(8, 4, date) m_1.user_score.add_to_score(10, 10, date - offset) m_2 = Member(2, first_name='Two') m_2.user_score.add_to_score(4, 2, date) m_2.user_score.add_to_score(10, 10, date - offset) m_3 = Member(3, first_name='Three') m_3.user_score.add_to_score(3, 1, date) m_3.user_score.add_to_score(10, 10, date - offset) m_4 = Member(4, first_name='Four') m_4.user_score.add_to_score(4, 1, date) m_4.user_score.add_to_score(10, 10, date - offset) for m in [m_1, m_2, m_3, m_4]: o.register_member(m) return o
def build_questions_hints_keyboard( orchestra: Orchestra, question: bool = False, hint: bool = False, current_selection: Optional[Dict[str, bool]] = None, multiple_choice: bool = True, allowed_hints: List[str] = None, exclude_members: Iterable[Member] = None, ) -> InlineKeyboardMarkup: """ Builds a :class:`telegram.InlineKeyboardMarkup` listing all questions that are up for selection for the given orchestra. The callback data for each button will equal its text. Also appends a button with the text :attr:`DONE` and data :attr:`DONE` at the very end of the keyboard. Args: orchestra: The orchestra to build the keyboard for. question: Optional. Set to :obj:`True`, if the keyboard is build for question selection. hint: Optional. Set to :obj:`True`, if the keyboard is build for hint selection. current_selection: Optional. If passed, gives the current selection and the keyboard will reflect that selection state. If not present, all options will be deselected. A corresponding dictionary is returned e.g. by :meth:`parse_questions_hints_keyboard`. multiple_choice: Optional. Whether the questions are supposed to be multiple choice or free text. Defaults to :obj:`True`. allowed_hints: Optional. Only relevant if :obj:`question` is :obj:`True`. If passed, in this case only question attributes which are questionable for at least one of the allowed hints will be listed in the keyboard. exclude_members: Optional. Members to exclude from serving as hint. Note: Exactly one on :attr:`hint` and :attr:`question` must be :obj:`True`. Returns: InlineKeyboardMarkup Raises: RuntimeError: If the orchestra currently has no questionable attributes. """ if question == hint: raise ValueError('Exactly one on hint and question must be True.') current_selection = current_selection or dict() questionable = orchestra.questionable(multiple_choice=multiple_choice, exclude_members=exclude_members) if not questionable: raise RuntimeError( 'Orchestra currently has no questionable attributes.') hints = [q[0].description for q in questionable] if question and allowed_hints: questions = [ q[1].description for q in questionable if q[0] in allowed_hints ] else: questions = [q[1].description for q in questionable] buttons = [] any_selected = False for row in QUESTION_HINT_KEYBOARD: button_row = [] for option in row: if (hint and option not in hints) or (question and option not in questions): continue text = ( f'{Orchestra.TO_HR[option]} ' f'{SELECTED if current_selection.get(option) else DESELECTED}') callback_data = f'{option} {SELECTED if current_selection.get(option) else DESELECTED}' button = InlineKeyboardButton(text=text, callback_data=callback_data) button_row.append(button) if current_selection.get(option): any_selected = True if row: buttons.append(button_row) if any_selected: buttons.append([ InlineKeyboardButton(text=ALL, callback_data=ALL), InlineKeyboardButton(text=DONE, callback_data=DONE), ]) else: buttons.append([InlineKeyboardButton(text=ALL, callback_data=DONE)]) return InlineKeyboardMarkup(buttons)
def empty_orchestra(): return Orchestra()
def setup( # pylint: disable=R0913,R0914,R0915 dispatcher: Dispatcher, admin: Union[int, str], oc_url: str, oc_username: str, oc_password: str, oc_path: str, ad_url: str, ad_url_active: str, ad_username: str, ad_password: str, yourls_url: str, yourls_signature: str, ) -> None: """ * Adds handlers. Convenience method to avoid doing that all in the main script. * Sets the bot commands and makes sure ``dispatcher.bot_data`` is set up correctly. * Registers a :class:`telegram.ext.TypeHandler` that makes sure that conversations are not interrupted * Sets up statistics Args: dispatcher: The :class:`telegram.ext.Dispatcher`. admin: The admins chat id. oc_url: URL of the OwnCloud Instance. oc_username: Username for the OwnCloud Instance. oc_password: Password of the OwnCloud Instance. oc_path: Remote path on the OwnCloud Instance. ad_url: URL of the AkaDressen file. ad_url_active: URL of the AkaDressen file containing only the active members. ad_username: Username for the AkaDressen. ad_password: Password for the AkaDressen. yourls_url: URL of the YOURLS instance. yourls_signature: Signature for the YOURLS instance. """ def check_conversation_status(update: Update, context: CallbackContext) -> None: """ Checks if the user is currently in a conversation. If they are and the corresponding conversation does *not* handle the incoming update, the user will get a corresponding message and the update will be discarded. Args: update: The update. context: The context as provided by the :class:`telegram.ext.Dispatcher`. """ if not update.effective_user: return conversation = context.user_data.get(CONVERSATION_KEY, None) if not conversation: return conversation_check = not bool( conversations[conversation].check_update(update)) # Make sure that the callback queries for vcard requests are not processed if update.callback_query: contact_request_check = bool( re.match(inline.REQUEST_CONTACT_PATTERN, update.callback_query.data)) highscore_check = 'highscore' in update.callback_query.data else: contact_request_check = False highscore_check = False if conversation_check or contact_request_check or highscore_check: text = interrupt_replies[conversation] if update.callback_query: update.callback_query.answer(text=text, show_alert=True) elif update.effective_message: update.effective_message.reply_text(text) raise DispatcherHandlerStop() def clear_conversation_status(update: Update, context: CallbackContext) -> None: """ Clears the conversation status of a user in case of an error. Just to be sure. Args: update: The update. context: The context as provided by the :class:`telegram.ext.Dispatcher`. """ if update.effective_user: context.user_data.pop(CONVERSATION_KEY) # ------------------------------------------------------------------------------------------- # # Set up statistics set_dispatcher(dispatcher) # Count total number of updates register_stats(SimpleStats('stats', lambda u: bool(u.effective_user)), admin_id=int(admin)) # Count number of started games register_stats( SimpleStats( 'game_stats', lambda u: bool(u.message) and Filters.text('/spiel_starten')(u)), admin_id=int(admin), ) # Count number of requested contacts register_stats( admin_id=int(admin), stats=SimpleStats( 'contact_stats', lambda u: bool(u.callback_query and 'contact_request' in u. callback_query.data), ), ) # Handlers # Prepare conversations game_handler = game.GAME_HANDLER editing_conversation = editing.build_editing_handler(int(admin)) canceling_conversation = cancel_membership.CANCEL_MEMBERSHIP_HANDLER banning_conversation = ban.build_banning_handler(int(admin)) conversations: Dict[str, ConversationHandler] = { game.CONVERSATION_VALUE: game_handler, editing.CONVERSATION_VALUE: editing_conversation, cancel_membership.CONVERSATION_VALUE: canceling_conversation, ban.CONVERSATION_VALUE: banning_conversation, } interrupt_replies: Dict[str, str] = { game.CONVERSATION_VALUE: game.CONVERSATION_INTERRUPT_TEXT, editing.CONVERSATION_VALUE: editing.CONVERSATION_INTERRUPT_TEXT, cancel_membership.CONVERSATION_VALUE: cancel_membership.CONVERSATION_INTERRUPT_TEXT, ban.CONVERSATION_VALUE: ban.CONVERSATION_INTERRUPT_TEXT, } # Registration status dispatcher.add_handler(TypeHandler(Update, registration.check_registration_status), group=-2) # Conversation Interruption behaviour dispatcher.add_handler(TypeHandler(Update, check_conversation_status), group=-1) # Game Conversation # Must be first so that the fallback can catch unrelated messages dispatcher.add_handler(game_handler) # Registration process # We need the filter here in order to not catch /start with deep linking parameter used for # inline help dispatcher.add_handler( CommandHandler('start', registration.start, filters=Filters.text('/start'))) dispatcher.add_handler( CallbackQueryHandler(registration.request_registration, pattern=REGISTRATION_PATTERN, run_async=True)) dispatcher.add_handler(registration.ACCEPT_REGISTRATION_HANDLER) dispatcher.add_handler(registration.DENY_REGISTRATION_HANDLER) # Edit user data dispatcher.add_handler(editing_conversation) # Cancel membership dispatcher.add_handler(canceling_conversation) # Banning members dispatcher.add_handler(banning_conversation) # Simple commands dispatcher.add_handler( CommandHandler(['hilfe', 'help'], commands.help_message)) dispatcher.add_handler(CommandHandler('daten_anzeigen', commands.show_data)) dispatcher.add_handler( CommandHandler('kontakt_abrufen', commands.start_inline)) dispatcher.add_handler( CommandHandler('start', commands.start_inline, filters=Filters.text(f'/start {INLINE_HELP}'))) # Inline Mode dispatcher.add_handler(InlineQueryHandler(inline.search_users)) dispatcher.add_handler(inline.SEND_VCARD_HANDLER) # Highscores dispatcher.add_handler( CommandHandler('highscore', highscore.show_highscore)) dispatcher.add_handler(highscore.HIGHSCORE_HANDLER) # Set commands dispatcher.bot.set_my_commands(BOT_COMMANDS) # Admin stuff dispatcher.add_handler( CommandHandler('rebuild', bot.admin.rebuild_orchestra, filters=Filters.user(int(admin)))) # Error Handler dispatcher.add_error_handler(error.handle_error) dispatcher.add_error_handler(clear_conversation_status) # Schedule jobs check_user_status.schedule_daily_job(dispatcher) backup.PATH = oc_path backup.URL = oc_url backup.USERNAME = oc_username backup.PASSWORD = oc_password backup.schedule_daily_job(dispatcher) # Set up AkaDressen credentials Member.set_akadressen_credentials(ad_url, ad_url_active, ad_username, ad_password) # Set up bot_data bot_data = dispatcher.bot_data if not bot_data.get(ORCHESTRA_KEY): bot_data[ORCHESTRA_KEY] = Orchestra() else: # We rebuild the orchestra on start up to make sure code changes are applied old_orchestra = bot_data.pop(ORCHESTRA_KEY) new_orchestra = old_orchestra.copy() bot_data[ORCHESTRA_KEY] = new_orchestra if not bot_data.get(PENDING_REGISTRATIONS_KEY): bot_data[PENDING_REGISTRATIONS_KEY] = dict() if not bot_data.get(DENIED_USERS_KEY): bot_data[DENIED_USERS_KEY] = list() bot_data[ADMIN_KEY] = int(admin) yourls_client = YOURLSClient(yourls_url, signature=yourls_signature, nonce_life=True) bot_data[YOURLS_KEY] = yourls_client # Clear conversation key user_data = dispatcher.user_data for user_id in user_data: user_data[user_id].pop(CONVERSATION_KEY, None)
def orchestra(): return Orchestra()