예제 #1
0
def post_msg(channel_or_user_id: str, text) -> None:
    logging.info(f"Posting to {channel_or_user_id}: {text}")
    SLACK_CLIENT.api_call(
        "chat.postMessage",
        channel=channel_or_user_id,
        text=text,
        link_names=True,  # convert # and @ in links
        as_user=True,
        unfurl_links=False,
        unfurl_media=False,
    )
예제 #2
0
파일: slack.py 프로젝트: pmayd/karmabot
def parse_next_msg():
    """Parse next message posted on slack for actions to do by bot"""
    msg = SLACK_CLIENT.rtm_read()
    if not msg:
        return None

    msg = msg[0]
    user_id = msg.get("user")
    channel_id = msg.get("channel")
    text = msg.get("text")

    # handle events first
    event_type = msg.get("type")
    # 1. if new channel auto-join bot
    if event_type == "channel_created":
        bot_joins_new_channel(msg["channel"]["id"])
        return None

    # 2. if a new user joins send a welcome msg
    if event_type == "team_join":
        # return the message to apply the karma change
        # https://api.slack.com/methods/users.info
        welcome_msg = AUTOMATED_COMMANDS["welcome"](
            user_id["id"])  # new user joining
        post_msg(user_id["id"], welcome_msg)
        # return Message object to handle karma in main
        return Message(user_id=KARMABOT_ID,
                       channel_id=GENERAL_CHANNEL,
                       text=welcome_msg)
    # end events

    # not sure but sometimes we get dicts?
    if (not isinstance(channel_id, str) or not isinstance(user_id, str)
            or not isinstance(text, str)):
        return None

    # ignore anything karma bot says!
    if user_id == KARMABOT_ID:
        return None

    # text replacements on first matching word in text
    # TODO: maybe this should replace all instances in text message ...
    text_replace_output = text and perform_text_replacements(text)
    if text_replace_output:
        post_msg(channel_id, text_replace_output)

    # if we recognize a valid bot command post its output, done
    # DM's = channels start with a 'D' / channel can be dict?!
    private = channel_id and channel_id.startswith("D")
    cmd_output = perform_bot_cmd(msg, private)
    if cmd_output:
        post_msg(channel_id, cmd_output)
        return None

    if not channel_id or not text:
        return None

    # Returns a message for karma processing
    return Message(user_id=user_id, channel_id=channel_id, text=text)
예제 #3
0
def get_channel_name(channel_id: str) -> str:
    channel_info: dict = SLACK_CLIENT.api_call("channels.info", channel=channel_id)

    # Private channels and direct messages cannot be resolved via api
    if not channel_info["ok"]:
        return "Unknown or Private"

    channel_name = channel_info["channel"]["name"]
    return channel_name
예제 #4
0
def test_slack_team_join(mock_slack_rtm_read_team_join, mock_slack_api_call):
    user_id = SLACK_CLIENT.rtm_read()[0].get("user")["id"]
    welcome_user(user_id)

    actual = parse_next_msg()

    assert actual.user_id == KARMABOT_ID
    assert actual.channel_id == GENERAL_CHANNEL
    assert user_id in actual.text
    assert "Introduce yourself in #general if you like" in actual.text
예제 #5
0
def get_bot_id():
    BOT_NAME = "karmabot"

    api_call = SLACK_CLIENT.api_call("users.list")

    if not api_call.get("ok"):
        error = api_call.get("error", "none")
        print(f"Could not get users.list, error: {error}")
        sys.exit(1)

    users = api_call.get("members")
    for user in users:
        if "name" in user and user.get("name") == BOT_NAME:
            print(f"Bot ID for {user['name']} is {user.get('id')}")
예제 #6
0
    def _create_karma_user(self, user_id):
        user_info = SLACK_CLIENT.api_call("users.info", user=user_id)

        error = user_info.get("error")
        if error is not None:
            logging.info(f"Cannot get user info for {user_id} - error: {error}")
            raise GetUserInfoException

        slack_id = user_info["user"]["id"]
        username = get_available_username(user_info)

        new_user = KarmaUser(user_id=slack_id, username=username)
        self.session.add(new_user)
        self.session.commit()

        logging.info(f"Created new KarmaUser: {repr(new_user)}")
        return new_user
