示例#1
0
def test_events_live(uqcsbot: MockUQCSBot):
    """
    This test simulates a user invoking '!events' against the live UQCS calendar
    and ITEE website. No particular assertion is made about the content of the
    bot's response, other than that there is one and it is not an error.
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "!events")
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 2
    assert (messages[1].get('text').startswith("_There don't appear to be any events"
                                               + " in the next *2* weeks_\r\n") or
            messages[1].get('text').startswith("Events in the *next _2_ weeks*\r\n"))
示例#2
0
def test_events_no_events(uqcsbot: MockUQCSBot):
    """
    This test simulates the user invoking !events when there are no events available.
    The test cases includes the ITEE website being unavailable.
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "!events")
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 2
    assert messages[1].get('text') == (
        "_There don't appear to be any events" + " in the next *2* weeks_\n"
        "For a full list of events, visit:" + " https://uqcs.org/events" +
        " and https://www.itee.uq.edu.au/seminar-list")
示例#3
0
def test_thread_discreet_majuscule(uqcsbot: MockUQCSBot):
    """
    test majuscule string reply to thread
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "NEUROMANCER")
    assert count_lowercase_msgs(uqcsbot) == 1
    thread = float(
        uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])[-1].get('ts', 0))
    uqcsbot.post_message(TEST_CHANNEL_ID,
                         "WINTERMUTE",
                         reply_broadcast=False,
                         thread_ts=thread)
    assert count_lowercase_msgs(uqcsbot) == 2
示例#4
0
def test_thread_blatant_minuscule(uqcsbot: MockUQCSBot):
    """
    test minuscule string reply to thread and channel
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "NEUROMANCER")
    assert count_lowercase_msgs(uqcsbot) == 1
    thread = float(
        uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])[-1].get('ts', 0))
    uqcsbot.post_message(TEST_CHANNEL_ID,
                         "wintermute",
                         reply_broadcast=True,
                         thread_ts=thread)
    assert count_lowercase_msgs(uqcsbot) == 3
示例#5
0
def test_final(uqcsbot: MockUQCSBot):
    uqcsbot.post_message(TEST_CHANNEL_ID, "!uqfinal CSSE2002 10% 15 0.15")
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 3
    assert (messages[-2]["text"] ==
            "Inputted score of 10% for Assignment 1 (weighted 10%)\n"
            "Inputted score of 15% for Assignment 2 (weighted 15%)\n"
            "Inputted score of 15% for Assignment 3 (weighted 15%)")
    assert (messages[-1]["text"] ==
            "You need to score at least 75% on the Final examination to achieve a four.\n"
            "You need to score at least 100% on the Final examination to achieve a five.\n"
            "_Disclaimer: this does not take hurdles into account._\n"
            "_Powered by http://uqfinal.com_")
示例#6
0
def test_emoji_log(uqcsbot: MockUQCSBot):
    """
    Test !emoji_log for a member adding or removing emoji.
    """
    events = [
        generate_event_object(MESSAGE_TYPE_CHANNEL_CREATED,
                              channel={
                                  'id': 'emoji-request',
                                  'name': 'emoji-request',
                                  'is_public': True
                              }),
        # normal add comes with an image URL
        generate_event_object(MESSAGE_TYPE_EMOJI_CHANGED,
                              subtype='add',
                              name='emoji_add_test',
                              value='https://bleh.gif'),
        # alias add comes with the alias name
        generate_event_object(MESSAGE_TYPE_EMOJI_CHANGED,
                              subtype='add',
                              name='alias_add',
                              value='alias:emoji_add_test'),
        generate_event_object(MESSAGE_TYPE_EMOJI_CHANGED,
                              subtype='remove',
                              names=['emoji_remove_test']),
        # if base emoji is removed, aliases go too
        generate_event_object(
            MESSAGE_TYPE_EMOJI_CHANGED,
            subtype='remove',
            names=['emoji_remove_test', 'emoji_remove_alias']),
        # no subtype is valid according to slack API
        generate_event_object(MESSAGE_TYPE_EMOJI_CHANGED,
                              names='emoji_remove_test')
    ]
    with patch('time.sleep') as mock_sleep:
        mock_sleep.return_value = None
        for event in events:
            uqcsbot._run_handlers(event)

    emoji_messages = uqcsbot.test_messages.get('emoji-request', [])
    print(emoji_messages)
    assert len(emoji_messages) == 4
    assert emoji_messages[0][
        'text'] == 'Emoji added: :emoji_add_test: (`:emoji_add_test:`)'
    assert (
        emoji_messages[1]['text'] ==
        'Emoji alias added: `:alias_add:` :arrow_right: `:emoji_add_test:` (:alias_add:)'
    )
    assert emoji_messages[2]['text'] == 'Emoji removed: `:emoji_remove_test:`'
    assert (emoji_messages[3]['text'] ==
            'Emojis removed: `:emoji_remove_test:`, `:emoji_remove_alias:`')
