Reversible = object
try:
- from urllib.parse import quote, urlencode
+ from urllib.parse import quote, unquote, urlencode
except ImportError:
- from urllib import quote, urlencode
+ from urllib import quote, unquote, urlencode
try:
JSONDecodeError = json.JSONDecodeError
SCRIPT_NAME = "slack"
SCRIPT_AUTHOR = "Ryan Huber <rhuber@gmail.com>"
-SCRIPT_VERSION = "2.8.0"
+SCRIPT_VERSION = "2.9.0"
SCRIPT_LICENSE = "MIT"
SCRIPT_DESC = "Extends weechat for typing notification/search/etc on slack.com"
REPO_URL = "https://github.com/wee-slack/wee-slack"
"join": "conversations.open",
"leave": "conversations.close",
"mark": "conversations.mark",
+ "info": "conversations.info",
},
"mpim": {
"history": "conversations.history",
return decode_from_utf8(orig_attr)
# Ensure all lines sent to weechat specifies a prefix. For lines after the
- # first, we want to disable the prefix, which is done by specifying a space.
+ # first, we want to disable the prefix, which we do by specifying the same
+ # number of spaces, so it aligns correctly.
def prnt_date_tags(self, buffer, date, tags, message):
- message = message.replace("\n", "\n \t")
+ prefix, _, _ = message.partition("\t")
+ prefix = weechat.string_remove_color(encode_to_utf8(prefix), "")
+ prefix_spaces = " " * weechat.strlen_screen(prefix)
+ message = message.replace("\n", "\n{}\t".format(prefix_spaces))
return self.wrap_for_utf8(self.wrapped_class.prnt_date_tags)(
buffer, date, tags, message
)
return "".join(decode_from_utf8(traceback.format_exception_only(etype, value)))
+def url_encode_if_not_encoded(value):
+ decoded = unquote(value)
+ is_encoded = value != decoded
+ if is_encoded:
+ return value
+ else:
+ return quote(value)
+
+
def get_localvar_type(slack_type):
if slack_type in ("im", "mpim"):
return "private"
)
team.set_disconnected()
if not team.connected:
- team.connect(reconnect=True)
+ team.connect()
dbg("reconnecting {}".format(team))
@utf8_decode
message_json["wee_slack_metadata_team"] = team
self.receive(message_json)
+ def http_check_ratelimited(self, request_metadata, response):
+ headers_end_index = response.index("\r\n\r\n")
+ headers = response[:headers_end_index].split("\r\n")
+ http_status = headers[0].split(" ")[1]
+
+ if http_status == "429":
+ for header in headers[1:]:
+ name, value = header.split(":", 1)
+ if name.lower() == "retry-after":
+ retry_after = int(value.strip())
+ request_metadata.retry_time = time.time() + retry_after
+ return "", "ratelimited"
+
+ body = response[headers_end_index + 4 :]
+ return body, ""
+
+ def retry_request(self, request_metadata, data, return_code, err):
+ self.reply_buffer.pop(request_metadata.response_id, None)
+ self.delete_context(data)
+ retry_text = (
+ "retrying"
+ if request_metadata.should_try()
+ else "will not retry after too many failed attempts"
+ )
+ team = (
+ "for team {}".format(request_metadata.team)
+ if request_metadata.team
+ else "with token {}".format(token_for_print(request_metadata.token))
+ )
+ w.prnt(
+ "",
+ (
+ "Failed requesting {} {}, {}. "
+ + "If this persists, try increasing slack_timeout. Error (code {}): {}"
+ ).format(
+ request_metadata.request,
+ team,
+ retry_text,
+ return_code,
+ err,
+ ),
+ )
+ dbg(
+ "{} failed with return_code {} and error {}. stack:\n{}".format(
+ request_metadata.request,
+ return_code,
+ err,
+ "".join(traceback.format_stack()),
+ ),
+ level=5,
+ )
+ self.receive(request_metadata)
+
@utf8_decode
def receive_httprequest_callback(self, data, command, return_code, out, err):
"""
if request_metadata.response_id not in self.reply_buffer:
self.reply_buffer[request_metadata.response_id] = StringIO()
self.reply_buffer[request_metadata.response_id].write(out)
- try:
- j = json.loads(
- self.reply_buffer[request_metadata.response_id].getvalue()
- )
- except:
- pass
- # dbg("Incomplete json, awaiting more", True)
- try:
- j["wee_slack_process_method"] = request_metadata.request_normalized
- if self.recording:
- self.record_event(
- j, request_metadata.team, "wee_slack_process_method", "http"
- )
- j["wee_slack_request_metadata"] = request_metadata
- self.reply_buffer.pop(request_metadata.response_id)
- self.receive(j)
- self.delete_context(data)
- except:
- dbg("HTTP REQUEST CALLBACK FAILED", True)
+
+ response = self.reply_buffer[request_metadata.response_id].getvalue()
+ body, error = self.http_check_ratelimited(request_metadata, response)
+ if error:
+ self.retry_request(request_metadata, data, return_code, error)
+ else:
+ j = json.loads(body)
+
+ try:
+ j[
+ "wee_slack_process_method"
+ ] = request_metadata.request_normalized
+ if self.recording:
+ self.record_event(
+ j,
+ request_metadata.team,
+ "wee_slack_process_method",
+ "http",
+ )
+ j["wee_slack_request_metadata"] = request_metadata
+ self.reply_buffer.pop(request_metadata.response_id)
+ self.receive(j)
+ self.delete_context(data)
+ except:
+ dbg("HTTP REQUEST CALLBACK FAILED", True)
# We got an empty reply and this is weird so just ditch it and retry
else:
dbg("length was zero, probably a bug..")
self.reply_buffer[request_metadata.response_id] = StringIO()
self.reply_buffer[request_metadata.response_id].write(out)
else:
- self.reply_buffer.pop(request_metadata.response_id, None)
- self.delete_context(data)
- if request_metadata.request.startswith("rtm."):
- retry_text = (
- "retrying"
- if request_metadata.should_try()
- else "will not retry after too many failed attempts"
- )
- w.prnt(
- "",
- (
- "Failed connecting to slack team with token {}, {}. "
- + "If this persists, try increasing slack_timeout. Error (code {}): {}"
- ).format(
- token_for_print(request_metadata.token),
- retry_text,
- return_code,
- err,
- ),
- )
- dbg(
- "rtm.start failed with return_code {}. stack:\n{}".format(
- return_code, "".join(traceback.format_stack())
- ),
- level=5,
- )
- self.receive(request_metadata)
+ self.retry_request(request_metadata, data, return_code, err)
return w.WEECHAT_RC_OK
def receive(self, dataobj, slow=False):
team = request.team
channel = request.channel
metadata = request.metadata
+ callback = request.callback
else:
team = j.get("wee_slack_metadata_team")
channel = None
metadata = {}
+ callback = None
if team:
if "channel" in j:
metadata["user"] = team.users.get(user_id)
dbg("running {}".format(function_name))
- if (
+ if callable(callback):
+ callback(j, self, team, channel, metadata)
+ elif (
function_name.startswith("local_")
and function_name in self.local_proc
):
random.choice(string.ascii_uppercase + string.digits) for _ in range(4)
)
)
- params = {"useragent": "wee_slack {}".format(SCRIPT_VERSION)}
request.tried()
+ options = request.options()
+ options["header"] = "1"
context = event_router.store_context(request)
- # TODO: let flashcode know about this bug - i have to 'clear' the hashtable or retry requests fail
- w.hook_process_hashtable("url:", params, config.slack_timeout, "", context)
w.hook_process_hashtable(
weechat_request,
- params,
+ options,
config.slack_timeout,
"receive_httprequest_callback",
context,
metadata=None,
retries=3,
token=None,
+ cookies=None,
+ callback=None,
):
if team is None and token is None:
raise ValueError("Both team and token can't be None")
self.channel = channel
self.metadata = metadata if metadata else {}
self.retries = retries
+ self.retry_time = 0
self.token = token if token else team.token
+ self.cookies = cookies or {}
+ if ":" in self.token:
+ token, cookie = self.token.split(":", 1)
+ self.token = token
+ if cookie.startswith("d="):
+ for name, value in [c.split("=") for c in cookie.split(";")]:
+ self.cookies[name] = value
+ else:
+ self.cookies["d"] = cookie
+ self.callback = callback
+ self.domain = "api.slack.com"
+ self.reset()
+
+ def reset(self):
self.tries = 0
self.start_time = time.time()
- self.request_normalized = re.sub(r"\W+", "", request)
- self.domain = "api.slack.com"
- self.post_data["token"] = self.token
+ self.request_normalized = re.sub(r"\W+", "", self.request)
self.url = "https://{}/api/{}?{}".format(
self.domain, self.request, urlencode(encode_to_utf8(self.post_data))
)
- self.params = {"useragent": "wee_slack {}".format(SCRIPT_VERSION)}
self.response_id = sha1_hex("{}{}".format(self.url, self.start_time))
def __repr__(self):
return (
"SlackRequest(team={}, request='{}', post_data={}, retries={}, token='{}', "
- "tries={}, start_time={})"
+ "cookies={}, tries={}, start_time={})"
).format(
self.team,
self.request,
self.post_data,
self.retries,
token_for_print(self.token),
+ self.cookies,
self.tries,
self.start_time,
)
def request_string(self):
return "{}".format(self.url)
+ def options(self):
+ cookies = "; ".join(
+ [
+ "{}={}".format(key, url_encode_if_not_encoded(value))
+ for key, value in self.cookies.items()
+ ]
+ )
+ return {
+ "useragent": "wee_slack {}".format(SCRIPT_VERSION),
+ "httpheader": "Authorization: Bearer {}".format(self.token),
+ "cookie": cookies,
+ }
+
+ def options_as_cli_args(self):
+ options = self.options()
+ options["user-agent"] = options.pop("useragent")
+ httpheader = options.pop("httpheader")
+ headers = [": ".join(x) for x in options.items()] + httpheader.split("\n")
+ return ["-H{}".format(header) for header in headers]
+
def tried(self):
self.tries += 1
self.response_id = sha1_hex("{}{}".format(self.url, time.time()))
return self.tries < self.retries
def retry_ready(self):
- return (self.start_time + (self.tries ** 2)) < time.time()
+ if self.retry_time:
+ return time.time() > self.retry_time
+ else:
+ return (self.start_time + (self.tries**2)) < time.time()
class SlackSubteam(object):
users,
bots,
channels,
- **kwargs,
+ **kwargs
):
self.slack_api_translator = copy.deepcopy(SLACK_API_TRANSLATOR)
self.identifier = team_info["id"]
def mark_read(self, ts=None, update_remote=True, force=False):
pass
- def connect(self, reconnect=False):
+ def connect(self):
if not self.connected and not self.connecting_ws:
if self.ws_url:
self.connecting_ws = True
# The fast reconnect failed, so start over-ish
for chan in self.channels:
self.channels[chan].history_needs_update = True
- s = initiate_connection(
- self.token, retries=999, team=self, reconnect=reconnect
- )
+ s = get_rtm_connect_request(self.token, retries=999, team=self)
self.eventrouter.receive(s)
self.connecting_rtm = True
self.create_buffer()
return
- # Only check is_member if is_open is not set, because in some cases
- # (e.g. group DMs), is_member should be ignored in favor of is_open.
- is_open = self.is_open if hasattr(self, "is_open") else self.is_member
- if is_open or self.unread_count_display:
+ if (
+ getattr(self, "is_open", False)
+ or self.unread_count_display
+ or self.type not in ["im", "mpim"]
+ and getattr(self, "is_member", False)
+ ):
self.create_buffer()
+ elif self.type in ["im", "mpim"]:
+ # If it is an IM or MPIM, we still might want to open it if there are unread messages.
+ info_method = self.team.slack_api_translator[self.type].get("info")
+ if info_method:
+ s = SlackRequest(
+ self.team, info_method, {"channel": self.identifier}, channel=self
+ )
+ self.eventrouter.receive(s)
def set_related_server(self, team):
self.team = team
config.unhide_buffers_with_activity
and not self.is_visible()
and not self.muted
+ and not no_log
):
w.buffer_set(self.channel_buffer, "hidden", "0")
self.pending_history_requests.add(self.identifier)
self.get_members()
- post_data = {"channel": self.identifier, "count": config.history_fetch_count}
+ post_data = {"channel": self.identifier, "limit": config.history_fetch_count}
if self.got_history and self.messages and not full:
post_data["oldest"] = next(reversed(self.messages))
info_method = self.team.slack_api_translator[self.type].get("info")
if info_method:
s = SlackRequest(
- self.team, info_method, {"name": self.identifier}, channel=self
+ self.team, info_method, {"channel": self.identifier}, channel=self
)
self.eventrouter.receive(s)
if update_remote:
"""
def __init__(self, eventrouter, team_users, myidentifier, **kwargs):
- if "members" in kwargs:
+ if kwargs.get("members"):
kwargs["name"] = self.name_from_members(
team_users, kwargs["members"], myidentifier
)
"""
def __init__(self, originating_team_id, **kwargs):
- super(SlackBot, self).__init__(originating_team_id, is_bot=True, **kwargs)
+ super(SlackBot, self).__init__(originating_team_id, **kwargs)
class SlackMessage(object):
if self.message_json.get("mrkdwn", True):
text = render_formatting(text)
- if (
- self.message_json.get("subtype")
- in (
- "channel_join",
- "group_join",
- )
- and self.message_json.get("inviter")
- ):
+ if self.message_json.get("subtype") in (
+ "channel_join",
+ "group_join",
+ ) and self.message_json.get("inviter"):
inviter_id = self.message_json.get("inviter")
text += " by invitation from <@{}>".format(inviter_id)
t.set_reconnect_url(login_data["url"])
t.connecting_rtm = False
- t.connect(metadata.metadata["reconnect"])
+ t.connect()
def handle_rtmconnect(login_data, eventrouter, team, channel, metadata):
return
team.set_reconnect_url(login_data["url"])
- team.connect(metadata.metadata["reconnect"])
+ team.connect()
def handle_emojilist(emoji_json, eventrouter, team, channel, metadata):
team.emoji_completions.extend(emoji_json["emoji"].keys())
-def handle_channelsinfo(channel_json, eventrouter, team, channel, metadata):
- channel.set_unread_count_display(
- channel_json["channel"].get("unread_count_display", 0)
- )
- channel.set_members(channel_json["channel"]["members"])
-
-
-def handle_groupsinfo(group_json, eventrouter, team, channel, metadatas):
- channel.set_unread_count_display(group_json["group"].get("unread_count_display", 0))
- channel.set_members(group_json["group"]["members"])
+def handle_conversationsinfo(channel_json, eventrouter, team, channel, metadata):
+ channel_info = channel_json["channel"]
+ if "unread_count_display" in channel_info:
+ unread_count = channel_info["unread_count_display"]
+ if unread_count and channel.channel_buffer is None:
+ channel.create_buffer()
+ channel.set_unread_count_display(unread_count)
+ if "last_read" in channel_info:
+ channel.last_read = SlackTS(channel_info["last_read"])
+ if "members" in channel_info:
+ channel.set_members(channel_info["members"])
+
+ # MPIMs don't have unread_count_display so we have to request the history to check if there are unread messages
+ if channel.type == "mpim" and not channel.channel_buffer:
+ s = SlackRequest(
+ team,
+ "conversations.history",
+ {"channel": channel.identifier, "limit": 1},
+ channel=channel,
+ metadata={"only_set_unread": True},
+ )
+ eventrouter.receive(s)
def handle_conversationsopen(
conversation_json, eventrouter, team, channel, metadata, object_name="channel"
):
- # Set unread count if the channel isn't new
- if channel:
- unread_count_display = conversation_json[object_name].get(
- "unread_count_display", 0
+ channel_info = conversation_json[object_name]
+ if not channel:
+ channel = create_channel_from_info(
+ eventrouter, channel_info, team, team.myidentifier, team.users
)
+ team.channels[channel_info["id"]] = channel
+
+ if channel.channel_buffer is None:
+ channel.create_buffer()
+
+ unread_count_display = channel_info.get("unread_count_display")
+ if unread_count_display is not None:
channel.set_unread_count_display(unread_count_display)
+ if metadata.get("switch") and config.switch_buffer_on_join:
+ w.buffer_set(channel.channel_buffer, "display", "1")
+
def handle_mpimopen(
mpim_json, eventrouter, team, channel, metadata, object_name="group"
def handle_history(
message_json, eventrouter, team, channel, metadata, includes_threads=True
):
+ if metadata.get("only_set_unread"):
+ if message_json["messages"]:
+ latest = message_json["messages"][0]
+ latest_ts = SlackTS(latest["ts"])
+ if latest_ts > channel.last_read:
+ if not channel.channel_buffer:
+ channel.create_buffer()
+ channel.set_unread_count_display(1)
+ return
+
channel.got_history = True
channel.history_needs_update = False
for message in reversed(message_json["messages"]):
def process_message(
message_json, eventrouter, team, channel, metadata, history_message=False
):
+ subtype = message_json.get("subtype")
if (
not history_message
+ and not subtype
and "ts" in message_json
and SlackTS(message_json["ts"]) in channel.messages
):
return
- subtype = message_json.get("subtype")
subtype_functions = get_functions_with_prefix("subprocess_")
if "thread_ts" in message_json and "reply_count" not in message_json:
for fileout in fileout_iter(os.path.join(download_location, filename)):
if os.path.isfile(fileout):
continue
+ curl_options = SlackRequest(channel.team, "").options()
+ curl_options["file_out"] = fileout
w.hook_process_hashtable(
"url:" + f["url_private"],
- {
- "file_out": fileout,
- "httpheader": "Authorization: Bearer " + channel.team.token,
- },
+ curl_options,
config.slack_timeout,
"",
"",
for element in block["elements"]:
if element["type"] == "button":
elements.append(unfurl_block_element(element["text"]))
+ if "url" in element:
+ elements.append(element["url"])
else:
elements.append(
colorize_string(
):
text = "{} ({})".format(f["url_private"], f["title"])
else:
- dbg("File {} has unrecognized mode {}".format(f["id"], f["mode"]), 5)
+ dbg("File {} has unrecognized mode {}".format(f["id"], f.get("mode")), 5)
text = colorize_string(
config.color_deleted, "(This file cannot be handled.)"
)
"client_id={}&client_secret={}&redirect_uri={}&code={}"
).format(CLIENT_ID, CLIENT_SECRET, redirect_uri, code)
params = {"useragent": "wee_slack {}".format(SCRIPT_VERSION)}
- w.hook_process_hashtable("url:", params, config.slack_timeout, "", "")
w.hook_process_hashtable(
"url:{}".format(uri), params, config.slack_timeout, "register_callback", ""
)
team,
team.slack_api_translator[channel_type]["join"],
{"users": ",".join(users)},
+ metadata={"switch": True},
)
EVENTROUTER.receive(s)
if isinstance(channel, SlackThreadChannel):
post_data["thread_ts"] = channel.thread_ts
- url = SlackRequest(
- channel.team, "files.upload", post_data, channel=channel
- ).request_string()
- options = ["-s", "-Ffile=@{}".format(file_path), url]
+ request = SlackRequest(channel.team, "files.upload", post_data, channel=channel)
+ options = request.options_as_cli_args() + [
+ "-s",
+ "-Ffile=@{}".format(file_path),
+ request.request_string(),
+ ]
proxy_string = ProxyWrapper().curl()
if proxy_string:
return
-def initiate_connection(token, retries=3, team=None, reconnect=False):
- request_type = "connect" if team else "start"
- post_data = {"batch_presence_aware": 1}
- if request_type == "start":
- post_data["mpim_aware"] = "true"
+def get_rtm_connect_request(token, retries=3, team=None, callback=None):
return SlackRequest(
team,
- "rtm.{}".format(request_type),
- post_data,
+ "rtm.connect",
+ {"batch_presence_aware": 1},
retries=retries,
token=token,
- metadata={"reconnect": reconnect},
+ callback=callback,
)
+def get_next_page(response_json):
+ next_cursor = response_json.get("response_metadata", {}).get("next_cursor")
+ if next_cursor:
+ request = response_json["wee_slack_request_metadata"]
+ request.post_data["cursor"] = next_cursor
+ request.reset()
+ EVENTROUTER.receive(request)
+ return True
+ else:
+ return False
+
+
+def initiate_connection(token):
+ initial_data = {
+ "channels": [],
+ "members": [],
+ "usergroups": [],
+ "remaining": {
+ "channels": 2,
+ "members": 1,
+ "usergroups": 1,
+ "prefs": 1,
+ "presence": 1,
+ },
+ "errors": [],
+ }
+
+ def handle_initial(data_type):
+ def handle(response_json, eventrouter, team, channel, metadata):
+ if not response_json["ok"]:
+ if response_json["error"] == "user_is_restricted":
+ w.prnt(
+ "",
+ "You are a restricted user in this team, "
+ "{} not loaded".format(data_type),
+ )
+ else:
+ initial_data["errors"].append(
+ "{}: {}".format(data_type, response_json["error"])
+ )
+ initial_data["remaining"][data_type] -= 1
+ create_team(token, initial_data)
+ return
+
+ initial_data[data_type].extend(response_json[data_type])
+
+ if not get_next_page(response_json):
+ initial_data["remaining"][data_type] -= 1
+ create_team(token, initial_data)
+
+ return handle
+
+ def handle_prefs(response_json, eventrouter, team, channel, metadata):
+ if not response_json["ok"]:
+ initial_data["errors"].append("prefs: {}".format(response_json["error"]))
+ initial_data["remaining"]["prefs"] -= 1
+ create_team(token, initial_data)
+ return
+
+ initial_data["prefs"] = response_json["prefs"]
+ initial_data["remaining"]["prefs"] -= 1
+ create_team(token, initial_data)
+
+ def handle_getPresence(response_json, eventrouter, team, channel, metadata):
+ if not response_json["ok"]:
+ initial_data["errors"].append("presence: {}".format(response_json["error"]))
+ initial_data["remaining"]["presence"] -= 1
+ create_team(token, initial_data)
+ return
+
+ initial_data["presence"] = response_json
+ initial_data["remaining"]["presence"] -= 1
+ create_team(token, initial_data)
+
+ s = SlackRequest(
+ None,
+ "conversations.list",
+ {
+ "exclude_archived": True,
+ "types": "public_channel,private_channel,im",
+ "limit": 1000,
+ },
+ token=token,
+ callback=handle_initial("channels"),
+ )
+ EVENTROUTER.receive(s)
+ s = SlackRequest(
+ None,
+ "conversations.list",
+ {
+ "exclude_archived": True,
+ "types": "mpim",
+ "limit": 1000,
+ },
+ token=token,
+ callback=handle_initial("channels"),
+ )
+ EVENTROUTER.receive(s)
+ s = SlackRequest(
+ None,
+ "users.list",
+ {"limit": 1000},
+ token=token,
+ callback=handle_initial("members"),
+ )
+ EVENTROUTER.receive(s)
+ s = SlackRequest(
+ None,
+ "usergroups.list",
+ {"include_users": True},
+ token=token,
+ callback=handle_initial("usergroups"),
+ )
+ EVENTROUTER.receive(s)
+ s = SlackRequest(
+ None,
+ "users.prefs.get",
+ token=token,
+ callback=handle_prefs,
+ )
+ EVENTROUTER.receive(s)
+ s = SlackRequest(
+ None,
+ "users.getPresence",
+ token=token,
+ callback=handle_getPresence,
+ )
+ EVENTROUTER.receive(s)
+
+
+def create_channel_from_info(eventrouter, channel_info, team, myidentifier, users):
+ if channel_info.get("is_im"):
+ return SlackDMChannel(eventrouter, users, team=team, **channel_info)
+ elif channel_info.get("is_mpim"):
+ return SlackMPDMChannel(
+ eventrouter, users, myidentifier, team=team, **channel_info
+ )
+ elif channel_info.get("is_shared"):
+ return SlackSharedChannel(eventrouter, team=team, **channel_info)
+ elif channel_info.get("is_private"):
+ return SlackPrivateChannel(eventrouter, team=team, **channel_info)
+ else:
+ return SlackChannel(eventrouter, team=team, **channel_info)
+
+
+def create_team(token, initial_data):
+ if not any(initial_data["remaining"].values()):
+ if initial_data["errors"]:
+ w.prnt(
+ "",
+ "ERROR: Failed connecting to Slack with token {}: {}".format(
+ token_for_print(token), ", ".join(initial_data["errors"])
+ ),
+ )
+ if not re.match(r"^xo\w\w(-\d+){3}-[0-9a-f]+(:.*)?$", token):
+ w.prnt(
+ "",
+ "ERROR: Token does not look like a valid Slack token. "
+ "Ensure it is a valid token and not just a OAuth code.",
+ )
+
+ return
+
+ def handle_rtmconnect(response_json, eventrouter, team, channel, metadata):
+ if not response_json["ok"]:
+ print(response_json["error"])
+ return
+
+ team_id = response_json["team"]["id"]
+ myidentifier = response_json["self"]["id"]
+
+ users = {}
+ bots = {}
+ for member in initial_data["members"]:
+ if member.get("is_bot"):
+ bots[member["id"]] = SlackBot(team_id, **member)
+ else:
+ users[member["id"]] = SlackUser(team_id, **member)
+
+ self_nick = nick_from_profile(
+ users[myidentifier].profile, response_json["self"]["name"]
+ )
+
+ channels = {}
+ for channel_info in initial_data["channels"]:
+ channels[channel_info["id"]] = create_channel_from_info(
+ eventrouter, channel_info, None, myidentifier, users
+ )
+
+ subteams = {}
+ for usergroup in initial_data["usergroups"]:
+ is_member = myidentifier in usergroup["users"]
+ subteams[usergroup["id"]] = SlackSubteam(
+ team_id, is_member=is_member, **usergroup
+ )
+
+ manual_presence = (
+ "away" if initial_data["presence"]["manual_away"] else "active"
+ )
+
+ team_info = {
+ "id": team_id,
+ "name": response_json["team"]["id"],
+ "domain": response_json["team"]["domain"],
+ }
+
+ team_hash = SlackTeam.generate_team_hash(
+ team_id, response_json["team"]["domain"]
+ )
+ if not eventrouter.teams.get(team_hash):
+ team = SlackTeam(
+ eventrouter,
+ token,
+ team_hash,
+ response_json["url"],
+ team_info,
+ subteams,
+ self_nick,
+ myidentifier,
+ manual_presence,
+ users,
+ bots,
+ channels,
+ muted_channels=initial_data["prefs"]["muted_channels"],
+ highlight_words=initial_data["prefs"]["highlight_words"],
+ )
+ eventrouter.register_team(team)
+ team.connect()
+ else:
+ team = eventrouter.teams.get(team_hash)
+ if team.myidentifier != myidentifier:
+ print_error(
+ "The Slack team {} has tokens for two different users, this is not supported. The "
+ "token {} is for user {}, and the token {} is for user {}. Please remove one of "
+ "them.".format(
+ team.team_info["name"],
+ token_for_print(team.token),
+ team.nick,
+ token_for_print(token),
+ self_nick,
+ )
+ )
+ else:
+ print_error(
+ "Ignoring duplicate Slack tokens for the same team ({}) and user ({}). The two "
+ "tokens are {} and {}.".format(
+ team.team_info["name"],
+ team.nick,
+ token_for_print(team.token),
+ token_for_print(token),
+ ),
+ warning=True,
+ )
+
+ s = get_rtm_connect_request(token, callback=handle_rtmconnect)
+ EVENTROUTER.receive(s)
+
+
if __name__ == "__main__":
w = WeechatWrapper(weechat)
auto_connect = weechat.info_get("auto_connect", "") != "0"
if auto_connect:
- tokens = [token.strip() for token in config.slack_api_token.split(",")]
+ tokens = [
+ token.strip()
+ for token in config.slack_api_token.split(",")
+ if token
+ ]
w.prnt(
"",
"Connecting to {} slack team{}.".format(
),
)
for t in tokens:
- s = initiate_connection(t)
- EVENTROUTER.receive(s)
+ if t.startswith("xoxc-") and ":" not in t:
+ w.prnt(
+ "",
+ "{}When using an xoxc token, you need to also provide the d cookie in the format token:cookie".format(
+ w.prefix("error")
+ ),
+ )
+ else:
+ initiate_connection(t)
EVENTROUTER.handle_next()