예제 #1
0
    def test_guess_member(self, monkeypatch):
        monkeypatch.setattr(Photon, 'geocode', get_address_from_cache)

        Member.set_akadressen_credentials('http://all', 'http://active', '',
                                          '')

        with open(check_file_path('tests/data/akadressen.pdf'),
                  'rb') as akadressen:
            responses.add(
                responses.GET,
                'http://all',
                body=akadressen.read(),
                stream=True,
                status=200,
                adding_headers={'Transfer-Encoding': 'chunked'},
            )

        with open(check_file_path('tests/data/akadressen-active.pdf'),
                  'rb') as akadressen_active:
            responses.add(
                responses.GET,
                'http://active',
                body=akadressen_active.read(),
                stream=True,
                status=200,
                adding_headers={'Transfer-Encoding': 'chunked'},
            )

        assert Member._AKADRESSEN_CACHE_TIME is None
        assert Member._AKADRESSEN is None
        user_1 = User(1, is_bot=False, first_name='John', last_name='Doe')
        members = Member.guess_member(user_1)
        assert Member._AKADRESSEN_CACHE_TIME == dt.date.today()
        assert isinstance(Member._AKADRESSEN, pd.DataFrame)
        assert len(members) == 1
        member = members[0]
        assert member.user_id == 1
        assert member.last_name == 'Doe'
        assert member.first_name == 'John'
        assert member.nickname == 'Jonny'
        assert member.date_of_birth == dt.date(2000, 1, 1)
        assert member.instruments == [instruments.Trumpet()]
        assert member.address == 'Münzstraße 5, 38100 Braunschweig'
        assert member.joined == 2004

        Member._AKADRESSEN = None
        user_2 = User(2, is_bot=False, first_name='Marcel', last_name='Marcel')
        members = Member.guess_member(user_2)
        assert Member._AKADRESSEN_CACHE_TIME == dt.date.today()
        assert isinstance(Member._AKADRESSEN, pd.DataFrame)
        assert len(members) == 1
        member = members[0]
        assert member.user_id == 2
        assert member.last_name == 'Marcel'
        assert member.first_name == 'Marcel'
        assert member.nickname is None
        assert member.date_of_birth == dt.date(2000, 5, 1)
        assert member.instruments == []
        assert member.address == 'Universitätsplatz 2, 38106 Braunschweig'
        assert member.joined == 2005

        test_flag = False

        def _get_akadressen(*args, **kwargs):
            nonlocal test_flag
            test_flag = True

        monkeypatch.setattr(Member, '_get_akadressen', _get_akadressen)

        user_3 = User(3, is_bot=False, first_name='Test', username='******')
        members = Member.guess_member(user_3)
        assert Member._AKADRESSEN_CACHE_TIME == dt.date.today()
        assert isinstance(Member._AKADRESSEN, pd.DataFrame)
        assert not test_flag
        assert len(members) == 1
        member = members[0]
        assert member.user_id == 3
        assert member.last_name == 'Zufall'
        assert member.first_name == 'Rainer'
        assert member.nickname == 'Das Brot'
        assert member.date_of_birth == dt.date(2007, 7, 5)
        assert member.instruments == [instruments.Flute()]
        assert member.address == 'Bültenweg 74-75, 38106 Braunschweig'
        assert member.joined is None

        user_4 = User(1, is_bot=False, first_name=None)
        assert Member.guess_member(user_4) is None
예제 #2
0
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)