示例#7
0
def test_thread_blatant_majuscule(uqcsbot: MockUQCSBot):
    """
    test majuscule string reply to thread and channel
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "NEUROMANCER", user=TEST_USER_ID)
    assert count_lowercase_msgs(uqcsbot) == 1
    thread = float(
        uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])[-1].get('ts', 0))
    uqcsbot.post_message(TEST_CHANNEL_ID,
                         "WINTERMUTE",
                         reply_broadcast=True,
                         thread_ts=thread,
                         user=TEST_USER_ID)
    assert count_lowercase_msgs(uqcsbot) == 2
示例#8
0
def test_thread_discreet_minuscule(uqcsbot: MockUQCSBot):
    """
    test minuscule string reply to thread
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "NEUROMANCER", user=TEST_USER_ID)
    assert count_lowercase_msgs(uqcsbot) == 1
    thread = float(
        uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])[-1].get('ts', 0))
    uqcsbot.post_message(TEST_CHANNEL_ID,
                         "wintermute",
                         reply_broadcast=False,
                         thread_ts=thread,
                         user=TEST_USER_ID)
    assert count_lowercase_msgs(uqcsbot) == 3
示例#9
0
def test_binify_cyclic(uqcsbot: MockUQCSBot):
    """
    Test !binify twice in a row returns original string
    """
    message = "".join([
        choice(
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
        for i in range(32)
    ])
    uqcsbot.post_message(TEST_CHANNEL_ID, "!binify " + message)
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    response = messages[-1]['text']
    uqcsbot.post_message(TEST_CHANNEL_ID, "!binify " + response)
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert messages[-1]['text'] == message
示例#10
0
def test_UQCS_events_october(uqcsbot: MockUQCSBot):
    """
    This test simulates the user invoking `!events oct` when there are uqcs events.
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "!events oct")
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 2
    expected = ("Events in *_October_*\r\n"
                "*TUE OCT 8 18:00 - 20:00* - `UQCS AGM` - _TBC_\r\n"
                "*THU OCT 17 17:00 - 21:00* - `Games Night` - _TBC_\r\n"
                "*TUE OCT 29 0:00 - WED OCT 30 23:59* - `Study Session` - _ITLC:"
                + " GP South 78-217_\r\n"
                "*THU OCT 31 0:00 - FRI NOV 1 23:59* - `Study Session` - _ITLC: GP"
                + " South 78-217_")
    assert messages[1].get('text') == expected
示例#11
0
def test_events_filter_uqcs(uqcsbot: MockUQCSBot):
    """
    This test simulates the user invoking '!events', for both UQCS and Seminar calendars
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "!events uqcs")
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 2
    expected = (
        "Events in the *next _2_ weeks*\r\n"
        "*FRI AUG 2 0:00 - MON AUG 5 23:59* - `CodeNetwork Hackathon` - _River City Labs_\r\n"
        "*FRI AUG 2 18:00 - 20:00* - `Dr Corey Shou` - _TBC_\r\n"
        "*TUE AUG 6 18:00 - 20:00* - `vim & tmux - Neil Ashford` - _Hawken 50-T103_\r\n"
        "*TUE AUG 13 18:00 - 20:00* - `Robogals x UQ Robotics x UQCS"
        + " Social Event` - _Hawken 50-C207_"
    )
    assert messages[1].get('text') == expected
