diff options
author | Trygve Aaberge <trygveaa@gmail.com> | 2018-06-27 21:34:50 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-27 21:34:50 +0200 |
commit | 4fac1a9512fdac1268599e13e37e1778e31487ef (patch) | |
tree | 0576b5478231d776ea7749443343717d0f6cd52e | |
parent | a8b11534fbb5d9543f8b556a5ac4fc1d7b219486 (diff) | |
parent | b6d9e04bc0aa47545f46fc468c8ccc7d39ddd256 (diff) | |
download | wee-slack-4fac1a9512fdac1268599e13e37e1778e31487ef.tar.gz |
Merge pull request #462 from ericdwang/shared-channels
Add support for shared channels
-rw-r--r-- | _pytest/data/http/rtm.start.json | 8 | ||||
-rw-r--r-- | wee_slack.py | 187 |
2 files changed, 152 insertions, 43 deletions
diff --git a/_pytest/data/http/rtm.start.json b/_pytest/data/http/rtm.start.json index d9cc464..34d875a 100644 --- a/_pytest/data/http/rtm.start.json +++ b/_pytest/data/http/rtm.start.json @@ -307,6 +307,8 @@ "creator": "U407ABLLW", "is_archived": false, "is_general": true, + "is_shared": false, + "is_org_shared": false, "has_pins": false, "is_member": true, "last_read": "1485976236.000019", @@ -392,6 +394,8 @@ "creator": "U407ABLLW", "is_archived": false, "is_general": false, + "is_shared": false, + "is_org_shared": false, "has_pins": false, "is_member": false, "previous_names": [] @@ -404,6 +408,8 @@ "creator": "U407ABLLW", "is_archived": false, "is_general": false, + "is_shared": false, + "is_org_shared": false, "has_pins": false, "is_member": true, "last_read": "1485969592.000002", @@ -441,6 +447,8 @@ "creator": "U407ABLLW", "is_archived": false, "is_general": false, + "is_shared": false, + "is_org_shared": false, "has_pins": false, "is_member": false, "previous_names": [ diff --git a/wee_slack.py b/wee_slack.py index c0eda21..f14cfa8 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -72,6 +72,13 @@ SLACK_API_TRANSLATOR = { "mark": "groups.mark", "info": "groups.info" }, + "shared": { + "history": "conversations.history", + "join": "conversations.join", + "leave": "conversations.leave", + "mark": "channels.mark", + "info": "conversations.info", + }, "thread": { "history": None, "join": None, @@ -122,6 +129,7 @@ def utf8_decode(f): NICK_GROUP_HERE = "0|Here" NICK_GROUP_AWAY = "1|Away" +NICK_GROUP_EXTERNAL = "2|External" sslopt_ca_certs = {} if hasattr(ssl, "get_default_verify_paths") and callable(ssl.get_default_verify_paths): @@ -998,7 +1006,8 @@ class SlackTeam(object): Team object under which users and channels live.. Does lots. """ - def __init__(self, eventrouter, token, websocket_url, subdomain, nick, myidentifier, users, bots, channels, **kwargs): + def __init__(self, eventrouter, token, websocket_url, team_info, nick, myidentifier, users, bots, channels, **kwargs): + self.identifier = team_info["id"] self.ws_url = websocket_url self.connected = False self.connecting = False @@ -1008,8 +1017,8 @@ class SlackTeam(object): self.eventrouter = eventrouter self.token = token self.team = self - self.subdomain = subdomain - self.domain = subdomain + ".slack.com" + self.subdomain = team_info["domain"] + self.domain = self.subdomain + ".slack.com" self.preferred_name = self.domain self.nick = nick self.myidentifier = myidentifier @@ -1129,7 +1138,7 @@ class SlackTeam(object): def is_user_present(self, user_id): user = self.users.get(user_id) - if user.presence == 'active': + if user and user.presence == 'active': return True else: return False @@ -1295,6 +1304,8 @@ class SlackChannel(object): prepend = ">" elif self.type == "group": prepend = config.group_name_prefix + elif self.type == "shared": + prepend = config.shared_name_prefix else: prepend = "#" select = { @@ -1600,7 +1611,7 @@ class SlackChannel(object): def update_nicklist(self, user=None): if not self.channel_buffer: return - if self.type not in ["channel", "group", "mpim"]: + if self.type not in ["channel", "group", "mpim", "shared"]: return w.buffer_set(self.channel_buffer, "nicklist", "1") # create nicklists for the current channel if they don't exist @@ -1612,16 +1623,25 @@ class SlackChannel(object): if not afk: afk = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 1) + # Add External nicklist group only for shared channels + if self.type == 'shared': + external = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_EXTERNAL) + if not external: + external = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_EXTERNAL, 'weechat.color.nicklist_group', 2) + if user and len(self.members) < 1000: - user = self.team.users[user] - if user.deleted: + user = self.team.users.get(user) + # External users that have left shared channels won't exist + if not user or user.deleted: return nick = w.nicklist_search_nick(self.channel_buffer, "", user.name) # since this is a change just remove it regardless of where it is w.nicklist_remove_nick(self.channel_buffer, nick) # now add it back in to whichever.. nick_group = afk - if self.team.is_user_present(user.identifier): + if user.is_external: + nick_group = external + elif self.team.is_user_present(user.identifier): nick_group = here if user.identifier in self.members: w.nicklist_add_nick(self.channel_buffer, nick_group, user.name, user.color_name, "", "", 1) @@ -1631,11 +1651,13 @@ class SlackChannel(object): if len(self.members) < 1000: try: for user in self.members: - user = self.team.users[user] + user = self.team.users.get(user) if user.deleted: continue nick_group = afk - if self.team.is_user_present(user.identifier): + if user.is_external: + nick_group = external + elif self.team.is_user_present(user.identifier): nick_group = here w.nicklist_add_nick(self.channel_buffer, nick_group, user.name, user.color_name, "", "", 1) except Exception as e: @@ -1681,12 +1703,19 @@ class SlackDMChannel(SlackChannel): def __init__(self, eventrouter, users, **kwargs): dmuser = kwargs["user"] - kwargs["name"] = users[dmuser].name + kwargs["name"] = users[dmuser].name if dmuser in users else dmuser super(SlackDMChannel, self).__init__(eventrouter, **kwargs) self.type = 'im' self.update_color() self.set_name(self.slack_name) - self.topic = create_user_status_string(users[dmuser].profile) + if dmuser in users: + self.topic = create_user_status_string(users[dmuser].profile) + + def set_related_server(self, team): + super(SlackDMChannel, self).set_related_server(team) + if self.user not in self.team.users: + s = SlackRequest(self.team.token, 'users.info', {'user': self.slack_name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) def set_name(self, slack_name): self.name = slack_name @@ -1818,6 +1847,29 @@ class SlackMPDMChannel(SlackChannel): pass +class SlackSharedChannel(SlackChannel): + def __init__(self, eventrouter, **kwargs): + super(SlackSharedChannel, self).__init__(eventrouter, **kwargs) + self.type = 'shared' + + def set_related_server(self, team): + super(SlackSharedChannel, self).set_related_server(team) + # Fetch members here (after the team is known) since they aren't + # included in rtm.start + s = SlackRequest(team.token, 'conversations.members', {'channel': self.identifier}, team_hash=team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + + def get_history(self, slow_queue=False): + # Get info for external users in the channel + for user in self.members - set(self.team.users.keys()): + s = SlackRequest(self.team.token, 'users.info', {'user': user}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + super(SlackSharedChannel, self).get_history(slow_queue) + + def set_name(self, slack_name): + self.name = config.shared_name_prefix + slack_name + + class SlackThreadChannel(object): """ A thread channel is a virtual channel. We don't inherit from @@ -1968,11 +2020,15 @@ class SlackUser(object): Represends an individual slack user. Also where you set their name formatting. """ - def __init__(self, **kwargs): - # We require these two things for a valid object, - # the rest we can just learn from slack + def __init__(self, originating_team_id, **kwargs): self.identifier = kwargs["id"] - self.profile = {} # in case it's not in kwargs + # These attributes may be missing in the response, so we have to make + # sure they're set + self.profile = {} + self.presence = kwargs.get("presence", "unknown") + self.deleted = kwargs.get("deleted", False) + self.is_external = (not kwargs.get("is_bot") and + kwargs.get("team_id") != originating_team_id) for key, value in kwargs.items(): setattr(self, key, value) @@ -2014,8 +2070,8 @@ class SlackBot(SlackUser): Basically the same as a user, but split out to identify and for future needs """ - def __init__(self, **kwargs): - super(SlackBot, self).__init__(**kwargs) + def __init__(self, originating_team_id, **kwargs): + super(SlackBot, self).__init__(originating_team_id, is_bot=True, **kwargs) class SlackMessage(object): @@ -2048,6 +2104,15 @@ class SlackMessage(object): if message_json.get('subtype') == 'me_message' and not message_json['text'].startswith(self.sender): message_json['text'] = self.sender + ' ' + self.message_json['text'] + # Replace Slack link with direct link to shared file (required if + # shared by an external user since only the direct link is accessible) + if message_json.get('subtype') == 'file_share': + message_json['text'] = re.sub( + r'<http.+\|', + r'<{}|'.format(message_json['file']['url_private']), + message_json['text'] + ) + def __hash__(self): return hash(self.ts) @@ -2067,30 +2132,27 @@ class SlackMessage(object): def get_sender(self): name = "" name_plain = "" - if 'user' in self.message_json: - if self.message_json['user'] == self.team.myidentifier: - u = self.team.users[self.team.myidentifier] - elif self.message_json['user'] in self.team.users: - u = self.team.users[self.message_json['user']] - name = "{}".format(u.formatted_name()) - name_plain = "{}".format(u.formatted_name(enable_color=False)) + user = self.team.users.get(self.message_json.get('user')) + if user: + name = "{}".format(user.formatted_name()) + name_plain = "{}".format(user.formatted_name(enable_color=False)) + if user.is_external: + name += config.external_user_suffix + name_plain += config.external_user_suffix elif 'username' in self.message_json: - u = self.message_json["username"] + username = self.message_json["username"] if self.message_json.get("subtype") == "bot_message": - name = "{} :]".format(u) - name_plain = "{}".format(u) + name = "{} :]".format(username) + name_plain = "{}".format(username) else: - name = "-{}-".format(u) - name_plain = "{}".format(u) + name = "-{}-".format(username) + name_plain = "{}".format(username) elif 'service_name' in self.message_json: name = "-{}-".format(self.message_json["service_name"]) name_plain = "{}".format(self.message_json["service_name"]) elif self.message_json.get('bot_id') in self.team.bots: name = "{} :]".format(self.team.bots[self.message_json["bot_id"]].formatted_name()) name_plain = "{}".format(self.team.bots[self.message_json["bot_id"]].formatted_name(enable_color=False)) - else: - name = "" - name_plain = "" return (name, name_plain) def add_reaction(self, reaction, user): @@ -2205,15 +2267,18 @@ def handle_rtmstart(login_data, eventrouter): users = {} for item in login_data["users"]: - users[item["id"]] = SlackUser(**item) + users[item["id"]] = SlackUser(login_data['team']['id'], **item) bots = {} for item in login_data["bots"]: - bots[item["id"]] = SlackBot(**item) + bots[item["id"]] = SlackBot(login_data['team']['id'], **item) channels = {} for item in login_data["channels"]: - channels[item["id"]] = SlackChannel(eventrouter, **item) + if item["is_shared"]: + channels[item["id"]] = SlackSharedChannel(eventrouter, **item) + else: + channels[item["id"]] = SlackChannel(eventrouter, **item) for item in login_data["ims"]: channels[item["id"]] = SlackDMChannel(eventrouter, users, **item) @@ -2228,7 +2293,7 @@ def handle_rtmstart(login_data, eventrouter): eventrouter, metadata.token, login_data['url'], - login_data["team"]["domain"], + login_data["team"], login_data["self"]["name"], login_data["self"]["id"], users, @@ -2310,6 +2375,10 @@ def handle_mpimhistory(message_json, eventrouter, **kwargs): handle_history(message_json, eventrouter, **kwargs) +def handle_conversationshistory(message_json, eventrouter, **kwargs): + handle_history(message_json, eventrouter, **kwargs) + + def handle_history(message_json, eventrouter, **kwargs): request_metadata = pickle.loads(message_json["wee_slack_request_metadata"]) kwargs['team'] = eventrouter.teams[request_metadata.team_hash] @@ -2326,6 +2395,29 @@ def handle_history(message_json, eventrouter, **kwargs): process_message(message, eventrouter, **kwargs) +def handle_conversationsmembers(members_json, eventrouter, **kwargs): + request_metadata = pickle.loads(members_json['wee_slack_request_metadata']) + team = eventrouter.teams[request_metadata.team_hash] + channel = team.channels[request_metadata.channel_identifier] + channel.members = set(members_json['members']) + + +def handle_usersinfo(user_json, eventrouter, **kwargs): + request_metadata = pickle.loads(user_json['wee_slack_request_metadata']) + team = eventrouter.teams[request_metadata.team_hash] + channel = team.channels[request_metadata.channel_identifier] + user_info = user_json['user'] + user = SlackUser(team.identifier, **user_info) + team.users[user_info['id']] = user + + if channel.type == 'shared': + channel.update_nicklist(user_info['id']) + elif channel.type == 'im': + channel.slack_name = user.name + channel.set_topic(create_user_status_string(user.profile)) + + + ###### New/converted process_ and subprocess_ methods def process_hello(message_json, eventrouter, **kwargs): kwargs['team'].subscribe_users_presence() @@ -2386,7 +2478,7 @@ def process_user_typing(message_json, eventrouter, **kwargs): def process_team_join(message_json, eventrouter, **kwargs): user = message_json['user'] team = kwargs["team"] - team.users[user["id"]] = SlackUser(**user) + team.users[user["id"]] = SlackUser(team.identifier, **user) def process_pong(message_json, eventrouter, **kwargs): @@ -2889,11 +2981,12 @@ def resolve_ref(ref): e = EVENTROUTER if ref.startswith('@U') or ref.startswith('@W'): for t in e.teams.keys(): - if ref[1:] in e.teams[t].users: - # try: - return "@{}".format(e.teams[t].users[ref[1:]].name) - # except: - # dbg("NAME: {}".format(ref)) + user = e.teams[t].users.get(ref[1:]) + if user: + name = '@{}'.format(user.name) + if user.is_external: + name += config.external_user_suffix + return name elif ref.startswith('#C'): for t in e.teams.keys(): if ref[1:] in e.teams[t].channels: @@ -3732,6 +3825,9 @@ class PluginConfig(object): 'distracting_channels': Setting( default='', desc='List of channels to hide.'), + 'external_user_suffix': Setting( + default='*', + desc='The suffix appended to nicks to indicate external users.'), 'group_name_prefix': Setting( default='&', desc='The prefix of buffer names for groups (private channels).'), @@ -3763,6 +3859,9 @@ class PluginConfig(object): ' will be used instead of the actual name of the slack (in buffer' ' names, logging, etc). E.g `work:no_fun_allowed` would make your' ' work slack show up as `no_fun_allowed` rather than `work.slack.com`.'), + 'shared_name_prefix': Setting( + default='%', + desc='The prefix of buffer names for shared channels.'), 'short_buffer_names': Setting( default='false', desc='Use `foo.#channel` rather than `foo.slack.com.#channel` as the' @@ -3862,10 +3961,12 @@ class PluginConfig(object): return w.config_get_plugin(key) == default get_debug_level = get_int + get_external_user_suffix = get_string get_group_name_prefix = get_string get_map_underline_to = get_string get_render_bold_as = get_string get_render_italic_as = get_string + get_shared_name_prefix = get_string get_slack_timeout = get_int get_thread_suffix_color = get_string get_unfurl_auto_link_display = get_string |