예제 #7
0
def get_recommended_channels(**kwargs):
    """Show some of our Community's favorite channels you can join
    see https://api.slack.com/methods/channels.list as well as https://api.slack.com/methods/channels.info for API info
    """
    _, text = kwargs.get("user"), kwargs.get("text")
    potential_channels: Channel = []
    msg = MSG_BEGIN

    nr_channels = text.split()[2] if len(
        text.split()) >= 3 else DEFAULT_NR_CHANNELS
    if isinstance(nr_channels, str):
        nr_channels = (int(nr_channels)
                       if nr_channels.isnumeric() else DEFAULT_NR_CHANNELS)

    # retrieve channel list
    response: Dict = SLACK_CLIENT.api_call("channels.list",
                                           exclude_archived=True,
                                           exclude_members=True)
    if not response["ok"]:
        logging.error(
            f'Error for API call "channels.list": {response["error"]}')
        return "I am truly sorry but something went wrong ;("

    channels: List[Dict] = response["channels"]

    # retrieve channel info for each channel in channel list
    # only consider channels that are not the general channel, that are not private and that have at least one message
    for channel in channels:
        channel_is_potential = (channel["is_channel"]
                                and not channel["is_general"]
                                and not channel["is_private"])

        if channel_is_potential:
            # we have to stick with channel.info, also it could be
            # that the latest message is a bot or join message
            # but channels.history is not allowed for bots.
            # However, it seems that in the future, Slack will update the bot permissions
            # see: https://api.slack.com/methods/channels.history
            response: Dict = SLACK_CLIENT.api_call("channels.info",
                                                   channel=channel["id"])
            if not response["ok"]:
                logging.error(
                    f'Error for API call "channel.info": {response["error"]}')
                return "I am truly sorry but something went wrong ;("

            channel_info: Dict = response["channel"]

            if channel_info.get("latest", None):
                potential_channels.append(
                    Channel(
                        channel["id"],
                        channel["name"],
                        channel_info["purpose"]["value"],
                        channel["num_members"],
                        float(channel_info["latest"]["ts"]),
                        channel_info["latest"].get("subtype"),
                    ))

    # now weight channels and return message
    potential_channels = sorted(
        ((calc_channel_score(chan), chan) for chan in potential_channels),
        reverse=True)

    msg = MSG_BEGIN + "\n".join((MSG_LINE.format(
        channel=channel.name,
        member_count=channel.num_members,
        time_since_last_post=humanize.naturaltime(
            seconds_since_last_post(channel)),
        purpose=channel.purpose
        or "<Invest today and get an awesome description!>",
    ) for score, channel in potential_channels[:nr_channels] if score > 0))

    return msg
예제 #8
0
def test_slack_rtm_read(mock_slack_rtm_read_msg):
    event = SLACK_CLIENT.rtm_read()
    assert event[0]["type"] == "message"
    assert event[0]["user"] == "ABC123"
    assert event[0]["text"] == "Hi everybody"
예제 #9
0
def test_ignore_message_subtypes(mock_slack_api_call, frozen_now):
    latest_ignored = SLACK_CLIENT.api_call("channels.info", channel="SOMEJOINS")
    all_ignored = SLACK_CLIENT.api_call("channels.info", channel="ONLYJOINS")
    assert _channel_score(latest_ignored) > 0
    assert _channel_score(all_ignored) == 0
예제 #10
0
def test_channel_score(mock_slack_api_call, frozen_now):
    most_recent = SLACK_CLIENT.api_call("channels.info", channel="CHANNEL42")
    less_recent = SLACK_CLIENT.api_call("channels.info", channel="CHANNEL43")
    assert _channel_score(most_recent) > _channel_score(less_recent)
예제 #11
0
def check_connection():
    # Slack Real Time Messaging API - https://api.slack.com/rtm
    if not SLACK_CLIENT.rtm_connect():
        logging.error("Connection Failed, invalid token?")
        sys.exit(1)
예제 #12
0
def test_get_available_username(mock_slack_api_call, test_user_id, expected):
    user_info = SLACK_CLIENT.api_call("users.info", user=test_user_id)
    assert get_available_username(user_info) == expected