示例#12
0
def test_seminar_events_typical(uqcsbot: MockUQCSBot):
    """
    This test simulates the user invoking !events when there are seminars available.
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "!events")
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 2
    expected = ("Events in the *next _2_ weeks*\r\n"
                "*TUE MAY 28 12:00 - 13:00* - `<https://www.itee.uq.edu.au/introduction"
                + "-functional-programming|Introduction to functional programming"
                + " - Tony Morris, Software Engineer at Data61>` - _78-420_\r\n"
                "*WED MAY 29 13:00 - 14:00*"
                + " - `<https://www.itee.uq.edu.au/performance-enhancement-"
                + "software-defined-cellular-5g-and-internet-things-networks"
                + "|Performance Enhancement of Software Defined Cellular 5G &amp;"
                + " Internet-of-Things Networks - Furqan Khan>` - _78-430_")
    assert messages[1].get('text') == expected
示例#13
0
def test_partial(uqcsbot: MockUQCSBot):
    uqcsbot.post_message(TEST_CHANNEL_ID, "!uqfinal CSSE2002 50% 50%")
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 3
    assert (messages[-2]["text"] ==
            "Inputted score of 50% for Assignment 1 (weighted 10%)\n"
            "Inputted score of 50% for Assignment 2 (weighted 15%)")
    assert (messages[-1]["text"] ==
            "You need to score at least a weighted average of 50% on the"
            " remaining 2 assessments to achieve a four.\n"
            "You need to score at least a weighted average of 70% on the"
            " remaining 2 assessments to achieve a five.\n"
            "You need to score at least a weighted average of 84% on the"
            " remaining 2 assessments to achieve a six.\n"
            "You need to score at least a weighted average of 97% on the"
            " remaining 2 assessments to achieve a seven.\n"
            "_Disclaimer: this does not take hurdles into account._\n"
            "_Powered by http://uqfinal.com_")
示例#14
0
def test_welcome_milestone(uqcsbot: MockUQCSBot):
    """
    Test !welcome for a member joining on a milestone number.
    """
    events = [
        generate_event_object(MESSAGE_TYPE_CHANNEL_CREATED,
                              channel={
                                  'id': 'general',
                                  'name': 'general',
                                  'is_public': True
                              }),
        generate_event_object(MESSAGE_TYPE_CHANNEL_CREATED,
                              channel={
                                  'id': 'announcements',
                                  'name': 'announcements',
                                  'is_public': True
                              })
    ]
    for i in range(MEMBER_MILESTONE):
        user_id = 'U' + str(i).rjust(10, '0')
        uqcsbot.test_users[user_id] = {'id': user_id}
        uqcsbot.test_channels[user_id] = {'id': user_id, 'name': user_id}
        events.extend(
            (generate_event_object(MESSAGE_TYPE_CHANNEL_CREATED,
                                   channel={
                                       'id': user_id,
                                       'name': user_id
                                   }),
             generate_event_object(MESSAGE_TYPE_TEAM_JOIN,
                                   user={'id': user_id}),
             generate_event_object(MESSAGE_TYPE_MEMBER_JOINED_CHANNEL,
                                   channel='announcements',
                                   user=user_id)))
    with patch('time.sleep') as mock_sleep:
        mock_sleep.return_value = None
        for event in events:
            uqcsbot._run_handlers(event)

    general_messages = uqcsbot.test_messages.get('general', [])
    assert len(general_messages) == MEMBER_MILESTONE + 1
    assert general_messages[-1][
        'text'] == f':tada: {MEMBER_MILESTONE} members! :tada:'
示例#15
0
def test_news(uqcsbot: MockUQCSBot):
    """
    Tests !techcrunch
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "!techcrunch")
    message = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert (
        message[-1]['text'] ==
        "*Latest News from <https://techcrunch.com|_TechCrunch_> :techcrunch:*\n"
        "• <http://feedproxy.google.com/~r/Techcrunch/~3/99Ndo-D1yGA/|"
        "Rising encrypted app Signal is down in China>\n"
        "• <http://feedproxy.google.com/~r/Techcrunch/~3/CvyW_t3oxSQ/|"
        "China wants to dismantle Alibaba’s media empire: reports>\n"
        "• <http://feedproxy.google.com/~r/Techcrunch/~3/EqeGTrnZEYs/|"
        "Bird to spend $150 million on European expansion plan>\n"
        "• <http://feedproxy.google.com/~r/Techcrunch/~3/X98SDLR4A6Q/|"
        "Sherpa raises $8.5M to expand from conversational AI to B2B "
        "privacy-first federated learning services>\n"
        "• <http://feedproxy.google.com/~r/Techcrunch/~3/SdwlDvQPZHU/|"
        "Daily Crunch: Stripe valued at $95B>\n")
