aboutsummaryrefslogtreecommitdiffstats
path: root/wee_slack.py
diff options
context:
space:
mode:
authorEric Wang <gnawrice@gmail.com>2017-10-27 00:02:57 -0700
committerTrygve Aaberge <trygveaa@gmail.com>2018-06-07 14:45:55 +0200
commit69a19eae35eac1e9960b7a33c589cf22cd7d4aea (patch)
treefcb0113083435a1fe3ef03749dfa854c7a28a944 /wee_slack.py
parent6df61f330460316db2a72e770214b1ba82c3cb56 (diff)
downloadwee-slack-69a19eae35eac1e9960b7a33c589cf22cd7d4aea.tar.gz
Add support for shared channels
Since shared channels only work with the new conversations API, a new class of SlackChannel and API type were added to support them. Also `rtm.start` doesn't include external users so their information needs to be fetched individually when getting the channel history. To distinguish external users from team members their nicks get appended with an asterisk, which can be changed with a new setting. They also appear in a new "External" group in the nicklist since their presences can't be fetched with `users.getPresence`. Unfortunately `conversations.history` doesn't include thread messages so threads won't show up in shared channels.
Diffstat (limited to 'wee_slack.py')
-rw-r--r--wee_slack.py117
1 files changed, 103 insertions, 14 deletions
diff --git a/wee_slack.py b/wee_slack.py
index 9262e7f..f6448ce 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):
@@ -981,6 +989,7 @@ class SlackTeam(object):
except:
self.channels = channels
self.users = users
+ self.external_users = {}
self.bots = bots
self.team_hash = SlackTeam.generate_team_hash(self.nick, self.subdomain)
self.name = self.domain
@@ -1071,6 +1080,12 @@ class SlackTeam(object):
def get_username_map(self):
return {v.name: k for k, v in self.users.iteritems()}
+ def get_user(self, user_id):
+ if user_id in self.users:
+ return self.users[user_id]
+ elif user_id in self.external_users:
+ return self.external_users[user_id]
+
def get_team_hash(self):
return self.team_hash
@@ -1248,6 +1263,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 = {
@@ -1552,7 +1569,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
@@ -1564,16 +1581,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.get_user(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)
@@ -1583,11 +1609,13 @@ class SlackChannel(object):
if len(self.members) < 1000:
try:
for user in self.members:
- user = self.team.users[user]
+ user = self.team.get_user(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:
@@ -1770,6 +1798,30 @@ 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
+ all_users = set(self.team.users.keys()) | set(self.team.external_users.keys())
+ for user in self.members - all_users:
+ 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
@@ -1923,6 +1975,7 @@ class SlackUser(object):
# the rest we can just learn from slack
self.identifier = kwargs["id"]
self.profile = {} # in case it's not in kwargs
+ self.is_external = False
for key, value in kwargs.items():
setattr(self, key, value)
@@ -2020,10 +2073,13 @@ class SlackMessage(object):
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']]
+ else:
+ u = self.team.get_user(self.message_json['user'])
name = "{}".format(u.formatted_name())
name_plain = "{}".format(u.formatted_name(enable_color=False))
+ if u.is_external:
+ name += config.external_user_suffix
+ name_plain += config.external_user_suffix
elif 'username' in self.message_json:
u = self.message_json["username"]
if self.message_json.get("subtype") == "bot_message":
@@ -2163,7 +2219,10 @@ def handle_rtmstart(login_data, eventrouter):
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)
@@ -2260,6 +2319,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]
@@ -2276,6 +2339,23 @@ 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_info.update(is_external=True, deleted=False)
+ team.external_users[user_info['id']] = SlackUser(**user_info)
+ channel.update_nicklist(user_info['id'])
+
+
###### New/converted process_ and subprocess_ methods
def process_hello(message_json, eventrouter, **kwargs):
kwargs['team'].subscribe_users_presence()
@@ -2834,11 +2914,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].get_user(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:
@@ -3666,6 +3747,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).'),
@@ -3697,6 +3781,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'
@@ -3796,10 +3883,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