示例#16
0
def test_parking(uqcsbot: MockUQCSBot):
    """
    Tests !parking
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, '!parking')
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 2
    assert (
        messages[-1]['text'] == "*Available Parks at UQ St. Lucia*\n"
        "32 Carparks Available in P1 - Warehouse (14P Daily)\n"
        "Few Carparks Available in P2 - Space Bank (14P Daily)\n"
        "6 Carparks Available in P6 - Hartley Teakle (14P Hourly)\n"
        "No Carparks Available in P7 - DustBowl (14P Daily)\n"
        "39 Carparks Available in P7 - Keith Street (14P Daily Capped)\n"
        "58 Carparks Available in P8 - Athletics Basement (14P Daily)\n"
        "29 Carparks Available in P8 - Athletics Roof (14P Daily)\n"
        "21 Carparks Available in P9 - Boatshed (14P Daily)\n"
        "167 Carparks Available in P10 - UQ Centre" +
        " & Playing Fields (14P Daily/14P Daily Capped)\n"
        "Few Carparks Available in P11 - Conifer Knoll Roof (14P Daily Restricted)"
    )
示例#17
0
def test_mock_past_message(uqcsbot: MockUQCSBot):
    '''
    Test !mock on a message from the past
    '''
    uqcsbot.post_message(TEST_CHANNEL_ID, LONG_MESSAGE)
    uqcsbot.post_message(TEST_CHANNEL_ID, 'just')
    uqcsbot.post_message(TEST_CHANNEL_ID, 'some')
    uqcsbot.post_message(TEST_CHANNEL_ID, 'message')
    uqcsbot.post_message(TEST_CHANNEL_ID, 'padding')
    uqcsbot.post_message(TEST_CHANNEL_ID, '!mock 4')
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 7
    assert messages[-1]['text'].lower() == LONG_MESSAGE.lower()
    assert messages[-1]['text'] != LONG_MESSAGE
示例#18
0
def test_bad_input(uqcsbot: MockUQCSBot):
    uqcsbot.post_message(TEST_CHANNEL_ID, "!trivia -d easp")
    uqcsbot.post_message(TEST_CHANNEL_ID, "!trivia -c -22")
    uqcsbot.post_message(TEST_CHANNEL_ID, "!trivia -t raspberry")

    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])

    assert len(messages) == 6
示例#19
0
def test_events_normal(uqcsbot: MockUQCSBot):
    """
    This test simulates the user invoking '!events', for both UQCS and Seminar calendars
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "!events")
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert len(messages) == 2
    expected = (
        "Events in the *next _2_ weeks*\r\n"
        "*TUE MAY 28 12:00 - 13:00* - `<https://www.itee.uq.edu.au/introduction"
        + "-functional-programming|Introduction to functional programming"
        + " - Tony Morris, Software Engineer at Data61>` - _78-420_\r\n"
        "*WED MAY 29 13:00 - 14:00* - `<https://www.itee.uq.edu.au/performance-enhancement-"
        + "software-defined-cellular-5g-and-internet-things-networks"
        + "|Performance Enhancement of Software Defined Cellular 5G &amp;"
        + " Internet-of-Things Networks - Furqan Khan>` - _78-430_\r\n"
        "*FRI AUG 2 0:00 - MON AUG 5 23:59* - `CodeNetwork Hackathon` - _River City Labs_\r\n"
        "*FRI AUG 2 18:00 - 20:00* - `Dr Corey Shou` - _TBC_\r\n"
        "*TUE AUG 6 18:00 - 20:00* - `vim &amp; tmux - Neil Ashford` - _Hawken 50-T103_\r\n"
        "*TUE AUG 13 18:00 - 20:00* - `Robogals x UQ Robotics x UQCS"
        + " Social Event` - _Hawken 50-C207_"
    )
    assert messages[1].get('text') == expected
示例#20
0
def test_welcome(uqcsbot: MockUQCSBot):
    """
    Test !welcome for a member joining UQCS.
    """
    events = [
        generate_event_object(MESSAGE_TYPE_CHANNEL_CREATED,
                              channel={'id': 'general', 'name': 'general', 'is_public': True}),
        generate_event_object(MESSAGE_TYPE_CHANNEL_CREATED,
                              channel={'id': 'announcements', 'name': 'announcements',
                                       'is_public': True}),
        generate_event_object(MESSAGE_TYPE_MEMBER_JOINED_CHANNEL,
                              channel='announcements', user=TEST_USER_ID)
    ]
    with patch('time.sleep') as mock_sleep:
        mock_sleep.return_value = None
        for event in events:
            uqcsbot._run_handlers(event)

    general_messages = uqcsbot.test_messages.get('general', [])
    direct_messages = uqcsbot.test_messages.get(TEST_DIRECT_ID, [])
    print(general_messages, direct_messages)
    assert len(direct_messages) > 0
    assert general_messages[-1]['text'] == f'Welcome to UQCS, <@{TEST_USER_ID}>! :tada:'
示例#21
0
def test_url(uqcsbot: MockUQCSBot):
    """
    Test that URLs are not detected as talking quietly.
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "Hoogle", user=TEST_USER_ID)
    assert count_messages(uqcsbot) == 2
    uqcsbot.post_message(TEST_CHANNEL_ID,
                         "hoogle.haskell.org/",
                         user=TEST_USER_ID)
    assert count_messages(uqcsbot) == 3
    thread = float(
        uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])[-1].get('ts', 0))
    uqcsbot.post_message(TEST_CHANNEL_ID,
                         "https://google.com",
                         reply_broadcast=True,
                         thread_ts=thread,
                         user=TEST_USER_ID)
    assert count_messages(uqcsbot) == 4
    uqcsbot.post_message(TEST_CHANNEL_ID,
                         "<https://google.com|google.com>",
                         user=TEST_USER_ID)
    assert count_messages(uqcsbot) == 5
示例#22
0
def test_error(uqcsbot: MockUQCSBot):
    """
    Test !weather for error messages
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather nowhere')
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert messages[-1]['text'] == "Location Not Found"

    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather -1')
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert messages[-1]['text'] == "No Forecast Available For That Day"

    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather TAS Hobart')
    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    assert messages[-1]['text'] == "Could Not Retrieve BOM Data"
示例#23
0
def test_brisbane(uqcsbot: MockUQCSBot):
    """
    Tests all varients !weather that give today's Brisbane weather
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather')
    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather Brisbane')
    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather QLD Brisbane')
    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather 0')
    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather Brisbane 0')
    uqcsbot.post_message(TEST_CHANNEL_ID, '!weather QLD Brisbane 0')

    messages = uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])
    for i in range(len(messages)-1, -1, -1):
        if messages[i]['text'].startswith("!weather"):
            messages.pop(i)

    assert len(messages) == 6
    for m in messages[1:]:
        assert m['text'] == messages[0]['text']

    assert messages[0]['text'].split("\n")[0] == "*Today's Weather Forecast For Brisbane*"
示例#24
0
def test_cat(uqcsbot: MockUQCSBot):
    """
    test !cat
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, '!cat')
    assert len(uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])) == 2
示例#25
0
def test_not_implemented_command(uqcsbot: MockUQCSBot):
    """
    Tests that the bot correctly detects an invalid command.
    """
    with pytest.raises(NotImplementedError):
        uqcsbot.post_message(TEST_CHANNEL_ID, '!thiscommanddoesntexist')
示例#26
0
def test_dog(uqcsbot: MockUQCSBot):
    """
    test !dog
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, '!dog')
    assert len(uqcsbot.test_messages.get(TEST_CHANNEL_ID, [])) == 2
示例#27
0
def test_channel(uqcsbot: MockUQCSBot):
    """
    tests outside of #yeling
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "wintermute", user=TEST_USER_ID)
    assert count_lowercase_msgs(uqcsbot) == 1
示例#28
0
def test_mixed(uqcsbot: MockUQCSBot):
    """
    test mixed case string
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "wiNTErMUTe", user=TEST_USER_ID)
    assert count_lowercase_msgs(uqcsbot) == 2
示例#29
0
def test_majuscule(uqcsbot: MockUQCSBot):
    """
    test majuscule string
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "WINTERMUTE", user=TEST_USER_ID)
    assert count_lowercase_msgs(uqcsbot) == 1
示例#30
0
def test_minuscule(uqcsbot: MockUQCSBot):
    """
    test minuscule string
    """
    uqcsbot.post_message(TEST_CHANNEL_ID, "wintermute", user=TEST_USER_ID)
    assert count_lowercase_msgs(uqcsbot) == 2