From 47607a88c578f804d76c25dd2d68386556255b21 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Tue, 31 Jan 2017 17:12:43 -0800 Subject: first run - lots doesn't work yet --- wee_slack.py | 1220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1220 insertions(+) create mode 100644 wee_slack.py diff --git a/wee_slack.py b/wee_slack.py new file mode 100644 index 0000000..aed88e9 --- /dev/null +++ b/wee_slack.py @@ -0,0 +1,1220 @@ +# -*- coding: utf-8 -*- +# + +import time +import json +import pickle +import sha +import re +import urllib +import sys +import traceback +import collections +import ssl +#import random + +from websocket import create_connection, WebSocketConnectionClosedException + +# hack to make tests possible.. better way? +try: + import weechat as w +except: + pass + +SCRIPT_NAME = "slack_extension" +SCRIPT_AUTHOR = "Ryan Huber " +SCRIPT_VERSION = "1.99" +SCRIPT_LICENSE = "MIT" +SCRIPT_DESC = "Extends weechat for typing notification/search/etc on slack.com" + +BACKLOG_SIZE = 200 +SCROLLBACK_SIZE = 500 + +CACHE_VERSION = "4" + +SLACK_API_TRANSLATOR = { + "channel": { + "history": "channels.history", + "join": "channels.join", + "leave": "channels.leave", + "mark": "channels.mark", + "info": "channels.info", + }, + "im": { + "history": "im.history", + "join": "im.open", + "leave": "im.close", + "mark": "im.mark", + }, + "group": { + "history": "groups.history", + "join": "channels.join", + "leave": "groups.leave", + "mark": "groups.mark", + }, + "thread": { + "history": None, + "join": None, + "leave": None, + "mark": None, + } + + +} + +NICK_GROUP_HERE = "0|Here" +NICK_GROUP_AWAY = "1|Away" + +sslopt_ca_certs = {} +if hasattr(ssl, "get_default_verify_paths") and callable(ssl.get_default_verify_paths): + ssl_defaults = ssl.get_default_verify_paths() + if ssl_defaults.cafile is not None: + sslopt_ca_certs = {'ca_certs': ssl_defaults.cafile} + +##### BEGIN NEW + +IGNORED_EVENTS = [ + "reconnect_url", + "hello", +] + +###### New central Event router + +class EventRouter(object): + + def __init__(self): + self.queue = [] + self.teams = {} + self.weechat_buffers = {} + self.previous_buffer = "" + self.reply_buffer = {} + self.cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} + self.proc = {k[8:]: v for k, v in globals().items() if k.startswith("process_")} + self.handlers = {k[7:]: v for k, v in globals().items() if k.startswith("handle_")} + self.local_proc = {k[14:]: v for k, v in globals().items() if k.startswith("local_process_")} + + def register_team(self, team): + """ + Adds a team to the list of known teams for this EventRouter + """ + if isinstance(team, SlackTeam): + self.teams[team.get_team_hash()] = team + else: + raise InvalidType(type(team)) + + def register_weechat_buffer(self, buffer_ptr, channel): + """ + Adds a weechat buffer to the list of handled buffers for this EventRouter + """ + if isinstance(buffer_ptr, str): + self.weechat_buffers[buffer_ptr] = channel + else: + raise InvalidType(type(buffer_ptr)) + + def unregister_weechat_buffer(self, buffer_ptr): + """ + Adds a weechat buffer to the list of handled buffers for this EventRouter + """ + if isinstance(buffer_ptr, str): + try: + self.weechat_buffers[buffer_ptr].destroy_buffer() + del self.weechat_buffers[buffer_ptr] + except: + dbg("Tried to close unknown buffer") + else: + raise InvalidType(type(buffer_ptr)) + + def receive_ws_callback(self, team_hash): + """ + This is called by the global method of the same name. + It is triggered when we have incoming data on a websocket, + which needs to be read. Once it is read, we will ensure + the data is valid JSON, add metadata, and place it back + on the queue for processing as JSON. + """ + try: + # Read the data from the websocket associated with this team. + data = self.teams[team_hash].ws.recv() + message_json = json.loads(data) + metadata = WeeSlackMetadata({ + "team": team_hash, + #"channels": self.teams[team_hash].channels, + #"users": self.teams[team_hash].users, + }).jsonify() + #print self.teams[team_hash].domain + message_json["wee_slack_metadata"] = metadata + #print message_json + self.receive_json(json.dumps(message_json)) + except WebSocketConnectionClosedException: + #TODO: handle reconnect here + self.teams[team_hash].set_disconnected() + return w.WEECHAT_RC_OK + except Exception: + dbg("socket issue: {}\n".format(traceback.format_exc())) + return w.WEECHAT_RC_OK + + def receive_httprequest_callback(self, data, command, return_code, out, err): + #def url_processor_cb(data, command, return_code, out, err): + request_metadata = pickle.loads(data) + dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out)), main_buffer=True) + if return_code == 0: + if request_metadata.response_id in self.reply_buffer: + self.reply_buffer[request_metadata.response_id] += out + #print self.reply_buffer[request_metadata.response_id] + else: + self.reply_buffer[request_metadata.response_id] = "" + self.reply_buffer[request_metadata.response_id] += out + try: + j = json.loads(self.reply_buffer[request_metadata.response_id]) + j["wee_slack_process_method"] = request_metadata.request_normalized + j["wee_slack_request_metadata"] = pickle.dumps(request_metadata) + #print self.reply_buffer[request_metadata.response_id] + self.reply_buffer.pop(request_metadata.response_id) + self.receive_json(json.dumps(j)) + except: + dbg("FAILED") + pass + elif return_code != -1: + self.reply_buffer.pop(request_metadata.response_id, None) + else: + if request_metadata.response_id not in self.reply_buffer: + self.reply_buffer[request_metadata.response_id] = "" + self.reply_buffer[request_metadata.response_id] += out + + def receive_json(self, data): + dbg("RECEIVED JSON") + dbg(str(data)) + message_json = json.loads(data) + dbg(message_json) + #print message_json.keys() + self.queue.append(message_json) + def receive(self, dataobj): + dbg("RECEIVED QUEUE", main_buffer=True) + #dbg(str(len(dataobj)), main_buffer=True) + self.queue.append(dataobj) + def handle_next(self): + if len(self.queue) > 0: + j = self.queue.pop(0) + # Reply is a special case of a json reply from websocket. + kwargs = {} + if isinstance(j, SlackRequest): + if j.should_try(): + local_process_async_slack_api_request(j, self) + return + + if "reply_to" in j: + dbg("SET FROM REPLY") + function_name = "reply" + elif "type" in j: + dbg("SET FROM type") + function_name = j["type"] + elif "wee_slack_process_method" in j: + dbg("SET FROM META") + function_name = j["wee_slack_process_method"] + else: + dbg("SET FROM NADA") + function_name = "unknown" + + # Here we are passing the actual objects. No more lookups. + if "wee_slack_metadata" in j: + + if isinstance(j["wee_slack_metadata"], str): + dbg("string of metadata") + if "team" in j["wee_slack_metadata"]: + kwargs["team"] = self.teams[j["wee_slack_metadata"]["team"]] + if "user" in j: + kwargs["user"] = self.teams[j["wee_slack_metadata"]["team"]].users[j["user"]] + if "channel" in j: + kwargs["channel"] = self.teams[j["wee_slack_metadata"]["team"]].channels[j["channel"]] + + if function_name not in IGNORED_EVENTS: + dbg("running {}".format(function_name)) + if function_name.startswith("local_") and function_name in self.local_proc: + self.local_proc[function_name](j, self, **kwargs) + elif function_name in self.proc: + self.proc[function_name](j, self, **kwargs) + elif function_name in self.handlers: + self.handlers[function_name](j, self, **kwargs) + else: + raise ProcessNotImplemented(function_name) + +def handle_next(*args): + EVENTROUTER.handle_next() + return w.WEECHAT_RC_OK + +###### New Local Processors + +def local_process_async_slack_api_request(request, event_router): + """ + Sends an API request to Slack. You'll need to give this a well formed SlackRequest object. + """ + weechat_request = 'url:{}'.format(request.request_string()) + params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} + request.tried() + context = pickle.dumps(request) + w.hook_process_hashtable(weechat_request, params, config.slack_timeout, "receive_httprequest_callback", context) + +###### New Callbacks + +def receive_httprequest_callback(data, command, return_code, out, err): + """ + This is a dirty hack. There must be a better way. + """ + #def url_processor_cb(data, command, return_code, out, err): + EVENTROUTER.receive_httprequest_callback(data, command, return_code, out, err) + return w.WEECHAT_RC_OK + +def receive_ws_callback(*args): + """ + The first arg is all we want here. It contains the team + hash which is set when we _hook the descriptor. + This is a dirty hack. There must be a better way. + """ + EVENTROUTER.receive_ws_callback(args[0]) + return w.WEECHAT_RC_OK + +def buffer_closing_callback(signal, sig_type, data): + eval(signal).unregister_weechat_buffer(data) + return w.WEECHAT_RC_OK + +def buffer_input_callback(signal, buffer_ptr, data): + eventrouter = eval(signal) + + dbg(signal, True) + dbg(data, True) + dbg(buffer_ptr, True) + channel = eventrouter.weechat_buffers[buffer_ptr] + print channel + if not channel: + return w.WEECHAT_RC_OK_EAT + +# reaction = re.match("^\s*(\d*)(\+|-):(.*):\s*$", data) +# if reaction: +# if reaction.group(2) == "+": +# channel.send_add_reaction(int(reaction.group(1) or 1), reaction.group(3)) +# elif reaction.group(2) == "-": +# channel.send_remove_reaction(int(reaction.group(1) or 1), reaction.group(3)) +# elif data.startswith('s/'): +# try: +# old, new, flags = re.split(r'(? 999: + self.ws_counter = 0 + self.ws_counter += 1 + return self.ws_counter + def send_to_websocket(self, data, expect_reply=True): + data["id"] = self.next_ws_transaction_id() + message = json.dumps(data) + try: + if expect_reply: + self.ws_replies[data["id"]] = data + self.ws.send(message) + dbg("Sent {}...".format(message[:100])) + except: + dbg("Unexpected error: {}\nSent: {}".format(sys.exc_info()[0], data)) + self.connected = False + + +class SlackChannel(object): + """ + Represents an individual slack channel. + """ + def __init__(self, eventrouter, **kwargs): + # We require these two things for a vaid object, + # the rest we can just learn from slack + self.eventrouter = eventrouter + self.identifier = kwargs["id"] + self.name = kwargs["name"] + self.channel_buffer = None + self.team = None + self.got_history = False + self.messages = {} + self.type = 'channel' + for key, value in kwargs.items(): + setattr(self, key, value) + def __repr__(self): + return "Name:{} Identifier:{}".format(self.name, self.identifier) + def open_if_we_should(self): + for reason in ["is_member", "is_open", "unread_count"]: + try: + if eval("self." + reason): + self.create_buffer() + except: + pass + pass + def set_related_server(self, team): + self.team = team + def create_buffer(self): + if not self.channel_buffer: + print self.team + self.channel_buffer = w.buffer_new("{}.{}".format(self.team.domain, self.name), "buffer_input_callback", "EVENTROUTER", "", "") + self.eventrouter.register_weechat_buffer(self.channel_buffer, self) + + #dbg(self.channel_buffer, True) + w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') + w.buffer_set(self.channel_buffer, "localvar_set_channel", self.name) + w.buffer_set(self.channel_buffer, "short_name", self.name) +# if self.server.alias: +# w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) +# else: +# w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) +# buffer_list_update_next() +# if self.unread_count != 0 and not self.muted: +# w.buffer_set(self.channel_buffer, "hotlist", "1") + def destroy_buffer(self): + if self.channel_buffer: + self.channel_buffer = None + def buffer_prnt(self, nick, text, timestamp, *args): + if self.channel_buffer: + w.prnt(self.channel_buffer, "{}\t{}".format(nick, text)) + #dbg("should buffer print {} {}".format(nick, text), True) + def send_message(self, message): + #team = self.eventrouter.teams[self.team] + #message = self.linkify_text(message) + dbg(message) + print self.team + request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} + dbg(request, True) + self.team.send_to_websocket(request) + def store_message(self, message, team, from_me=False): + if from_me: + message.message_json["user"] = team.myidentifier + self.messages[message.ts] = message + if len(self.messages.keys()) > SCROLLBACK_SIZE: + mk = self.messages.keys() + mk.sort() + for k in mk[:SCROLLBACK_SIZE]: + del self.messages[k] + def change_message(self, ts, text=None, suffix=''): + #print "should have changed" + if ts in self.messages: + m = self.messages[ts] + m.change_text(text) + #m = self.get_message(ts) + #text = m[2].render(force=True) + #timestamp, time_id = ts.split(".", 2) + #timestamp = int(timestamp) + #modify_buffer_line(self.channel_buffer, text + suffix, timestamp, time_id) + return True + def get_history(self): + #if config.cache_messages: + # for message in message_cache[self.identifier]: + # process_message(json.loads(message), True) + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + EVENTROUTER.receive(s) + #async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}) + self.got_history = True + def sorted_message_keys(self): + return sorted(self.messages) + + +class SlackDMChannel(SlackChannel): + def __init__(self, eventrouter, users, **kwargs): + dmuser = kwargs["user"] + kwargs["name"] = users[dmuser].name + super(SlackDMChannel, self).__init__(eventrouter, **kwargs) + self.type = 'im' + def create_buffer(self): + if not self.channel_buffer: + super(SlackDMChannel, self).create_buffer() + w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') + +class SlackGroupChannel(SlackChannel): + def __init__(self, eventrouter, **kwargs): + super(SlackGroupChannel, self).__init__(eventrouter, **kwargs) + self.type = "group" + +class SlackMPDMChannel(SlackChannel): + """ + An MPDM channel is a special instance of a 'group' channel. + We change the name to look less terrible in weechat. + """ + def __init__(self, eventrouter, **kwargs): + n = kwargs.get('name') + name = "|".join("-".join(n.split("-")[1:-1]).split("--")) + kwargs["name"] = name + super(SlackMPDMChannel, self).__init__(eventrouter, **kwargs) + self.type = "group" + +class SlackUser(object): + """ + Represends an individual slack user. + """ + def __init__(self, **kwargs): + # We require these two things for a vaid object, + # the rest we can just learn from slack + self.identifier = kwargs["id"] + self.name = kwargs["name"] + for key, value in kwargs.items(): + setattr(self, key, value) + def __repr__(self): + return "Name:{} Identifier:{}".format(self.name, self.identifier) + def formatted_name(self, prepend="", enable_color=True): + if config.colorize_nicks and enable_color: + print_color = self.color + else: + print_color = "" + return print_color + prepend + self.name + +class SlackMessage(object): + def __init__(self, message_json, team, channel): + self.team = team + self.channel = channel + self.message_json = message_json + self.sender = self.get_sender() + self.ts = message_json['ts'] + def render(self): + return render(self.message_json, self.team, self.channel) + def change_text(self, new_text): + dbg("should change text to {}".format(new_text), True) + return "meh" + def get_sender(self, utf8=True): + if 'bot_id' in self.message_json and self.message_json['bot_id'] is not None: + name = u"{} :]".format(self.server.bots.find(self.message_json["bot_id"]).formatted_name()) + elif 'user' in self.message_json: + if self.message_json['user'] in self.team.users: + u = self.team.users[self.message_json['user']] + if u.is_bot: + name = u"{} :]".format(u.formatted_name()) + else: + name = u.name + elif 'username' in self.message_json: + name = u"-{}-".format(self.message_json["username"]) + elif 'service_name' in self.message_json: + name = u"-{}-".format(self.message_json["service_name"]) + else: + name = u"" + if utf8: + return name.encode('utf-8') + else: + return name + +class WeeSlackMetadata(object): + def __init__(self, meta): + self.meta = meta + def jsonify(self): + return self.meta + +###### New handlers + +def handle_rtmstart(login_data, eventrouter): + """ + This handles the main entry call to slack, rtm.start + """ + if login_data["ok"]: + metadata = pickle.loads(login_data["wee_slack_request_metadata"]) + + users = {} + for item in login_data["users"]: + users[item["id"]] = SlackUser(**item) + #users.append(SlackUser(**item)) + + channels = {} + for item in login_data["channels"]: + channels[item["id"]] = SlackChannel(eventrouter, **item) + + for item in login_data["ims"]: + channels[item["id"]] = SlackDMChannel(eventrouter, users, **item) + + for item in login_data["groups"]: + if item["name"].startswith('mpdm-'): + channels[item["id"]] = SlackMPDMChannel(eventrouter, **item) + else: + channels[item["id"]] = SlackGroupChannel(eventrouter, **item) + + t = SlackTeam( + metadata.token, + login_data["team"]["domain"], + login_data["self"]["name"], + login_data["self"]["id"], + users, + channels, + ) + eventrouter.register_team(t) + + web_socket_url = login_data['url'] + try: + ws = create_connection(web_socket_url, sslopt=sslopt_ca_certs) + w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", t.get_team_hash()) + #ws_hook = w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", pickle.dumps(t)) + ws.sock.setblocking(0) + t.attach_websocket(ws) + t.set_connected() + except Exception as e: + dbg("websocket connection error: {}".format(e)) + return False + + dbg("connected to {}".format(t.domain)) + + #self.identifier = self.domain + +def handle_groupshistory(message_json, eventrouter, **kwargs): + handle_history(message_json, eventrouter, **kwargs) + +def handle_channelshistory(message_json, eventrouter, **kwargs): + handle_history(message_json, eventrouter, **kwargs) + +def handle_imhistory(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] + kwargs['channel'] = kwargs['team'].channels[request_metadata.channel_identifier] + for message in message_json["messages"]: + process_message(message, eventrouter, **kwargs) + +###### New/converted process_ and subprocess_ methods + +def process_manual_presence_change(message_json, eventrouter, **kwargs): + process_presence_change(message_json, eventrouter, **kwargs) + + +def process_presence_change(message_json, eventrouter, **kwargs): + kwargs["user"].presence = message_json["presence"] + + +def process_pong(message_json, eventrouter, **kwargs): + pass + +def process_message(message_json, eventrouter, store=True, **kwargs): + channel = kwargs["channel"] + team = kwargs["team"] + #try: + # send these subtype messages elsewhere + known_subtypes = [ + #'thread_message', + #'message_replied', + #'message_changed', + #'message_deleted', + #'channel_join', + #'channel_leave', + #'channel_topic', + #'group_join', + #'group_leave', + ] + if "thread_ts" in message_json and "reply_count" not in message_json: + message_json["subtype"] = "thread_message" + if "subtype" in message_json: + if message_json["subtype"] in known_subtypes: + f = eval('subprocess_' + message_json["subtype"]) + f(message_json, eventrouter, channel, team) + + else: + message = SlackMessage(message_json, team, channel) + #message = Message(message_json, server=team, channel=channel) + text = message.render() + #print text + + # special case with actions. + if text.startswith("_") and text.endswith("_"): + text = text[1:-1] + if message.sender != channel.server.nick: + text = message.sender + " " + text + channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts) + + else: + suffix = '' + if 'edited' in message_json: + suffix = ' (edited)' + channel.buffer_prnt(message.sender, text + suffix, message.ts) + + if store: + channel.store_message(message, team) + dbg("NORMAL REPLY {}".format(message_json)) + +def subprocess_thread_message(message_json, eventrouter, channel, team): + dbg("REPLIEDDDD: " + str(message_json)) +# channel = channels.find(message_json["channel"]) +# server = channel.server +# #threadinfo = channel.get_message(message_json["thread_ts"]) +# message = Message(message_json, server=server, channel=channel) +# dbg(message, main_buffer=True) +# +# orig = channel.get_message(message_json['thread_ts']) +# if orig[0]: +# channel.get_message(message_json['thread_ts'])[2].add_thread_message(message) +# else: +# dbg("COULDN'T find orig message {}".format(message_json['thread_ts']), main_buffer=True) + + #if threadinfo[0]: + # channel.messages[threadinfo[1]].become_thread() + # message_json["item"]["ts"], message_json) + #channel.change_message(message_json["thread_ts"], None, message_json["text"]) + #channel.become_thread(message_json["item"]["ts"], message_json) + + +def subprocess_message_changed(message_json, eventrouter, channel, team): + m = message_json["message"] + if "message" in message_json: + if "attachments" in m: + message_json["attachments"] = m["attachments"] + if "text" in m: + if "text" in message_json: + message_json["text"] += m["text"] + dbg("added text!") + else: + message_json["text"] = m["text"] + if "fallback" in m: + if "fallback" in message_json: + message_json["fallback"] += m["fallback"] + else: + message_json["fallback"] = m["fallback"] + + text_before = (len(m['text']) > 0) + m["text"] += unwrap_attachments(message_json, text_before) + if "edited" in m: + channel.change_message(m["ts"], m["text"], ' (edited)') + else: + channel.change_message(m["ts"], m["text"]) + +def process_reply(message_json, eventrouter, **kwargs): + dbg('processing reply') + dbg(message_json, True) + team = kwargs["team"] + identifier = message_json["reply_to"] + try: + original_message_json = team.ws_replies[identifier] + del team.ws_replies[identifier] + if "ts" in message_json: + original_message_json["ts"] = message_json["ts"] + else: + dbg("no reply ts {}".format(message_json)) + + if "channel" in original_message_json: + channel = team.channels[original_message_json["channel"]] + m = SlackMessage(original_message_json, team, channel) + # m = Message(message_json, server=server) + dbg(m, True) + + #if "type" in message_json: + # if message_json["type"] == "message" and "channel" in message_json.keys(): + # message_json["ts"] = message_json["ts"] + # channels.find(message_json["channel"]).store_message(m, from_me=True) + + # channels.find(message_json["channel"]).buffer_prnt(server.nick, m.render(), m.ts) + process_message(m.message_json, eventrouter, channel=channel, team=team) + dbg("REPLY {}".format(message_json)) + except KeyError: + dbg("Unexpected reply") + +def process_channel_marked(message_json, eventrouter, **kwargs): + channel = kwargs["channel"] + dbg(channel, True) + #channel.mark_read(False) + #w.buffer_set(channel.channel_buffer, "hotlist", "-1") + +def process_channel_joined(message_json, eventrouter, **kwargs): + print message_json + print kwargs +# server = servers.find(message_json["_server"]) +# channel = server.channels.find(message_json["channel"]) +# text = unfurl_refs(message_json["text"], ignore_alt_text=False) +# channel.buffer_prnt(w.prefix("join").rstrip(), text, message_json["ts"]) +# channel.user_join(message_json["user"]) + + +###### New module/global methods + +def render(message_json, team, channel, force=False): + # If we already have a rendered version in the object, just return that. + if not force and message_json.get("_rendered_text", ""): + return message_json["_rendered_text"] + else: + # server = servers.find(message_json["_server"]) + + if "fallback" in message_json: + text = message_json["fallback"] + elif "text" in message_json: + if message_json['text'] is not None: + text = message_json["text"] + else: + text = u"" + else: + text = u"" + + text = unfurl_refs(text, ignore_alt_text=config.unfurl_ignore_alt_text) + + text_before = (len(text) > 0) + text += unfurl_refs(unwrap_attachments(message_json, text_before), ignore_alt_text=config.unfurl_ignore_alt_text) + + text = text.lstrip() + text = text.replace("\t", " ") + text = text.replace("<", "<") + text = text.replace(">", ">") + text = text.replace("&", "&") + text = text.encode('utf-8') + +# if self.threads: +# text += " [Replies: {} Thread ID: {} ] ".format(len(self.threads), self.thread_id) +# #for thread in self.threads: + + if "reactions" in message_json: + text += create_reaction_string(message_json["reactions"]) + message_json["_rendered_text"] = text + + return text + +def linkify_text(message): + message = message.split(' ') + for item in enumerate(message): + targets = re.match('.*([@#])([\w.]+\w)(\W*)', item[1]) + if targets and targets.groups()[0] == '@': + named = targets.groups() + if named[1] in ["group", "channel", "here"]: + message[item[0]] = "".format(named[1]) + if self.server.users.find(named[1]): + message[item[0]] = "<@{}>{}".format(self.server.users.find(named[1]).identifier, named[2]) + if targets and targets.groups()[0] == '#': + named = targets.groups() + if self.server.channels.find(named[1]): + message[item[0]] = "<#{}|{}>{}".format(self.server.channels.find(named[1]).identifier, named[1], named[2]) + dbg(message) + return " ".join(message) + +def unfurl_refs(text, ignore_alt_text=False): + """ + input : <@U096Q7CQM|someuser> has joined the channel + ouput : someuser has joined the channel + """ + # Find all strings enclosed by <> + # - + # - <#C2147483705|#otherchannel> + # - <@U2147483697|@othernick> + # Test patterns lives in ./_pytest/test_unfurl.py + matches = re.findall(r"(<[@#]?(?:[^<]*)>)", text) + for m in matches: + # Replace them with human readable strings + text = text.replace(m, unfurl_ref(m[1:-1], ignore_alt_text)) + return text + +def unfurl_ref(ref, ignore_alt_text=False): + id = ref.split('|')[0] + display_text = ref + if ref.find('|') > -1: + if ignore_alt_text: + display_text = resolve_ref(id) + else: + if id.startswith("#C") or id.startswith("@U"): + display_text = ref.split('|')[1] + else: + url, desc = ref.split('|', 1) + display_text = u"{} ({})".format(url, desc) + else: + display_text = resolve_ref(ref) + return display_text + +def unwrap_attachments(message_json, text_before): + attachment_text = '' + if "attachments" in message_json: + if text_before: + attachment_text = u'\n' + for attachment in message_json["attachments"]: + # Attachments should be rendered roughly like: + # + # $pretext + # $author: (if rest of line is non-empty) $title ($title_link) OR $from_url + # $author: (if no $author on previous line) $text + # $fields + t = [] + prepend_title_text = '' + if 'author_name' in attachment: + prepend_title_text = attachment['author_name'] + ": " + if 'pretext' in attachment: + t.append(attachment['pretext']) + if "title" in attachment: + if 'title_link' in attachment: + t.append('%s%s (%s)' % (prepend_title_text, attachment["title"], attachment["title_link"],)) + else: + t.append(prepend_title_text + attachment["title"]) + prepend_title_text = '' + elif "from_url" in attachment: + t.append(attachment["from_url"]) + if "text" in attachment: + tx = re.sub(r' *\n[\n ]+', '\n', attachment["text"]) + t.append(prepend_title_text + tx) + prepend_title_text = '' + if 'fields' in attachment: + for f in attachment['fields']: + if f['title'] != '': + t.append('%s %s' % (f['title'], f['value'],)) + else: + t.append(f['value']) + if t == [] and "fallback" in attachment: + t.append(attachment["fallback"]) + attachment_text += "\n".join([x.strip() for x in t if x]) + return attachment_text + + +def resolve_ref(ref): + #TODO: This hack to use eventrouter needs to go + #this resolver should probably move to the slackteam or eventrouter itself + #global EVENTROUTER + if 'EVENTROUTER' in globals(): + 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)) + elif ref.startswith('#C'): + for t in e.teams.keys(): + if ref[1:] in e.teams[t].channels: + #try: + return "{}".format(e.teams[t].channels[ref[1:]].name) + #except: + # dbg("CHANNEL: {}".format(ref)) + + # Something else, just return as-is + return ref + +def create_reaction_string(reactions): + count = 0 + if not isinstance(reactions, list): + reaction_string = " [{}]".format(reactions) + else: + reaction_string = ' [' + for r in reactions: + if len(r["users"]) > 0: + count += 1 + if config.show_reaction_nicks: + nicks = [resolve_ref("@{}".format(user)) for user in r["users"]] + users = "({})".format(",".join(nicks)) + else: + users = len(r["users"]) + reaction_string += ":{}:{} ".format(r["name"], users) + reaction_string = reaction_string[:-1] + ']' + if count == 0: + reaction_string = '' + return reaction_string + + +###### NEW EXCEPTIONS + +class ProcessNotImplemented(Exception): + """ + Raised when we try to call process_(something), but + (something) has not been defined as a function. + """ + def __init__(self, function_name): + super(ProcessNotImplemented, self).__init__(function_name) + +class InvalidType(Exception): + """ + Raised when we do type checking to ensure objects of the wrong + type are not used improperly. + """ + def __init__(self, type_str): + super(InvalidType, self).__init__(type_str) + +###### New but probably old and need to migrate + +def closed_slack_debug_buffer_cb(data, buffer): + global slack_debug + slack_debug = None + return w.WEECHAT_RC_OK + + +def create_slack_debug_buffer(): + global slack_debug, debug_string + if slack_debug is not None: + w.buffer_set(slack_debug, "display", "1") + else: + debug_string = None + slack_debug = w.buffer_new("slack-debug", "", "", "closed_slack_debug_buffer_cb", "") + w.buffer_set(slack_debug, "notify", "0") + + +##### END NEW + + +def dbg(message, main_buffer=False, fout=False): + """ + send debug output to the slack-debug buffer and optionally write to a file. + """ + #TODO: do this smarter + #return + global debug_string + message = "DEBUG: {}".format(message) + # message = message.encode('utf-8', 'replace') + if fout: + file('/tmp/debug.log', 'a+').writelines(message + '\n') + if main_buffer: + w.prnt("", "---------") + w.prnt("", "slack: " + message) + else: + if slack_debug and (not debug_string or debug_string in message): + w.prnt(slack_debug, "---------") + w.prnt(slack_debug, message) + + +###### Config code + +class PluginConfig(object): + # Default settings. + # These are in the (string) format that weechat expects; at __init__ time + # this value will be used to set the default for any settings not already + # defined, and then the real (python) values of the settings will be + # extracted. + # TODO: setting descriptions. + settings = { + 'colorize_messages': 'false', + 'colorize_nicks': 'true', + 'colorize_private_chats': 'false', + 'debug_mode': 'false', + 'distracting_channels': '', + 'show_reaction_nicks': 'false', + 'slack_api_token': 'INSERT VALID KEY HERE!', + 'slack_timeout': '20000', + 'switch_buffer_on_join': 'true', + 'trigger_value': 'false', + 'unfurl_ignore_alt_text': 'false', + 'cache_messages': 'true', + } + + # Set missing settings to their defaults. Load non-missing settings from + # weechat configs. + def __init__(self): + for key, default in self.settings.iteritems(): + if not w.config_get_plugin(key): + w.config_set_plugin(key, default) + self.config_changed(None, None, None) + + def __str__(self): + return "".join([x + "\t" + str(self.settings[x]) + "\n" for x in self.settings.keys()]) + + def config_changed(self, data, key, value): + for key in self.settings: + self.settings[key] = self.fetch_setting(key) + if self.debug_mode: + create_slack_debug_buffer() + return w.WEECHAT_RC_OK + + def fetch_setting(self, key): + if hasattr(self, 'get_' + key): + try: + return getattr(self, 'get_' + key)(key) + except: + return self.settings[key] + else: + # Most settings are on/off, so make get_boolean the default + return self.get_boolean(key) + + def __getattr__(self, key): + return self.settings[key] + + def get_boolean(self, key): + return w.config_string_to_boolean(w.config_get_plugin(key)) + + def get_distracting_channels(self, key): + return [x.strip() for x in w.config_get_plugin(key).split(',')] + + def get_slack_api_token(self, key): + token = w.config_get_plugin("slack_api_token") + if token.startswith('${sec.data'): + return w.string_eval_expression(token, {}, {}, {}) + else: + return token + + def get_slack_timeout(self, key): + return int(w.config_get_plugin(key)) + + +# Main +if __name__ == "__main__": + + if w.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, + SCRIPT_DESC, "script_unloaded", ""): + + version = w.info_get("version_number", "") or 0 + if int(version) < 0x1030000: + w.prnt("", "\nERROR: Weechat version 1.3+ is required to use {}.\n\n".format(SCRIPT_NAME)) + else: + + WEECHAT_HOME = w.info_get("weechat_dir", "") + CACHE_NAME = "slack.cache" + STOP_TALKING_TO_SLACK = False + + # Global var section + slack_debug = None + config = PluginConfig() + config_changed_cb = config.config_changed + + typing_timer = time.time() + domain = None + previous_buffer = None + slack_buffer = None + + buffer_list_update = False + previous_buffer_list_update = 0 + + never_away = False + hide_distractions = False + hotlist = w.infolist_get("hotlist", "", "") + main_weechat_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, "DOESNOTEXIST!@#$")) + + message_cache = collections.defaultdict(list) + if config.cache_messages: + cache_load() + + #servers = SearchList() + #for token in config.slack_api_token.split(','): + # server = SlackServer(token) + # servers.append(server) + #channels = SearchList() + #users = SearchList() + #threads = SearchList() + + w.hook_config("plugins.var.python." + SCRIPT_NAME + ".*", "config_changed_cb", "") + #w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") + + # attach to the weechat hooks we need + #w.hook_timer(1000, 0, 0, "typing_update_cb", "") + #w.hook_timer(1000, 0, 0, "buffer_list_update_cb", "") + w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") + w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") + w.hook_signal('buffer_closing', "buffer_closing_callback", "EVENTROUTER") + w.hook_signal('buffer_opened', "buffer_opened_cb", "") + w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") + w.hook_signal('window_switch', "buffer_switch_callback", "EVENTROUTER") + #w.hook_signal('input_text_changed', "typing_notification_cb", "") + w.hook_signal('quit', "quit_notification_cb", "") + w.hook_signal('window_scrolled', "scrolled_cb", "") + #w.hook_command( + # # Command name and description + # 'slack', 'Plugin to allow typing notification and sync of read markers for slack.com', + # # Usage + # '[command] [command options]', + # # Description of arguments + # 'Commands:\n' + + # '\n'.join(cmds.keys()) + + # '\nUse /slack help [command] to find out more\n', + # # Completions + # '|'.join(cmds.keys()), + # # Function name + # 'slack_command_cb', '') + # w.hook_command('me', 'me_command_cb', '') + w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') + w.hook_command_run('/query', 'join_command_cb', '') + w.hook_command_run('/join', 'join_command_cb', '') + w.hook_command_run('/part', 'part_command_cb', '') + w.hook_command_run('/leave', 'part_command_cb', '') + w.hook_command_run('/topic', 'topic_command_cb', '') + w.hook_command_run('/msg', 'msg_command_cb', '') + w.hook_command_run('/label', 'label_command_cb', '') + w.hook_command_run("/input complete_next", "complete_next_cb", "") + w.hook_command_run('/away', 'away_command_cb', '') + w.hook_completion("nicks", "complete @-nicks for slack", + "nick_completion_cb", "") + #w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') + + tok = config.slack_api_token.split(',')[0] + s = SlackRequest(tok, 'rtm.start', {}) + #async_slack_api_request("slack.com", self.token, "rtm.start", {"ts": t}) + #s = SlackRequest('xoxoxoxox', "blah.get", {"meh": "blah"}) + global EVENTROUTER + EVENTROUTER = EventRouter() + EVENTROUTER.receive(s) + EVENTROUTER.handle_next() + w.hook_timer(10, 0, 0, "handle_next", "") + # END attach to the weechat hooks we need -- cgit From bcc2ac1bb5cf2750f27d73348888f60098ca3291 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 11:14:06 -0800 Subject: recorded test data added --- .../websocket/1485975367.79-reconnect_url.json | 1 + .../data/websocket/1485975408.19-user_typing.json | 1 + .../data/websocket/1485975412.74-user_typing.json | 1 + .../data/websocket/1485975420.36-user_typing.json | 1 + _pytest/data/websocket/1485975421.33-message.json | 1 + .../1485975421.42-desktop_notification.json | 1 + .../websocket/1485975426.55-reaction_added.json | 1 + .../websocket/1485975428.76-reaction_removed.json | 1 + .../websocket/1485975458.35-channel_created.json | 1 + .../websocket/1485975458.87-channel_joined.json | 1 + _pytest/data/websocket/1485975458.87-message.json | 1 + .../1485975462.62-update_thread_state.json | 1 + .../data/websocket/1485975462.62-user_typing.json | 1 + .../websocket/1485975476.62-channel_deleted.json | 1 + .../data/websocket/1485975476.86-group_join.json | 1 + .../data/websocket/1485975476.86-group_joined.json | 1 + .../1485975487.69-group_history_changed.json | 1 + .../data/websocket/1485975487.69-group_join.json | 1 + .../data/websocket/1485975493.19-group_joined.json | 1 + _pytest/data/websocket/1485975493.23-message.json | 1 + _pytest/data/websocket/1485975547.75-message.json | 1 + .../1485975547.75-update_thread_state.json | 1 + .../websocket/1485975547.83-reconnect_url.json | 1 + .../data/websocket/1485975606.59-team_join.json | 1 + _pytest/data/websocket/1485975606.67-message.json | 1 + .../data/websocket/1485975606.75-im_created.json | 1 + _pytest/data/websocket/1485975606.75-im_open.json | 1 + .../websocket/1485975611.29-presence_change.json | 1 + .../data/websocket/1485975640.5-user_change.json | 1 + .../data/websocket/1485975669.14-user_change.json | 1 + .../websocket/1485975675.81-presence_change.json | 1 + .../data/websocket/1485975690.59-mpim_joined.json | 1 + .../data/websocket/1485975690.67-group_joined.json | 1 + .../1485975690.67-update_thread_state.json | 1 + .../data/websocket/1485975697.58-user_typing.json | 1 + _pytest/data/websocket/1485975698.45-message.json | 1 + .../websocket/1485975701.72-reaction_added.json | 1 + .../websocket/1485975703.22-reaction_removed.json | 1 + _pytest/data/websocket/1485975715.79-message.json | 1 + .../data/websocket/1485975715.87-file_shared.json | 1 + _pytest/data/websocket/1485975723.85-message.json | 1 + .../websocket/1485975727.84-reconnect_url.json | 1 + .../websocket/1485975735.91-group_archive.json | 1 + _pytest/data/websocket/1485975735.99-message.json | 1 + _pytest/data/websocket/1485975736.08-message.json | 1 + .../1485975738.1-desktop_notification.json | 1 + .../websocket/1485975747.17-channel_created.json | 1 + .../websocket/1485975757.63-channel_rename.json | 1 + .../websocket/1485975771.6-presence_change.json | 1 + .../websocket/1485975774.62-presence_change.json | 1 + .../data/websocket/1485975822.17-user_typing.json | 1 + _pytest/data/websocket/1485975824.48-message.json | 1 + _pytest/data/websocket/1485975836.23-message.json | 1 + _pytest/data/websocket/1485975836.31-message.json | 1 + _pytest/data/websocket/1485975842.1-message.json | 1 + _pytest/data/websocket/1485975842.18-message.json | 1 + .../data/websocket/1485975850.32-mpim_open.json | 1 + .../data/websocket/1485975850.45-group_open.json | 1 + .../websocket/1485975858.56-reaction_added.json | 1 + _pytest/data/websocket/1485975890.7-bot_added.json | 1 + _pytest/data/websocket/1485975890.78-message.json | 1 + _pytest/data/websocket/1485975896.16-message.json | 1 + .../websocket/1485975907.84-reconnect_url.json | 1 + .../data/websocket/1485975926.59-user_change.json | 1 + .../data/websocket/1485975930.81-user_change.json | 1 + .../data/websocket/1485975956.38-file_public.json | 1 + .../data/websocket/1485975956.47-file_shared.json | 1 + _pytest/data/websocket/1485975956.47-message.json | 1 + _pytest/data/websocket/1485975978.79-message.json | 1 + .../data/websocket/1485975978.91-file_public.json | 1 + .../data/websocket/1485975978.91-file_shared.json | 1 + .../data/websocket/1485975995.98-file_change.json | 1 + .../data/websocket/1485975998.13-file_change.json | 1 + .../data/websocket/1485976002.57-file_deleted.json | 1 + _pytest/data/websocket/1485976002.66-message.json | 1 + .../data/websocket/1485976024.46-user_typing.json | 1 + .../data/websocket/1485976028.31-user_typing.json | 1 + .../websocket/1485976039.32-reaction_added.json | 1 + .../websocket/1485976040.8-reaction_removed.json | 1 + .../websocket/1485976087.84-reconnect_url.json | 1 + .../websocket/1485976107.57-channel_created.json | 1 + .../data/websocket/1485976114.72-user_typing.json | 1 + .../data/websocket/1485976117.99-user_typing.json | 1 + .../data/websocket/1485976124.9-user_typing.json | 1 + _pytest/data/websocket/1485976124.98-message.json | 1 + .../1485976125.06-desktop_notification.json | 1 + .../data/websocket/1485976125.95-user_typing.json | 1 + .../data/websocket/1485976129.49-user_typing.json | 1 + .../data/websocket/1485976133.0-user_typing.json | 1 + .../data/websocket/1485976137.51-user_typing.json | 1 + _pytest/data/websocket/1485976137.95-message.json | 1 + .../data/websocket/1485976138.73-user_typing.json | 1 + .../data/websocket/1485976141.79-user_typing.json | 1 + .../data/websocket/1485976145.8-user_typing.json | 1 + .../data/websocket/1485976149.89-user_typing.json | 1 + _pytest/data/websocket/1485976151.6-message.json | 1 + _pytest/data/websocket/1485976157.18-message.json | 1 + .../data/websocket/1485976157.8-user_typing.json | 1 + .../data/websocket/1485976161.29-user_typing.json | 1 + _pytest/data/websocket/1485976161.75-message.json | 1 + .../websocket/1485976182.59-channel_archive.json | 1 + .../websocket/1485976186.62-channel_unarchive.json | 1 + _pytest/data/websocket/1485976236.58-message.json | 1 + .../1485976236.67-file_comment_added.json | 1 + _pytest/data/websocket/1485976248.57-message.json | 1 + .../1485976248.65-file_comment_edited.json | 1 + .../websocket/1485976267.81-reconnect_url.json | 1 + wee_slack.py | 604 ++++++++++++++++----- 108 files changed, 561 insertions(+), 150 deletions(-) create mode 100644 _pytest/data/websocket/1485975367.79-reconnect_url.json create mode 100644 _pytest/data/websocket/1485975408.19-user_typing.json create mode 100644 _pytest/data/websocket/1485975412.74-user_typing.json create mode 100644 _pytest/data/websocket/1485975420.36-user_typing.json create mode 100644 _pytest/data/websocket/1485975421.33-message.json create mode 100644 _pytest/data/websocket/1485975421.42-desktop_notification.json create mode 100644 _pytest/data/websocket/1485975426.55-reaction_added.json create mode 100644 _pytest/data/websocket/1485975428.76-reaction_removed.json create mode 100644 _pytest/data/websocket/1485975458.35-channel_created.json create mode 100644 _pytest/data/websocket/1485975458.87-channel_joined.json create mode 100644 _pytest/data/websocket/1485975458.87-message.json create mode 100644 _pytest/data/websocket/1485975462.62-update_thread_state.json create mode 100644 _pytest/data/websocket/1485975462.62-user_typing.json create mode 100644 _pytest/data/websocket/1485975476.62-channel_deleted.json create mode 100644 _pytest/data/websocket/1485975476.86-group_join.json create mode 100644 _pytest/data/websocket/1485975476.86-group_joined.json create mode 100644 _pytest/data/websocket/1485975487.69-group_history_changed.json create mode 100644 _pytest/data/websocket/1485975487.69-group_join.json create mode 100644 _pytest/data/websocket/1485975493.19-group_joined.json create mode 100644 _pytest/data/websocket/1485975493.23-message.json create mode 100644 _pytest/data/websocket/1485975547.75-message.json create mode 100644 _pytest/data/websocket/1485975547.75-update_thread_state.json create mode 100644 _pytest/data/websocket/1485975547.83-reconnect_url.json create mode 100644 _pytest/data/websocket/1485975606.59-team_join.json create mode 100644 _pytest/data/websocket/1485975606.67-message.json create mode 100644 _pytest/data/websocket/1485975606.75-im_created.json create mode 100644 _pytest/data/websocket/1485975606.75-im_open.json create mode 100644 _pytest/data/websocket/1485975611.29-presence_change.json create mode 100644 _pytest/data/websocket/1485975640.5-user_change.json create mode 100644 _pytest/data/websocket/1485975669.14-user_change.json create mode 100644 _pytest/data/websocket/1485975675.81-presence_change.json create mode 100644 _pytest/data/websocket/1485975690.59-mpim_joined.json create mode 100644 _pytest/data/websocket/1485975690.67-group_joined.json create mode 100644 _pytest/data/websocket/1485975690.67-update_thread_state.json create mode 100644 _pytest/data/websocket/1485975697.58-user_typing.json create mode 100644 _pytest/data/websocket/1485975698.45-message.json create mode 100644 _pytest/data/websocket/1485975701.72-reaction_added.json create mode 100644 _pytest/data/websocket/1485975703.22-reaction_removed.json create mode 100644 _pytest/data/websocket/1485975715.79-message.json create mode 100644 _pytest/data/websocket/1485975715.87-file_shared.json create mode 100644 _pytest/data/websocket/1485975723.85-message.json create mode 100644 _pytest/data/websocket/1485975727.84-reconnect_url.json create mode 100644 _pytest/data/websocket/1485975735.91-group_archive.json create mode 100644 _pytest/data/websocket/1485975735.99-message.json create mode 100644 _pytest/data/websocket/1485975736.08-message.json create mode 100644 _pytest/data/websocket/1485975738.1-desktop_notification.json create mode 100644 _pytest/data/websocket/1485975747.17-channel_created.json create mode 100644 _pytest/data/websocket/1485975757.63-channel_rename.json create mode 100644 _pytest/data/websocket/1485975771.6-presence_change.json create mode 100644 _pytest/data/websocket/1485975774.62-presence_change.json create mode 100644 _pytest/data/websocket/1485975822.17-user_typing.json create mode 100644 _pytest/data/websocket/1485975824.48-message.json create mode 100644 _pytest/data/websocket/1485975836.23-message.json create mode 100644 _pytest/data/websocket/1485975836.31-message.json create mode 100644 _pytest/data/websocket/1485975842.1-message.json create mode 100644 _pytest/data/websocket/1485975842.18-message.json create mode 100644 _pytest/data/websocket/1485975850.32-mpim_open.json create mode 100644 _pytest/data/websocket/1485975850.45-group_open.json create mode 100644 _pytest/data/websocket/1485975858.56-reaction_added.json create mode 100644 _pytest/data/websocket/1485975890.7-bot_added.json create mode 100644 _pytest/data/websocket/1485975890.78-message.json create mode 100644 _pytest/data/websocket/1485975896.16-message.json create mode 100644 _pytest/data/websocket/1485975907.84-reconnect_url.json create mode 100644 _pytest/data/websocket/1485975926.59-user_change.json create mode 100644 _pytest/data/websocket/1485975930.81-user_change.json create mode 100644 _pytest/data/websocket/1485975956.38-file_public.json create mode 100644 _pytest/data/websocket/1485975956.47-file_shared.json create mode 100644 _pytest/data/websocket/1485975956.47-message.json create mode 100644 _pytest/data/websocket/1485975978.79-message.json create mode 100644 _pytest/data/websocket/1485975978.91-file_public.json create mode 100644 _pytest/data/websocket/1485975978.91-file_shared.json create mode 100644 _pytest/data/websocket/1485975995.98-file_change.json create mode 100644 _pytest/data/websocket/1485975998.13-file_change.json create mode 100644 _pytest/data/websocket/1485976002.57-file_deleted.json create mode 100644 _pytest/data/websocket/1485976002.66-message.json create mode 100644 _pytest/data/websocket/1485976024.46-user_typing.json create mode 100644 _pytest/data/websocket/1485976028.31-user_typing.json create mode 100644 _pytest/data/websocket/1485976039.32-reaction_added.json create mode 100644 _pytest/data/websocket/1485976040.8-reaction_removed.json create mode 100644 _pytest/data/websocket/1485976087.84-reconnect_url.json create mode 100644 _pytest/data/websocket/1485976107.57-channel_created.json create mode 100644 _pytest/data/websocket/1485976114.72-user_typing.json create mode 100644 _pytest/data/websocket/1485976117.99-user_typing.json create mode 100644 _pytest/data/websocket/1485976124.9-user_typing.json create mode 100644 _pytest/data/websocket/1485976124.98-message.json create mode 100644 _pytest/data/websocket/1485976125.06-desktop_notification.json create mode 100644 _pytest/data/websocket/1485976125.95-user_typing.json create mode 100644 _pytest/data/websocket/1485976129.49-user_typing.json create mode 100644 _pytest/data/websocket/1485976133.0-user_typing.json create mode 100644 _pytest/data/websocket/1485976137.51-user_typing.json create mode 100644 _pytest/data/websocket/1485976137.95-message.json create mode 100644 _pytest/data/websocket/1485976138.73-user_typing.json create mode 100644 _pytest/data/websocket/1485976141.79-user_typing.json create mode 100644 _pytest/data/websocket/1485976145.8-user_typing.json create mode 100644 _pytest/data/websocket/1485976149.89-user_typing.json create mode 100644 _pytest/data/websocket/1485976151.6-message.json create mode 100644 _pytest/data/websocket/1485976157.18-message.json create mode 100644 _pytest/data/websocket/1485976157.8-user_typing.json create mode 100644 _pytest/data/websocket/1485976161.29-user_typing.json create mode 100644 _pytest/data/websocket/1485976161.75-message.json create mode 100644 _pytest/data/websocket/1485976182.59-channel_archive.json create mode 100644 _pytest/data/websocket/1485976186.62-channel_unarchive.json create mode 100644 _pytest/data/websocket/1485976236.58-message.json create mode 100644 _pytest/data/websocket/1485976236.67-file_comment_added.json create mode 100644 _pytest/data/websocket/1485976248.57-message.json create mode 100644 _pytest/data/websocket/1485976248.65-file_comment_edited.json create mode 100644 _pytest/data/websocket/1485976267.81-reconnect_url.json diff --git a/_pytest/data/websocket/1485975367.79-reconnect_url.json b/_pytest/data/websocket/1485975367.79-reconnect_url.json new file mode 100644 index 0000000..4b53a4e --- /dev/null +++ b/_pytest/data/websocket/1485975367.79-reconnect_url.json @@ -0,0 +1 @@ +{u'url': u'wss://mpmulti-gvop.slack-msgs.com/websocket/tamYoLuX4lU-WBS7cFe2RCh8kqeO86F0Mi1RKFVfk7FM-QNu1KD7HiMBStfdLkwkKHmtyTACNE6SONtoQRTbnC0q9fAoLHPl76Y7y9IhCve6VKs2KNLmRH37WutXTBsj3b9HvF79VySlPgAwVXZeH0lgfDDk_RAY9l_dJ8u-jSs=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975408.19-user_typing.json b/_pytest/data/websocket/1485975408.19-user_typing.json new file mode 100644 index 0000000..572b0fd --- /dev/null +++ b/_pytest/data/websocket/1485975408.19-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'D3ZEQULHZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975412.74-user_typing.json b/_pytest/data/websocket/1485975412.74-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485975412.74-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975420.36-user_typing.json b/_pytest/data/websocket/1485975420.36-user_typing.json new file mode 100644 index 0000000..572b0fd --- /dev/null +++ b/_pytest/data/websocket/1485975420.36-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'D3ZEQULHZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975421.33-message.json b/_pytest/data/websocket/1485975421.33-message.json new file mode 100644 index 0000000..5dd2924 --- /dev/null +++ b/_pytest/data/websocket/1485975421.33-message.json @@ -0,0 +1 @@ +{u'text': u'hi bob', u'ts': u'1485975421.000002', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'D3ZEQULHZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975421.42-desktop_notification.json b/_pytest/data/websocket/1485975421.42-desktop_notification.json new file mode 100644 index 0000000..fedda07 --- /dev/null +++ b/_pytest/data/websocket/1485975421.42-desktop_notification.json @@ -0,0 +1 @@ +{u'launchUri': u'slack://channel?id=D3ZEQULHZ&message=1485975421000002&team=T3YS5EAL9', u'subtitle': u'alice', u'is_shared': False, u'title': u'weeslacktest', u'ssbFilename': u'knock_brush.mp3', u'avatarImage': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png', u'imageUri': None, u'content': u'hi bob', u'event_ts': u'1485975421.875655', u'msg': u'1485975421.000002', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'desktop_notification', u'channel': u'D3ZEQULHZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975426.55-reaction_added.json b/_pytest/data/websocket/1485975426.55-reaction_added.json new file mode 100644 index 0000000..2a1347c --- /dev/null +++ b/_pytest/data/websocket/1485975426.55-reaction_added.json @@ -0,0 +1 @@ +{u'reaction': u'grinning', u'event_ts': u'1485975426.875724', u'ts': u'1485975426.000003', u'item': {u'type': u'message', u'ts': u'1485975421.000002', u'channel': u'D3ZEQULHZ'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_added'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975428.76-reaction_removed.json b/_pytest/data/websocket/1485975428.76-reaction_removed.json new file mode 100644 index 0000000..efa1605 --- /dev/null +++ b/_pytest/data/websocket/1485975428.76-reaction_removed.json @@ -0,0 +1 @@ +{u'reaction': u'grinning', u'event_ts': u'1485975428.875759', u'ts': u'1485975428.000004', u'item': {u'type': u'message', u'ts': u'1485975421.000002', u'channel': u'D3ZEQULHZ'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_removed'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975458.35-channel_created.json b/_pytest/data/websocket/1485975458.35-channel_created.json new file mode 100644 index 0000000..bd197df --- /dev/null +++ b/_pytest/data/websocket/1485975458.35-channel_created.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975458.876318', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_created', u'channel': {u'is_shared': False, u'is_channel': True, u'created': 1485975458, u'creator': u'U407ABLLW', u'is_org_shared': False, u'id': u'C3ZJKCGTU', u'name': u'some-channel'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975458.87-channel_joined.json b/_pytest/data/websocket/1485975458.87-channel_joined.json new file mode 100644 index 0000000..fffa5c5 --- /dev/null +++ b/_pytest/data/websocket/1485975458.87-channel_joined.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975458.876336', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_joined', u'channel': {u'topic': {u'last_set': 0, u'value': u'', u'creator': u''}, u'is_general': False, u'name': u'some-channel', u'is_channel': True, u'created': 1485975458, u'is_member': True, u'is_archived': False, u'creator': u'U407ABLLW', u'members': [u'U3ZKBBDL5', u'U407ABLLW'], u'unread_count': 0, u'previous_names': [], u'purpose': {u'last_set': 1485975458, u'value': u'who knows?', u'creator': u'U407ABLLW'}, u'unread_count_display': 0, u'last_read': u'1485975458.000003', u'id': u'C3ZJKCGTU', u'latest': {u'text': u'<@U407ABLLW|alice> set the channel purpose: who knows?', u'ts': u'1485975458.000003', u'subtype': u'channel_purpose', u'user': u'U407ABLLW', u'type': u'message', u'purpose': u'who knows?'}}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975458.87-message.json b/_pytest/data/websocket/1485975458.87-message.json new file mode 100644 index 0000000..80e961f --- /dev/null +++ b/_pytest/data/websocket/1485975458.87-message.json @@ -0,0 +1 @@ +{u'user_profile': {u'avatar_hash': u'g6f7a5bf7eb7', u'first_name': u'Second', u'real_name': u'Second Testuser', u'name': u'bob', u'image_72': u'https://secure.gravatar.com/avatar/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png'}, u'inviter': u'U407ABLLW', u'ts': u'1485975458.000004', u'subtype': u'channel_join', u'text': u'<@U3ZKBBDL5|bob> has joined the channel', u'user': u'U3ZKBBDL5', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C3ZJKCGTU'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975462.62-update_thread_state.json b/_pytest/data/websocket/1485975462.62-update_thread_state.json new file mode 100644 index 0000000..6a74d95 --- /dev/null +++ b/_pytest/data/websocket/1485975462.62-update_thread_state.json @@ -0,0 +1 @@ +{u'mention_count': 0, u'event_ts': u'1485975458.876340', u'timestamp': u'1485975458.741354', u'has_unreads': False, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'update_thread_state'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975462.62-user_typing.json b/_pytest/data/websocket/1485975462.62-user_typing.json new file mode 100644 index 0000000..5169af3 --- /dev/null +++ b/_pytest/data/websocket/1485975462.62-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C3ZJKCGTU'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975476.62-channel_deleted.json b/_pytest/data/websocket/1485975476.62-channel_deleted.json new file mode 100644 index 0000000..fe1ad9e --- /dev/null +++ b/_pytest/data/websocket/1485975476.62-channel_deleted.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975476.876638', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_deleted', u'channel': u'C3ZJKCGTU'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975476.86-group_join.json b/_pytest/data/websocket/1485975476.86-group_join.json new file mode 100644 index 0000000..fdc6878 --- /dev/null +++ b/_pytest/data/websocket/1485975476.86-group_join.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_join', u'user': u'U407ABLLW', u'channel': u'G409GKN9M', u'ts': u'1485975476.000002'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975476.86-group_joined.json b/_pytest/data/websocket/1485975476.86-group_joined.json new file mode 100644 index 0000000..b4316f3 --- /dev/null +++ b/_pytest/data/websocket/1485975476.86-group_joined.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975476.876646', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_joined', u'channel': {u'name': u'some-channel', u'creator': u'U407ABLLW', u'is_mpim': False, u'is_archived': False, u'created': 1485975458, u'is_group': True, u'topic': {u'last_set': 1485975476, u'value': u'', u'creator': u'U407ABLLW'}, u'purpose': {u'last_set': 1485975476, u'value': u'who knows?', u'creator': u'U407ABLLW'}, u'members': [u'U407ABLLW', u'U3ZKBBDL5'], u'is_starred': False, u'id': u'G409GKN9M'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975487.69-group_history_changed.json b/_pytest/data/websocket/1485975487.69-group_history_changed.json new file mode 100644 index 0000000..8eabb23 --- /dev/null +++ b/_pytest/data/websocket/1485975487.69-group_history_changed.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975476.876650', u'is_mpim': False, u'ts': u'1485975476.000004', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_history_changed', u'channel': u'G409GKN9M', u'latest': u'1485975476.876632'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975487.69-group_join.json b/_pytest/data/websocket/1485975487.69-group_join.json new file mode 100644 index 0000000..4732dd4 --- /dev/null +++ b/_pytest/data/websocket/1485975487.69-group_join.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_join', u'user': u'U3ZKBBDL5', u'channel': u'G409GKN9M', u'ts': u'1485975476.000003'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975493.19-group_joined.json b/_pytest/data/websocket/1485975493.19-group_joined.json new file mode 100644 index 0000000..b4431c2 --- /dev/null +++ b/_pytest/data/websocket/1485975493.19-group_joined.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975492.876964', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_joined', u'channel': {u'topic': {u'last_set': 0, u'value': u'', u'creator': u''}, u'name': u'some-private-channel', u'last_read': u'1485975492.000002', u'creator': u'U407ABLLW', u'is_mpim': False, u'is_archived': False, u'created': 1485975492, u'is_group': True, u'members': [u'U3ZKBBDL5', u'U407ABLLW'], u'unread_count': 0, u'is_open': True, u'purpose': {u'last_set': 0, u'value': u'', u'creator': u''}, u'unread_count_display': 0, u'id': u'G3ZJKP7GA', u'latest': {u'text': u'<@U407ABLLW|alice> has joined the group', u'type': u'message', u'user': u'U407ABLLW', u'ts': u'1485975492.000002', u'subtype': u'group_join'}}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975493.23-message.json b/_pytest/data/websocket/1485975493.23-message.json new file mode 100644 index 0000000..e82d057 --- /dev/null +++ b/_pytest/data/websocket/1485975493.23-message.json @@ -0,0 +1 @@ +{u'user_profile': {u'avatar_hash': u'g6f7a5bf7eb7', u'first_name': u'Second', u'real_name': u'Second Testuser', u'name': u'bob', u'image_72': u'https://secure.gravatar.com/avatar/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png'}, u'inviter': u'U407ABLLW', u'ts': u'1485975492.000003', u'subtype': u'group_join', u'text': u'<@U3ZKBBDL5|bob> has joined the group', u'user': u'U3ZKBBDL5', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'G3ZJKP7GA'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975547.75-message.json b/_pytest/data/websocket/1485975547.75-message.json new file mode 100644 index 0000000..80b233b --- /dev/null +++ b/_pytest/data/websocket/1485975547.75-message.json @@ -0,0 +1 @@ +{u'user_profile': {u'avatar_hash': u'gfd0ce7a168d', u'first_name': u'First', u'real_name': u'First Testuser', u'name': u'alice', u'image_72': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png'}, u'text': u"<@U407ABLLW|alice> set the channel's purpose: seekret", u'ts': u'1485975492.000004', u'subtype': u'group_purpose', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'G3ZJKP7GA', u'purpose': u'seekret'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975547.75-update_thread_state.json b/_pytest/data/websocket/1485975547.75-update_thread_state.json new file mode 100644 index 0000000..08de51c --- /dev/null +++ b/_pytest/data/websocket/1485975547.75-update_thread_state.json @@ -0,0 +1 @@ +{u'mention_count': 0, u'event_ts': u'1485975492.876967', u'timestamp': u'1485975493.013414', u'has_unreads': False, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'update_thread_state'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975547.83-reconnect_url.json b/_pytest/data/websocket/1485975547.83-reconnect_url.json new file mode 100644 index 0000000..f8ec065 --- /dev/null +++ b/_pytest/data/websocket/1485975547.83-reconnect_url.json @@ -0,0 +1 @@ +{u'url': u'wss://mpmulti-t63z.slack-msgs.com/websocket/I4VgKhpT11zUwKt_Wajw_phIbuzM7xTy0V0DVLb6dyPDkqGU5-497Jn3bOGncotPX4QhfZepKVtlUkrSWCqMcbjH_vjRo91HBvzvk4nJoScjn6KJatZ6vpzokG44Ee-vNMsxXLeedcbraFAzCrLlODGRqvdqxUQDQFMVES0XHP8=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975606.59-team_join.json b/_pytest/data/websocket/1485975606.59-team_join.json new file mode 100644 index 0000000..23f780f --- /dev/null +++ b/_pytest/data/websocket/1485975606.59-team_join.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975606.879253', u'cache_ts': 1485975606, u'type': u'team_join', u'user': {u'status': None, u'profile': {u'first_name': u'Chuck', u'last_name': u'Testuser', u'fields': None, u'real_name': u'Chuck Testuser', u'image_24': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png', u'real_name_normalized': u'Chuck Testuser', u'image_512': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png', u'image_32': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png', u'image_48': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png', u'image_72': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png', u'avatar_hash': u'g7f87f7015f8', u'image_192': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png'}, u'tz': u'America/Los_Angeles', u'name': u'chuck', u'presence': u'away', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Chuck Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975606.67-message.json b/_pytest/data/websocket/1485975606.67-message.json new file mode 100644 index 0000000..8aec187 --- /dev/null +++ b/_pytest/data/websocket/1485975606.67-message.json @@ -0,0 +1 @@ +{u'user_profile': {u'avatar_hash': u'g7f87f7015f8', u'first_name': u'Chuck', u'real_name': u'Chuck Testuser', u'name': u'chuck', u'image_72': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png'}, u'text': u'<@U4096CBHC|chuck> has joined the channel', u'ts': u'1485975606.000003', u'subtype': u'channel_join', u'user': u'U4096CBHC', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975606.75-im_created.json b/_pytest/data/websocket/1485975606.75-im_created.json new file mode 100644 index 0000000..295929f --- /dev/null +++ b/_pytest/data/websocket/1485975606.75-im_created.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975606.879278', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'im_created', u'user': u'U4096CBHC', u'channel': {u'last_read': u'0000000000.000000', u'created': 1485975606, u'is_org_shared': False, u'unread_count': 0, u'is_open': False, u'user': u'U4096CBHC', u'unread_count_display': 0, u'is_im': True, u'id': u'D409J34CF', u'latest': None}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975606.75-im_open.json b/_pytest/data/websocket/1485975606.75-im_open.json new file mode 100644 index 0000000..883a9ee --- /dev/null +++ b/_pytest/data/websocket/1485975606.75-im_open.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975606.879280', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'im_open', u'user': u'U4096CBHC', u'channel': u'D409J34CF'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975611.29-presence_change.json b/_pytest/data/websocket/1485975611.29-presence_change.json new file mode 100644 index 0000000..2395dc0 --- /dev/null +++ b/_pytest/data/websocket/1485975611.29-presence_change.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'presence_change', u'user': u'U4096CBHC', u'presence': u'active'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975640.5-user_change.json b/_pytest/data/websocket/1485975640.5-user_change.json new file mode 100644 index 0000000..a722cfc --- /dev/null +++ b/_pytest/data/websocket/1485975640.5-user_change.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975640.879906', u'cache_ts': 1485975640, u'type': u'user_change', u'user': {u'status': None, u'profile': {u'first_name': u'Charles', u'last_name': u'Testuser', u'fields': [], u'real_name': u'Charles Testuser', u'image_24': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png', u'real_name_normalized': u'Charles Testuser', u'image_512': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png', u'image_32': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png', u'image_48': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png', u'image_72': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png', u'avatar_hash': u'g7f87f7015f8', u'image_192': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png'}, u'tz': u'America/Los_Angeles', u'name': u'chuck', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Charles Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975669.14-user_change.json b/_pytest/data/websocket/1485975669.14-user_change.json new file mode 100644 index 0000000..2faf007 --- /dev/null +++ b/_pytest/data/websocket/1485975669.14-user_change.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975668.880329', u'cache_ts': 1485975669, u'type': u'user_change', u'user': {u'status': None, u'profile': {u'first_name': u'Charles', u'last_name': u'Testuser', u'fields': [], u'real_name': u'Charles Testuser', u'image_24': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png', u'real_name_normalized': u'Charles Testuser', u'image_512': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png', u'image_32': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png', u'image_48': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png', u'image_72': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png', u'avatar_hash': u'g7f87f7015f8', u'image_192': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png'}, u'tz': u'America/Los_Angeles', u'name': u'charles', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Charles Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975675.81-presence_change.json b/_pytest/data/websocket/1485975675.81-presence_change.json new file mode 100644 index 0000000..2395dc0 --- /dev/null +++ b/_pytest/data/websocket/1485975675.81-presence_change.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'presence_change', u'user': u'U4096CBHC', u'presence': u'active'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975690.59-mpim_joined.json b/_pytest/data/websocket/1485975690.59-mpim_joined.json new file mode 100644 index 0000000..c4e0bc0 --- /dev/null +++ b/_pytest/data/websocket/1485975690.59-mpim_joined.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975690.880722', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'mpim_joined', u'channel': {u'topic': {u'last_set': 1485975690, u'value': u'Group messaging', u'creator': u'U407ABLLW'}, u'name': u'mpdm-bob--alice--charles-1', u'last_read': u'0000000000.000000', u'creator': u'U407ABLLW', u'is_mpim': True, u'is_archived': False, u'created': 1485975690, u'is_group': True, u'members': [u'U407ABLLW', u'U3ZKBBDL5', u'U4096CBHC'], u'unread_count': 0, u'is_open': False, u'purpose': {u'last_set': 1485975690, u'value': u'Group messaging with: @bob @alice @charles', u'creator': u'U407ABLLW'}, u'unread_count_display': 0, u'id': u'G3ZGMF4RZ', u'latest': None}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975690.67-group_joined.json b/_pytest/data/websocket/1485975690.67-group_joined.json new file mode 100644 index 0000000..3968cc3 --- /dev/null +++ b/_pytest/data/websocket/1485975690.67-group_joined.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975690.880723', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_joined', u'channel': {u'topic': {u'last_set': 1485975690, u'value': u'Group messaging', u'creator': u'U407ABLLW'}, u'name': u'mpdm-bob--alice--charles-1', u'last_read': u'0000000000.000000', u'creator': u'U407ABLLW', u'is_mpim': True, u'is_archived': False, u'created': 1485975690, u'is_group': True, u'members': [u'U407ABLLW', u'U3ZKBBDL5', u'U4096CBHC'], u'unread_count': 0, u'is_open': False, u'purpose': {u'last_set': 1485975690, u'value': u'Group messaging with: @bob @alice @charles', u'creator': u'U407ABLLW'}, u'unread_count_display': 0, u'id': u'G3ZGMF4RZ', u'latest': None}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975690.67-update_thread_state.json b/_pytest/data/websocket/1485975690.67-update_thread_state.json new file mode 100644 index 0000000..c9ba02a --- /dev/null +++ b/_pytest/data/websocket/1485975690.67-update_thread_state.json @@ -0,0 +1 @@ +{u'mention_count': 0, u'event_ts': u'1485975690.880724', u'timestamp': u'1485975690.537094', u'has_unreads': False, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'update_thread_state'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975697.58-user_typing.json b/_pytest/data/websocket/1485975697.58-user_typing.json new file mode 100644 index 0000000..b909fa3 --- /dev/null +++ b/_pytest/data/websocket/1485975697.58-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'G3ZGMF4RZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975698.45-message.json b/_pytest/data/websocket/1485975698.45-message.json new file mode 100644 index 0000000..777d55f --- /dev/null +++ b/_pytest/data/websocket/1485975698.45-message.json @@ -0,0 +1 @@ +{u'text': u'surely', u'ts': u'1485975698.000002', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'G3ZGMF4RZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975701.72-reaction_added.json b/_pytest/data/websocket/1485975701.72-reaction_added.json new file mode 100644 index 0000000..e9328c7 --- /dev/null +++ b/_pytest/data/websocket/1485975701.72-reaction_added.json @@ -0,0 +1 @@ +{u'reaction': u'unamused', u'event_ts': u'1485975701.880957', u'ts': u'1485975701.000003', u'item': {u'type': u'message', u'ts': u'1485975698.000002', u'channel': u'G3ZGMF4RZ'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_added'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975703.22-reaction_removed.json b/_pytest/data/websocket/1485975703.22-reaction_removed.json new file mode 100644 index 0000000..1e0e0df --- /dev/null +++ b/_pytest/data/websocket/1485975703.22-reaction_removed.json @@ -0,0 +1 @@ +{u'reaction': u'unamused', u'event_ts': u'1485975703.880979', u'ts': u'1485975703.000004', u'item': {u'type': u'message', u'ts': u'1485975698.000002', u'channel': u'G3ZGMF4RZ'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_removed'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975715.79-message.json b/_pytest/data/websocket/1485975715.79-message.json new file mode 100644 index 0000000..2668055 --- /dev/null +++ b/_pytest/data/websocket/1485975715.79-message.json @@ -0,0 +1 @@ +{u'username': u'<@U407ABLLW|alice>', u'source_team': u'T3YS5EAL9', u'display_as_bot': False, u'text': u'<@U407ABLLW|alice> uploaded a file: ', u'channel': u'G3ZGMF4RZ', u'user_profile': {u'avatar_hash': u'gfd0ce7a168d', u'first_name': u'First', u'real_name': u'First Testuser', u'name': u'alice', u'image_72': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png'}, u'upload': True, u'ts': u'1485975715.000005', u'subtype': u'file_share', u'user': u'U407ABLLW', u'file': {u'filetype': u'text', u'lines_more': 0, u'channels': [], u'display_as_bot': False, u'id': u'F3ZLY6K5J', u'size': 14, u'title': u'Untitled', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZLY6K5J/-.txt', u'ims': [], u'preview': u'some code here', u'external_type': u'', u'edit_link': u'https://weeslacktest.slack.com/files/alice/F3ZLY6K5J/-.txt/edit', u'username': u'', u'timestamp': 1485975715, u'public_url_shared': False, u'editable': True, u'preview_is_truncated': False, u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZLY6K5J/download/-.txt', u'user': u'U407ABLLW', u'groups': [], u'is_public': False, u'pretty_type': u'Plain Text', u'name': u'-.txt', u'mimetype': u'text/plain', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZLY6K5J-39c2c4f739', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZLY6K5J/-.txt', u'is_external': False, u'created': 1485975715, u'lines': 1, u'comments_count': 0, u'mode': u'snippet', u'preview_highlight': u'
\n
\n
some code here
\n
\n
\n'}, u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'user_team': u'T3YS5EAL9', u'bot_id': None} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975715.87-file_shared.json b/_pytest/data/websocket/1485975715.87-file_shared.json new file mode 100644 index 0000000..2149f8c --- /dev/null +++ b/_pytest/data/websocket/1485975715.87-file_shared.json @@ -0,0 +1 @@ +{u'user_id': u'U407ABLLW', u'event_ts': u'1485975715.881302', u'file_id': u'F3ZLY6K5J', u'file': {u'id': u'F3ZLY6K5J'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_shared'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975723.85-message.json b/_pytest/data/websocket/1485975723.85-message.json new file mode 100644 index 0000000..0e52b67 --- /dev/null +++ b/_pytest/data/websocket/1485975723.85-message.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975723.881563', u'ts': u'1485975723.000006', u'subtype': u'message_deleted', u'hidden': True, u'deleted_ts': u'1485975698.000002', u'type': u'message', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'channel': u'G3ZGMF4RZ', u'previous_message': {u'text': u'surely', u'type': u'message', u'user': u'U407ABLLW', u'ts': u'1485975698.000002'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975727.84-reconnect_url.json b/_pytest/data/websocket/1485975727.84-reconnect_url.json new file mode 100644 index 0000000..d1417e2 --- /dev/null +++ b/_pytest/data/websocket/1485975727.84-reconnect_url.json @@ -0,0 +1 @@ +{u'url': u'wss://mpmulti-824w.slack-msgs.com/websocket/Pl7CWLLi3w1S0gHESj79_JySeV0ho6vZRl1usylFoDVBWTj332mKkaLBR6OdrDqRDlYPnwwnh9TqlqOMVajfyHMH_Q02oVU14YJS_ao5_nEJBBd58vDB3XkKfGdPRZNgP2Id05xTpEy9izt9EF_BdDaDaTU9nGc5c2ggnuS9gos=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975735.91-group_archive.json b/_pytest/data/websocket/1485975735.91-group_archive.json new file mode 100644 index 0000000..05dd53e --- /dev/null +++ b/_pytest/data/websocket/1485975735.91-group_archive.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975735.881780', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_archive', u'ts': u'1485975735.000005', u'channel': u'G409GKN9M'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975735.99-message.json b/_pytest/data/websocket/1485975735.99-message.json new file mode 100644 index 0000000..3ca5b71 --- /dev/null +++ b/_pytest/data/websocket/1485975735.99-message.json @@ -0,0 +1 @@ +{u'user_profile': {u'avatar_hash': u'gfd0ce7a168d', u'first_name': u'First', u'real_name': u'First Testuser', u'name': u'alice', u'image_72': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png'}, u'text': u'<@U407ABLLW|alice> archived the private channel', u'ts': u'1485975735.000006', u'subtype': u'group_archive', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'G409GKN9M'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975736.08-message.json b/_pytest/data/websocket/1485975736.08-message.json new file mode 100644 index 0000000..172f553 --- /dev/null +++ b/_pytest/data/websocket/1485975736.08-message.json @@ -0,0 +1 @@ +{u'text': u'<@U407ABLLW|alice> archived the private channel ', u'ts': u'1485975735.000002', u'user': u'USLACKBOT', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'D3ZK1D8JY'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975738.1-desktop_notification.json b/_pytest/data/websocket/1485975738.1-desktop_notification.json new file mode 100644 index 0000000..56e72f9 --- /dev/null +++ b/_pytest/data/websocket/1485975738.1-desktop_notification.json @@ -0,0 +1 @@ +{u'launchUri': u'slack://channel?id=D3ZK1D8JY&message=1485975735000002&team=T3YS5EAL9', u'subtitle': u'slackbot', u'is_shared': False, u'title': u'weeslacktest', u'ssbFilename': u'knock_brush.mp3', u'avatarImage': u'https://a.slack-edge.com/66f9/img/slackbot_192.png', u'imageUri': None, u'content': u'@alice archived the private channel some-channel', u'event_ts': u'1485975737.881829', u'msg': u'1485975735.000002', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'desktop_notification', u'channel': u'D3ZK1D8JY'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975747.17-channel_created.json b/_pytest/data/websocket/1485975747.17-channel_created.json new file mode 100644 index 0000000..453552d --- /dev/null +++ b/_pytest/data/websocket/1485975747.17-channel_created.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975746.882009', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_created', u'channel': {u'is_shared': False, u'is_channel': True, u'created': 1485975747, u'creator': u'U407ABLLW', u'is_org_shared': False, u'id': u'C3ZM8JTD3', u'name': u'some-channel2'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975757.63-channel_rename.json b/_pytest/data/websocket/1485975757.63-channel_rename.json new file mode 100644 index 0000000..ff5e537 --- /dev/null +++ b/_pytest/data/websocket/1485975757.63-channel_rename.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975757.882178', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_rename', u'channel': {u'created': u'1485975747', u'id': u'C3ZM8JTD3', u'is_channel': True, u'name': u'some-channel2-renamed'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975771.6-presence_change.json b/_pytest/data/websocket/1485975771.6-presence_change.json new file mode 100644 index 0000000..e7252bc --- /dev/null +++ b/_pytest/data/websocket/1485975771.6-presence_change.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'presence_change', u'user': u'U407ABLLW', u'presence': u'away'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975774.62-presence_change.json b/_pytest/data/websocket/1485975774.62-presence_change.json new file mode 100644 index 0000000..a180d76 --- /dev/null +++ b/_pytest/data/websocket/1485975774.62-presence_change.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'presence_change', u'user': u'U407ABLLW', u'presence': u'active'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975822.17-user_typing.json b/_pytest/data/websocket/1485975822.17-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485975822.17-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975824.48-message.json b/_pytest/data/websocket/1485975824.48-message.json new file mode 100644 index 0000000..bdcee99 --- /dev/null +++ b/_pytest/data/websocket/1485975824.48-message.json @@ -0,0 +1 @@ +{u'text': u'generally, yep!', u'ts': u'1485975824.000004', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975836.23-message.json b/_pytest/data/websocket/1485975836.23-message.json new file mode 100644 index 0000000..5ac0bac --- /dev/null +++ b/_pytest/data/websocket/1485975836.23-message.json @@ -0,0 +1 @@ +{u'thread_ts': u'1485975824.000004', u'text': u'have you met my friend threads?', u'ts': u'1485975835.000005', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975836.31-message.json b/_pytest/data/websocket/1485975836.31-message.json new file mode 100644 index 0000000..a90ce8b --- /dev/null +++ b/_pytest/data/websocket/1485975836.31-message.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975835.883772', u'ts': u'1485975835.000006', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'subtype': u'message_replied', u'message': {u'thread_ts': u'1485975824.000004', u'text': u'generally, yep!', u'ts': u'1485975824.000004', u'reply_count': 1, u'user': u'U407ABLLW', u'replies': [{u'user': u'U407ABLLW', u'ts': u'1485975835.000005'}], u'type': u'message'}, u'type': u'message', u'hidden': True, u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975842.1-message.json b/_pytest/data/websocket/1485975842.1-message.json new file mode 100644 index 0000000..c8b202b --- /dev/null +++ b/_pytest/data/websocket/1485975842.1-message.json @@ -0,0 +1 @@ +{u'thread_ts': u'1485975824.000004', u'text': u'react if yes', u'ts': u'1485975841.000007', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975842.18-message.json b/_pytest/data/websocket/1485975842.18-message.json new file mode 100644 index 0000000..d77fd22 --- /dev/null +++ b/_pytest/data/websocket/1485975842.18-message.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975841.883922', u'ts': u'1485975841.000008', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'subtype': u'message_replied', u'message': {u'thread_ts': u'1485975824.000004', u'text': u'generally, yep!', u'ts': u'1485975824.000004', u'reply_count': 2, u'user': u'U407ABLLW', u'replies': [{u'user': u'U407ABLLW', u'ts': u'1485975835.000005'}, {u'user': u'U407ABLLW', u'ts': u'1485975841.000007'}], u'type': u'message'}, u'type': u'message', u'hidden': True, u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975850.32-mpim_open.json b/_pytest/data/websocket/1485975850.32-mpim_open.json new file mode 100644 index 0000000..908ee49 --- /dev/null +++ b/_pytest/data/websocket/1485975850.32-mpim_open.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975849.884091', u'is_mpim': True, u'user': u'U3ZKBBDL5', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'mpim_open', u'channel': u'G3ZGMF4RZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975850.45-group_open.json b/_pytest/data/websocket/1485975850.45-group_open.json new file mode 100644 index 0000000..60dd1d4 --- /dev/null +++ b/_pytest/data/websocket/1485975850.45-group_open.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975849.884092', u'is_mpim': True, u'user': u'U3ZKBBDL5', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_open', u'channel': u'G3ZGMF4RZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975858.56-reaction_added.json b/_pytest/data/websocket/1485975858.56-reaction_added.json new file mode 100644 index 0000000..6b10607 --- /dev/null +++ b/_pytest/data/websocket/1485975858.56-reaction_added.json @@ -0,0 +1 @@ +{u'reaction': u'stuck_out_tongue', u'event_ts': u'1485975858.884268', u'item': {u'type': u'message', u'ts': u'1485975841.000007', u'channel': u'C407ABS94'}, u'user': u'U4096CBHC', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_added'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975890.7-bot_added.json b/_pytest/data/websocket/1485975890.7-bot_added.json new file mode 100644 index 0000000..8ca953b --- /dev/null +++ b/_pytest/data/websocket/1485975890.7-bot_added.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975890.885679', u'bot': {u'deleted': False, u'icons': {u'image_36': u'https://a.slack-edge.com/12b5a/plugins/tester/assets/service_36.png', u'image_48': u'https://a.slack-edge.com/12b5a/plugins/tester/assets/service_48.png', u'image_72': u'https://a.slack-edge.com/12b5a/plugins/tester/assets/service_72.png'}, u'app_id': u'A0F7XDUAZ', u'id': u'B3YTBU6L8', u'name': u'incoming-webhook'}, u'cache_ts': 1485975890, u'type': u'bot_added', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975890.78-message.json b/_pytest/data/websocket/1485975890.78-message.json new file mode 100644 index 0000000..414f544 --- /dev/null +++ b/_pytest/data/websocket/1485975890.78-message.json @@ -0,0 +1 @@ +{u'text': u'added an integration to this channel: ', u'ts': u'1485975890.000009', u'subtype': u'bot_add', u'user': u'U4096CBHC', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94', u'bot_id': u'B3YTBU6L8'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975896.16-message.json b/_pytest/data/websocket/1485975896.16-message.json new file mode 100644 index 0000000..2d0499f --- /dev/null +++ b/_pytest/data/websocket/1485975896.16-message.json @@ -0,0 +1 @@ +{u'text': u'disabled an integration in this channel: ', u'ts': u'1485975896.000010', u'subtype': u'bot_disable', u'user': u'U4096CBHC', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94', u'bot_id': u'B3YTBU6L8'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975907.84-reconnect_url.json b/_pytest/data/websocket/1485975907.84-reconnect_url.json new file mode 100644 index 0000000..f26d883 --- /dev/null +++ b/_pytest/data/websocket/1485975907.84-reconnect_url.json @@ -0,0 +1 @@ +{u'url': u'wss://mpmulti-1r8c.slack-msgs.com/websocket/8atJCMnYZ10YaqNai-zwEaNMfkwh85XwRpc0MYVVOzAojnPcrx8SboF-NBWeep6Hy7arytqEffr_Fh7mPrDagwEGwbOeX-OH3OLlubVjpC2cCLWwm2jN3rEZcq8A4j0tpPP56GZ84jbLn4BKvDhKkkKhRXQhuYtC7kskaXXLTbk=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975926.59-user_change.json b/_pytest/data/websocket/1485975926.59-user_change.json new file mode 100644 index 0000000..f3ef251 --- /dev/null +++ b/_pytest/data/websocket/1485975926.59-user_change.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975926.886492', u'cache_ts': 1485975926, u'type': u'user_change', u'user': {u'status': None, u'profile': {u'first_name': u'Charles', u'last_name': u'Testuser', u'fields': [], u'real_name': u'Charles Testuser', u'image_24': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_original': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_original.png', u'real_name_normalized': u'Charles Testuser', u'image_512': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_32': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_48': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_72': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'avatar_hash': u'5af404f7d4b7', u'image_1024': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_192': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png'}, u'tz': u'America/Los_Angeles', u'name': u'charles', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Charles Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975930.81-user_change.json b/_pytest/data/websocket/1485975930.81-user_change.json new file mode 100644 index 0000000..40ed3da --- /dev/null +++ b/_pytest/data/websocket/1485975930.81-user_change.json @@ -0,0 +1 @@ +{u'event_ts': u'1485975930.886576', u'cache_ts': 1485975930, u'type': u'user_change', u'user': {u'status': None, u'profile': {u'first_name': u'Charles', u'last_name': u'Testuser', u'fields': [], u'real_name': u'Charles Testuser', u'image_24': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_original': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_original.png', u'real_name_normalized': u'Charles Testuser', u'image_512': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_32': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_48': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_72': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'avatar_hash': u'5af404f7d4b7', u'image_1024': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_192': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png'}, u'tz': u'America/Los_Angeles', u'name': u'charles', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Charles Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975956.38-file_public.json b/_pytest/data/websocket/1485975956.38-file_public.json new file mode 100644 index 0000000..3f44646 --- /dev/null +++ b/_pytest/data/websocket/1485975956.38-file_public.json @@ -0,0 +1 @@ +{u'user_id': u'U407ABLLW', u'event_ts': u'1485975956.887078', u'file_id': u'F3ZJQTA66', u'file': {u'id': u'F3ZJQTA66'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_public'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975956.47-file_shared.json b/_pytest/data/websocket/1485975956.47-file_shared.json new file mode 100644 index 0000000..53296ba --- /dev/null +++ b/_pytest/data/websocket/1485975956.47-file_shared.json @@ -0,0 +1 @@ +{u'user_id': u'U407ABLLW', u'event_ts': u'1485975956.887081', u'file_id': u'F3ZJQTA66', u'file': {u'id': u'F3ZJQTA66'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_shared'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975956.47-message.json b/_pytest/data/websocket/1485975956.47-message.json new file mode 100644 index 0000000..03047f4 --- /dev/null +++ b/_pytest/data/websocket/1485975956.47-message.json @@ -0,0 +1 @@ +{u'username': u'<@U407ABLLW|alice>', u'source_team': u'T3YS5EAL9', u'display_as_bot': False, u'text': u'<@U407ABLLW|alice> uploaded a file: ', u'channel': u'C407ABS94', u'user_profile': {u'avatar_hash': u'gfd0ce7a168d', u'first_name': u'First', u'real_name': u'First Testuser', u'name': u'alice', u'image_72': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png'}, u'upload': True, u'ts': u'1485975956.000011', u'subtype': u'file_share', u'user': u'U407ABLLW', u'file': {u'groups': [], u'filetype': u'png', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'thumb_64': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png', u'size': 68, u'original_h': 1, u'thumb_360_w': 1, u'title': u'1x1.png', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png', u'thumb_360': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png', u'id': u'F3ZJQTA66', u'ims': [], u'thumb_80': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png', u'thumb_360_h': 1, u'external_type': u'', u'username': u'', u'timestamp': 1485975955, u'public_url_shared': False, u'editable': False, u'thumb_160': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png', u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png', u'user': u'U407ABLLW', u'image_exif_rotation': 1, u'is_public': True, u'pretty_type': u'PNG', u'name': u'1x1.png', u'mimetype': u'image/png', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png', u'is_external': False, u'created': 1485975955, u'original_w': 1, u'comments_count': 0, u'mode': u'hosted'}, u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'user_team': u'T3YS5EAL9', u'bot_id': None} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975978.79-message.json b/_pytest/data/websocket/1485975978.79-message.json new file mode 100644 index 0000000..339504c --- /dev/null +++ b/_pytest/data/websocket/1485975978.79-message.json @@ -0,0 +1 @@ +{u'username': u'<@U407ABLLW|alice>', u'display_as_bot': False, u'text': u'<@U407ABLLW|alice> shared a file: ', u'upload': False, u'ts': u'1485975978.000012', u'subtype': u'file_share', u'user': u'U407ABLLW', u'file': {u'filetype': u'space', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'id': u'F3YTCL8TA', u'size': 73, u'title': u'some post here', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/some_post_here', u'ims': [], u'state': u'locked', u'editor': u'U407ABLLW', u'preview': None, u'external_type': u'', u'username': u'', u'updated': 1485975959, u'timestamp': 1485975967, u'public_url_shared': False, u'editable': True, u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/download/some_post_here', u'user': u'U407ABLLW', u'groups': [], u'is_public': False, u'last_editor': u'U407ABLLW', u'pretty_type': u'Post', u'name': u'some_post_here', u'mimetype': u'text/plain', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3YTCL8TA-9d9391a713', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3YTCL8TA/some_post_here', u'is_external': False, u'created': 1485975959, u'comments_count': 0, u'mode': u'space'}, u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94', u'bot_id': None} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975978.91-file_public.json b/_pytest/data/websocket/1485975978.91-file_public.json new file mode 100644 index 0000000..39ad780 --- /dev/null +++ b/_pytest/data/websocket/1485975978.91-file_public.json @@ -0,0 +1 @@ +{u'user_id': u'U407ABLLW', u'event_ts': u'1485975978.887563', u'file_id': u'F3YTCL8TA', u'file': {u'id': u'F3YTCL8TA'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_public'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975978.91-file_shared.json b/_pytest/data/websocket/1485975978.91-file_shared.json new file mode 100644 index 0000000..e41c2be --- /dev/null +++ b/_pytest/data/websocket/1485975978.91-file_shared.json @@ -0,0 +1 @@ +{u'user_id': u'U407ABLLW', u'event_ts': u'1485975978.887560', u'file_id': u'F3YTCL8TA', u'file': {u'id': u'F3YTCL8TA'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_shared'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975995.98-file_change.json b/_pytest/data/websocket/1485975995.98-file_change.json new file mode 100644 index 0000000..ff4ea71 --- /dev/null +++ b/_pytest/data/websocket/1485975995.98-file_change.json @@ -0,0 +1 @@ +{u'user_id': u'U407ABLLW', u'event_ts': u'1485975995.887846', u'file_id': u'F3YTCL8TA', u'file': {u'id': u'F3YTCL8TA'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_change'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485975998.13-file_change.json b/_pytest/data/websocket/1485975998.13-file_change.json new file mode 100644 index 0000000..bbbc4ec --- /dev/null +++ b/_pytest/data/websocket/1485975998.13-file_change.json @@ -0,0 +1 @@ +{u'user_id': u'U407ABLLW', u'event_ts': u'1485975998.887906', u'file_id': u'F3YTCL8TA', u'file': {u'id': u'F3YTCL8TA'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_change'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976002.57-file_deleted.json b/_pytest/data/websocket/1485976002.57-file_deleted.json new file mode 100644 index 0000000..a4f4c71 --- /dev/null +++ b/_pytest/data/websocket/1485976002.57-file_deleted.json @@ -0,0 +1 @@ +{u'event_ts': u'1485976002.888005', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_deleted', u'file_id': u'F3YTCL8TA'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976002.66-message.json b/_pytest/data/websocket/1485976002.66-message.json new file mode 100644 index 0000000..b32f31a --- /dev/null +++ b/_pytest/data/websocket/1485976002.66-message.json @@ -0,0 +1 @@ +{u'event_ts': u'1485976002.888006', u'ts': u'1485976002.000013', u'subtype': u'message_deleted', u'hidden': True, u'deleted_ts': u'1485975978.000012', u'type': u'message', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'channel': u'C407ABS94', u'previous_message': {u'username': u'<@U407ABLLW|alice>', u'display_as_bot': False, u'text': u'<@U407ABLLW|alice> shared a file: ', u'upload': False, u'ts': u'1485975978.000012', u'subtype': u'file_share', u'user': u'U407ABLLW', u'file': {u'filetype': u'space', u'channels': [], u'display_as_bot': False, u'id': u'F3YTCL8TA', u'size': 73, u'title': u'some post here', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/some_post_here', u'ims': [], u'state': u'locked', u'editor': u'U407ABLLW', u'preview': None, u'external_type': u'', u'username': u'', u'updated': 1485975959, u'timestamp': 1485975967, u'public_url_shared': False, u'editable': True, u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/download/some_post_here', u'user': u'U407ABLLW', u'groups': [], u'is_public': True, u'last_editor': u'U407ABLLW', u'pretty_type': u'Post', u'name': u'some_post_here', u'mimetype': u'text/plain', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3YTCL8TA-9d9391a713', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3YTCL8TA/some_post_here', u'is_external': False, u'created': 1485975959, u'comments_count': 0, u'mode': u'space'}, u'type': u'message', u'bot_id': None}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976024.46-user_typing.json b/_pytest/data/websocket/1485976024.46-user_typing.json new file mode 100644 index 0000000..b909fa3 --- /dev/null +++ b/_pytest/data/websocket/1485976024.46-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'G3ZGMF4RZ'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976028.31-user_typing.json b/_pytest/data/websocket/1485976028.31-user_typing.json new file mode 100644 index 0000000..b7f65a8 --- /dev/null +++ b/_pytest/data/websocket/1485976028.31-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'G3ZJKP7GA'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976039.32-reaction_added.json b/_pytest/data/websocket/1485976039.32-reaction_added.json new file mode 100644 index 0000000..acccc65 --- /dev/null +++ b/_pytest/data/websocket/1485976039.32-reaction_added.json @@ -0,0 +1 @@ +{u'reaction': u'unamused', u'event_ts': u'1485976039.888795', u'item': {u'type': u'message', u'ts': u'1485975841.000007', u'channel': u'C407ABS94'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_added'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976040.8-reaction_removed.json b/_pytest/data/websocket/1485976040.8-reaction_removed.json new file mode 100644 index 0000000..2f39d43 --- /dev/null +++ b/_pytest/data/websocket/1485976040.8-reaction_removed.json @@ -0,0 +1 @@ +{u'reaction': u'unamused', u'event_ts': u'1485976040.888829', u'item': {u'type': u'message', u'ts': u'1485975841.000007', u'channel': u'C407ABS94'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_removed'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976087.84-reconnect_url.json b/_pytest/data/websocket/1485976087.84-reconnect_url.json new file mode 100644 index 0000000..018c2c5 --- /dev/null +++ b/_pytest/data/websocket/1485976087.84-reconnect_url.json @@ -0,0 +1 @@ +{u'url': u'wss://mpmulti-clcz.slack-msgs.com/websocket/ZwLc8-LUIYLyvEwRO5WKs99SLtxBN9Cyu1W5qmqkre0AtWFl_H8xTE7HbF0E9LUvgI4bNkIRG8WgPxIzfr5gLJsU6vg8By36_mUUZYfH0TV65y_h2vV7NQe6s3A6WJdKRoJAe_EI2AN5L-VeL9rK1Ygc0nw3ngmuL78G6cm48xw=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976107.57-channel_created.json b/_pytest/data/websocket/1485976107.57-channel_created.json new file mode 100644 index 0000000..fed2059 --- /dev/null +++ b/_pytest/data/websocket/1485976107.57-channel_created.json @@ -0,0 +1 @@ +{u'event_ts': u'1485976107.890695', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_created', u'channel': {u'is_shared': False, u'is_channel': True, u'created': 1485976107, u'creator': u'U407ABLLW', u'is_org_shared': False, u'id': u'C3ZM2GMGU', u'name': u'made-to-be-archived'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976114.72-user_typing.json b/_pytest/data/websocket/1485976114.72-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976114.72-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976117.99-user_typing.json b/_pytest/data/websocket/1485976117.99-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976117.99-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976124.9-user_typing.json b/_pytest/data/websocket/1485976124.9-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976124.9-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976124.98-message.json b/_pytest/data/websocket/1485976124.98-message.json new file mode 100644 index 0000000..95a8d77 --- /dev/null +++ b/_pytest/data/websocket/1485976124.98-message.json @@ -0,0 +1 @@ +{u'text': u'referencing someong by <@U3ZKBBDL5> name', u'ts': u'1485976124.000014', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976125.06-desktop_notification.json b/_pytest/data/websocket/1485976125.06-desktop_notification.json new file mode 100644 index 0000000..3a57bc0 --- /dev/null +++ b/_pytest/data/websocket/1485976125.06-desktop_notification.json @@ -0,0 +1 @@ +{u'launchUri': u'slack://channel?id=C407ABS94&message=1485976124000014&team=T3YS5EAL9', u'subtitle': u'#general', u'is_shared': False, u'title': u'weeslacktest', u'ssbFilename': u'knock_brush.mp3', u'avatarImage': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png', u'imageUri': None, u'content': u'alice: referencing someong by @bob name', u'event_ts': u'1485976124.891186', u'msg': u'1485976124.000014', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'desktop_notification', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976125.95-user_typing.json b/_pytest/data/websocket/1485976125.95-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976125.95-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976129.49-user_typing.json b/_pytest/data/websocket/1485976129.49-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976129.49-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976133.0-user_typing.json b/_pytest/data/websocket/1485976133.0-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976133.0-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976137.51-user_typing.json b/_pytest/data/websocket/1485976137.51-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976137.51-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976137.95-message.json b/_pytest/data/websocket/1485976137.95-message.json new file mode 100644 index 0000000..715e4e4 --- /dev/null +++ b/_pytest/data/websocket/1485976137.95-message.json @@ -0,0 +1 @@ +{u'text': u'referencing someone else by <@U407ABLLW> name', u'ts': u'1485976137.000015', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976138.73-user_typing.json b/_pytest/data/websocket/1485976138.73-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976138.73-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976141.79-user_typing.json b/_pytest/data/websocket/1485976141.79-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976141.79-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976145.8-user_typing.json b/_pytest/data/websocket/1485976145.8-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976145.8-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976149.89-user_typing.json b/_pytest/data/websocket/1485976149.89-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976149.89-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976151.6-message.json b/_pytest/data/websocket/1485976151.6-message.json new file mode 100644 index 0000000..a2f431e --- /dev/null +++ b/_pytest/data/websocket/1485976151.6-message.json @@ -0,0 +1 @@ +{u'text': u'referencing a #channel-that-doesnt-exist', u'ts': u'1485976151.000016', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976157.18-message.json b/_pytest/data/websocket/1485976157.18-message.json new file mode 100644 index 0000000..96c82f6 --- /dev/null +++ b/_pytest/data/websocket/1485976157.18-message.json @@ -0,0 +1 @@ +{u'event_ts': u'1485976156.891735', u'ts': u'1485976156.000017', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'subtype': u'message_changed', u'message': {u'text': u'referencing a <#C407ABS94|general>', u'type': u'message', u'user': u'U407ABLLW', u'ts': u'1485976151.000016', u'edited': {u'user': u'U407ABLLW', u'ts': u'1485976157.000000'}}, u'type': u'message', u'hidden': True, u'channel': u'C407ABS94', u'previous_message': {u'text': u'referencing a #channel-that-doesnt-exist', u'type': u'message', u'user': u'U407ABLLW', u'ts': u'1485976151.000016'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976157.8-user_typing.json b/_pytest/data/websocket/1485976157.8-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976157.8-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976161.29-user_typing.json b/_pytest/data/websocket/1485976161.29-user_typing.json new file mode 100644 index 0000000..4e53cd4 --- /dev/null +++ b/_pytest/data/websocket/1485976161.29-user_typing.json @@ -0,0 +1 @@ +{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976161.75-message.json b/_pytest/data/websocket/1485976161.75-message.json new file mode 100644 index 0000000..9ed97d2 --- /dev/null +++ b/_pytest/data/websocket/1485976161.75-message.json @@ -0,0 +1 @@ +{u'text': u'referencing <#C3ZM8JTD3|some-channel2-renamed>', u'ts': u'1485976161.000018', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976182.59-channel_archive.json b/_pytest/data/websocket/1485976182.59-channel_archive.json new file mode 100644 index 0000000..66ce021 --- /dev/null +++ b/_pytest/data/websocket/1485976182.59-channel_archive.json @@ -0,0 +1 @@ +{u'event_ts': u'1485976182.892242', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_archive', u'user': u'U407ABLLW', u'channel': u'C3ZM2GMGU'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976186.62-channel_unarchive.json b/_pytest/data/websocket/1485976186.62-channel_unarchive.json new file mode 100644 index 0000000..67f15d7 --- /dev/null +++ b/_pytest/data/websocket/1485976186.62-channel_unarchive.json @@ -0,0 +1 @@ +{u'event_ts': u'1485976186.892309', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_unarchive', u'user': u'U407ABLLW', u'channel': u'C3ZM2GMGU'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976236.58-message.json b/_pytest/data/websocket/1485976236.58-message.json new file mode 100644 index 0000000..ea7ecef --- /dev/null +++ b/_pytest/data/websocket/1485976236.58-message.json @@ -0,0 +1 @@ +{u'comment': {u'comment': u'first comment!', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'text': u'<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment!', u'ts': u'1485976236.000019', u'subtype': u'file_comment', u'is_intro': False, u'file': {u'groups': [], u'filetype': u'png', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'thumb_64': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png', u'size': 68, u'original_h': 1, u'thumb_360_w': 1, u'title': u'1x1.png', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png', u'thumb_360': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png', u'id': u'F3ZJQTA66', u'ims': [], u'thumb_80': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png', u'thumb_360_h': 1, u'external_type': u'', u'username': u'', u'timestamp': 1485975955, u'public_url_shared': False, u'editable': False, u'thumb_160': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png', u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png', u'user': u'U407ABLLW', u'image_exif_rotation': 1, u'is_public': True, u'pretty_type': u'PNG', u'name': u'1x1.png', u'mimetype': u'image/png', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png', u'is_external': False, u'created': 1485975955, u'original_w': 1, u'comments_count': 1, u'mode': u'hosted'}, u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976236.67-file_comment_added.json b/_pytest/data/websocket/1485976236.67-file_comment_added.json new file mode 100644 index 0000000..13272a8 --- /dev/null +++ b/_pytest/data/websocket/1485976236.67-file_comment_added.json @@ -0,0 +1 @@ +{u'comment': {u'comment': u'first comment!', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'user_id': u'U407ABLLW', u'event_ts': u'1485976236.893560', u'file_id': u'F3ZJQTA66', u'file': {u'id': u'F3ZJQTA66'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_comment_added'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976248.57-message.json b/_pytest/data/websocket/1485976248.57-message.json new file mode 100644 index 0000000..797e0dc --- /dev/null +++ b/_pytest/data/websocket/1485976248.57-message.json @@ -0,0 +1 @@ +{u'event_ts': u'1485976248.893799', u'ts': u'1485976248.000020', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'subtype': u'message_changed', u'message': {u'comment': {u'comment': u'first comment! now i edited it.', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'text': u'<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.', u'ts': u'1485976236.000019', u'subtype': u'file_comment', u'is_intro': False, u'file': {u'groups': [], u'filetype': u'png', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'thumb_64': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png', u'size': 68, u'original_h': 1, u'thumb_360_w': 1, u'title': u'1x1.png', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png', u'thumb_360': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png', u'id': u'F3ZJQTA66', u'ims': [], u'thumb_80': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png', u'thumb_360_h': 1, u'external_type': u'', u'username': u'', u'timestamp': 1485975955, u'public_url_shared': False, u'editable': False, u'thumb_160': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png', u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png', u'user': u'U407ABLLW', u'image_exif_rotation': 1, u'is_public': True, u'pretty_type': u'PNG', u'name': u'1x1.png', u'mimetype': u'image/png', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png', u'is_external': False, u'created': 1485975955, u'original_w': 1, u'comments_count': 1, u'mode': u'hosted'}, u'type': u'message'}, u'type': u'message', u'hidden': True, u'channel': u'C407ABS94', u'previous_message': {u'comment': {u'comment': u'first comment! now i edited it.', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'text': u'<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.', u'ts': u'1485976236.000019', u'subtype': u'file_comment', u'is_intro': False, u'file': {u'groups': [], u'filetype': u'png', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'thumb_64': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png', u'size': 68, u'original_h': 1, u'thumb_360_w': 1, u'title': u'1x1.png', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png', u'thumb_360': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png', u'id': u'F3ZJQTA66', u'ims': [], u'thumb_80': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png', u'thumb_360_h': 1, u'external_type': u'', u'username': u'', u'timestamp': 1485975955, u'public_url_shared': False, u'editable': False, u'thumb_160': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png', u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png', u'user': u'U407ABLLW', u'image_exif_rotation': 1, u'is_public': True, u'pretty_type': u'PNG', u'name': u'1x1.png', u'mimetype': u'image/png', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png', u'is_external': False, u'created': 1485975955, u'original_w': 1, u'comments_count': 1, u'mode': u'hosted'}, u'type': u'message'}} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976248.65-file_comment_edited.json b/_pytest/data/websocket/1485976248.65-file_comment_edited.json new file mode 100644 index 0000000..f944ede --- /dev/null +++ b/_pytest/data/websocket/1485976248.65-file_comment_edited.json @@ -0,0 +1 @@ +{u'comment': {u'comment': u'first comment! now i edited it.', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'user_id': u'U407ABLLW', u'event_ts': u'1485976248.893801', u'file_id': u'F3ZJQTA66', u'file': {u'id': u'F3ZJQTA66'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_comment_edited'} \ No newline at end of file diff --git a/_pytest/data/websocket/1485976267.81-reconnect_url.json b/_pytest/data/websocket/1485976267.81-reconnect_url.json new file mode 100644 index 0000000..c373cbc --- /dev/null +++ b/_pytest/data/websocket/1485976267.81-reconnect_url.json @@ -0,0 +1 @@ +{u'url': u'wss://mpmulti-m00z.slack-msgs.com/websocket/fZI1FV2ZuMYXeQLMKi1SrH2j69xL6m_6hC38CVCr0ugPFpMS1IBxlCvHJYbSrH-fMhSuLmbkEd1te_ND5Q-EyVVX-w06mn5NLZM9GaX_mWb9A3w79sThYzEgnnQ8onoeFM2CWgFNxWM_3XS4HJaWXeee-_sNh_booNbby8jm9mg=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file diff --git a/wee_slack.py b/wee_slack.py index aed88e9..51c12b6 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -32,6 +32,8 @@ SCROLLBACK_SIZE = 500 CACHE_VERSION = "4" +RECORD_DIR = "/tmp/weeslack-debug" + SLACK_API_TRANSLATOR = { "channel": { "history": "channels.history", @@ -76,6 +78,7 @@ if hasattr(ssl, "get_default_verify_paths") and callable(ssl.get_default_verify_ IGNORED_EVENTS = [ "reconnect_url", "hello", + "pref_change", ] ###### New central Event router @@ -92,6 +95,19 @@ class EventRouter(object): self.proc = {k[8:]: v for k, v in globals().items() if k.startswith("process_")} self.handlers = {k[7:]: v for k, v in globals().items() if k.startswith("handle_")} self.local_proc = {k[14:]: v for k, v in globals().items() if k.startswith("local_process_")} + self.shutting_down = False + self.recording = False + self.recording_path = "/tmp" + + def record(self): + self.recording = not self.recording + if self.recording: + import os + if not os.path.exists(RECORD_DIR): + os.makedirs(RECORD_DIR) + + def shutdown(self): + self.shutting_down = not self.shutting_down def register_team(self, team): """ @@ -111,13 +127,15 @@ class EventRouter(object): else: raise InvalidType(type(buffer_ptr)) - def unregister_weechat_buffer(self, buffer_ptr): + def unregister_weechat_buffer(self, buffer_ptr, update_remote=False, close_buffer=False): """ Adds a weechat buffer to the list of handled buffers for this EventRouter """ if isinstance(buffer_ptr, str): try: - self.weechat_buffers[buffer_ptr].destroy_buffer() + self.weechat_buffers[buffer_ptr].destroy_buffer(update_remote) + if close_buffer: + w.buffer_close(buffer_ptr) del self.weechat_buffers[buffer_ptr] except: dbg("Tried to close unknown buffer") @@ -144,6 +162,12 @@ class EventRouter(object): #print self.teams[team_hash].domain message_json["wee_slack_metadata"] = metadata #print message_json + if self.recording: + now = time.time() + mtype = message_json.get('type', 'unknown') + f = open('{}/{}-{}.json'.format(RECORD_DIR, now, mtype), 'w') + f.write("{}".format(message_json)) + f.close() self.receive_json(json.dumps(message_json)) except WebSocketConnectionClosedException: #TODO: handle reconnect here @@ -182,10 +206,8 @@ class EventRouter(object): self.reply_buffer[request_metadata.response_id] += out def receive_json(self, data): - dbg("RECEIVED JSON") - dbg(str(data)) + dbg("RECEIVED JSON of len {}".format(len(data))) message_json = json.loads(data) - dbg(message_json) #print message_json.keys() self.queue.append(message_json) def receive(self, dataobj): @@ -216,16 +238,20 @@ class EventRouter(object): function_name = "unknown" # Here we are passing the actual objects. No more lookups. - if "wee_slack_metadata" in j: - - if isinstance(j["wee_slack_metadata"], str): - dbg("string of metadata") - if "team" in j["wee_slack_metadata"]: - kwargs["team"] = self.teams[j["wee_slack_metadata"]["team"]] - if "user" in j: - kwargs["user"] = self.teams[j["wee_slack_metadata"]["team"]].users[j["user"]] - if "channel" in j: - kwargs["channel"] = self.teams[j["wee_slack_metadata"]["team"]].channels[j["channel"]] + meta = j.get("wee_slack_metadata", None) + if meta: + try: + if isinstance(meta, str): + dbg("string of metadata") + team = meta.get("team", None) + if team: + kwargs["team"] = self.teams[team] + if "user" in j: + kwargs["user"] = self.teams[team].users[j["user"]] + if "channel" in j: + kwargs["channel"] = self.teams[team].channels[j["channel"]] + except: + dbg("metadata failure") if function_name not in IGNORED_EVENTS: dbg("running {}".format(function_name)) @@ -248,11 +274,13 @@ def local_process_async_slack_api_request(request, event_router): """ Sends an API request to Slack. You'll need to give this a well formed SlackRequest object. """ - weechat_request = 'url:{}'.format(request.request_string()) - params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} - request.tried() - context = pickle.dumps(request) - w.hook_process_hashtable(weechat_request, params, config.slack_timeout, "receive_httprequest_callback", context) + if not event_router.shutting_down: + weechat_request = 'url:{}'.format(request.request_string()) + print weechat_request + params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} + request.tried() + context = pickle.dumps(request) + w.hook_process_hashtable(weechat_request, params, config.slack_timeout, "receive_httprequest_callback", context) ###### New Callbacks @@ -274,26 +302,21 @@ def receive_ws_callback(*args): return w.WEECHAT_RC_OK def buffer_closing_callback(signal, sig_type, data): - eval(signal).unregister_weechat_buffer(data) + eval(signal).unregister_weechat_buffer(data, True, False) return w.WEECHAT_RC_OK def buffer_input_callback(signal, buffer_ptr, data): eventrouter = eval(signal) - - dbg(signal, True) - dbg(data, True) - dbg(buffer_ptr, True) channel = eventrouter.weechat_buffers[buffer_ptr] - print channel if not channel: return w.WEECHAT_RC_OK_EAT -# reaction = re.match("^\s*(\d*)(\+|-):(.*):\s*$", data) -# if reaction: -# if reaction.group(2) == "+": -# channel.send_add_reaction(int(reaction.group(1) or 1), reaction.group(3)) -# elif reaction.group(2) == "-": -# channel.send_remove_reaction(int(reaction.group(1) or 1), reaction.group(3)) + reaction = re.match("^\s*(\d*)(\+|-):(.*):\s*$", data) + if reaction: + if reaction.group(2) == "+": + channel.send_add_reaction(int(reaction.group(1) or 1), reaction.group(3)) + elif reaction.group(2) == "-": + channel.send_remove_reaction(int(reaction.group(1) or 1), reaction.group(3)) # elif data.startswith('s/'): # try: # old, new, flags = re.split(r'(? 1: + # gray_check = True + for b in eventrouter.weechat_buffers: + #print b + eventrouter.weechat_buffers[b].rename() + buffer_list_update = False + return w.WEECHAT_RC_OK + +def quit_notification_callback(signal, sig_type, data): + stop_talking_to_slack() + +def script_unloaded(): + stop_talking_to_slack() + return w.WEECHAT_RC_OK + +def stop_talking_to_slack(): + """ + Prevents a race condition where quitting closes buffers + which triggers leaving the channel because of how close + buffer is handled + """ + EVENTROUTER.shutdown() + return w.WEECHAT_RC_OK + + ##### New Classes class SlackRequest(object): @@ -360,11 +416,12 @@ class SlackRequest(object): return self.tries < 3 class SlackTeam(object): - def __init__(self, token, team, nick, myidentifier, users, channels): + def __init__(self, eventrouter, token, team, nick, myidentifier, users, bots, channels): self.connected = False self.ws = None self.ws_counter = 0 self.ws_replies = {} + self.eventrouter = eventrouter self.token = token self.team = team self.domain = team + ".slack.com" @@ -372,11 +429,18 @@ class SlackTeam(object): self.myidentifier = myidentifier self.channels = channels self.users = users + self.bots = bots self.team_hash = str(sha.sha("{}{}".format(self.nick, self.team)).hexdigest()) + self.name = self.domain + self.server_buffer = None + self.got_history = True + self.create_buffer() for c in self.channels.keys(): channels[c].set_related_server(self) channels[c].open_if_we_should() # self.channel_set_related_server(c) + # Last step is to make sure my nickname is the set color + self.users[self.myidentifier].force_color(w.config_string(w.config_get('weechat.color.chat_nick_self'))) def __eq__(self, compare_str): if compare_str == self.token: return True @@ -387,8 +451,21 @@ class SlackTeam(object): channel.set_related_server(self) # def connect_request_generate(self): # return SlackRequest(self.token, 'rtm.start', {}) + def create_buffer(self): + if not self.server_buffer: + self.server_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") + self.eventrouter.register_weechat_buffer(self.server_buffer, self) + w.buffer_set(self.server_buffer, "localvar_set_type", 'channel') + w.buffer_set(self.server_buffer, "localvar_set_channel", self.name) + w.buffer_set(self.server_buffer, "short_name", self.name) + def get_channel_map(self): + return {v.slack_name: k for k, v in self.channels.iteritems()} + def get_username_map(self): + return {v.name: k for k, v in self.users.iteritems()} def get_team_hash(self): return self.team_hash + def rename(self): + pass def attach_websocket(self, ws): self.ws = ws def set_connected(self): @@ -421,8 +498,8 @@ class SlackChannel(object): # We require these two things for a vaid object, # the rest we can just learn from slack self.eventrouter = eventrouter + self.slack_name = kwargs["name"] self.identifier = kwargs["id"] - self.name = kwargs["name"] self.channel_buffer = None self.team = None self.got_history = False @@ -430,28 +507,41 @@ class SlackChannel(object): self.type = 'channel' for key, value in kwargs.items(): setattr(self, key, value) + self.set_name(self.slack_name) def __repr__(self): return "Name:{} Identifier:{}".format(self.name, self.identifier) - def open_if_we_should(self): - for reason in ["is_member", "is_open", "unread_count"]: - try: - if eval("self." + reason): - self.create_buffer() - except: - pass + def set_name(self, slack_name): + self.name = "#" + slack_name + def rename(self): pass + #self.name = "?" + self.slack_name + #w.buffer_set(self.channel_buffer, "short_name", self.name) + def formatted_name(self): + return self.name + def update_from_message_json(self, message_json): + for key, value in message_json.items(): + setattr(self, key, value) + def open_if_we_should(self, force=False): + if force: + self.create_buffer() + else: + for reason in ["is_member", "is_open", "unread_count"]: + try: + if eval("self." + reason): + self.create_buffer() + except: + pass def set_related_server(self, team): self.team = team def create_buffer(self): if not self.channel_buffer: - print self.team self.channel_buffer = w.buffer_new("{}.{}".format(self.team.domain, self.name), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.register_weechat_buffer(self.channel_buffer, self) #dbg(self.channel_buffer, True) w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') w.buffer_set(self.channel_buffer, "localvar_set_channel", self.name) - w.buffer_set(self.channel_buffer, "short_name", self.name) + w.buffer_set(self.channel_buffer, "short_name", self.formatted_name()) # if self.server.alias: # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) # else: @@ -459,16 +549,27 @@ class SlackChannel(object): # buffer_list_update_next() # if self.unread_count != 0 and not self.muted: # w.buffer_set(self.channel_buffer, "hotlist", "1") - def destroy_buffer(self): - if self.channel_buffer: + def destroy_buffer(self, update_remote): + if self.channel_buffer is not None: self.channel_buffer = None + self.got_history = False + #if update_remote and not eventrouter.shutting_down: + if update_remote and not self.eventrouter.shutting_down: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + EVENTROUTER.receive(s) def buffer_prnt(self, nick, text, timestamp, *args): + t, time_id = timestamp.split('.', 1) + time_int = int(t) if self.channel_buffer: - w.prnt(self.channel_buffer, "{}\t{}".format(nick, text)) + #w.prnt(self.channel_buffer, "{}\t{}".format(nick, text)) + tags = '' + data = "{}\t{}".format(nick, text) + w.prnt_date_tags(self.channel_buffer, time_int, tags, data) + modify_print_time(self.channel_buffer, time_id, time_int) #dbg("should buffer print {} {}".format(nick, text), True) def send_message(self, message): #team = self.eventrouter.teams[self.team] - #message = self.linkify_text(message) + message = linkify_text(message, self.team, self) dbg(message) print self.team request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} @@ -483,16 +584,17 @@ class SlackChannel(object): mk.sort() for k in mk[:SCROLLBACK_SIZE]: del self.messages[k] - def change_message(self, ts, text=None, suffix=''): - #print "should have changed" + def change_message(self, ts, text=None, suffix=None): if ts in self.messages: m = self.messages[ts] - m.change_text(text) - #m = self.get_message(ts) - #text = m[2].render(force=True) - #timestamp, time_id = ts.split(".", 2) - #timestamp = int(timestamp) - #modify_buffer_line(self.channel_buffer, text + suffix, timestamp, time_id) + if text: + m.change_text(text) + if suffix: + m.change_suffix(suffix) + text = m.render(force=True) + timestamp, time_id = ts.split(".", 2) + timestamp = int(timestamp) + modify_buffer_line(self.channel_buffer, text, timestamp, time_id) return True def get_history(self): #if config.cache_messages: @@ -502,6 +604,16 @@ class SlackChannel(object): EVENTROUTER.receive(s) #async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}) self.got_history = True + def send_add_reaction(self, msg_number, reaction): + self.send_change_reaction("reactions.add", msg_number, reaction) + def send_remove_reaction(self, msg_number, reaction): + self.send_change_reaction("reactions.remove", msg_number, reaction) + def send_change_reaction(self, method, msg_number, reaction): + if 0 < msg_number < len(self.messages): + timestamp = self.sorted_message_keys()[-msg_number] + data = {"channel": self.identifier, "timestamp": timestamp, "name": reaction} + s = SlackRequest(self.team.token, method, data) + self.eventrouter.receive(s) def sorted_message_keys(self): return sorted(self.messages) @@ -512,15 +624,38 @@ class SlackDMChannel(SlackChannel): kwargs["name"] = users[dmuser].name super(SlackDMChannel, self).__init__(eventrouter, **kwargs) self.type = 'im' + self.update_color() + self.set_name(self.slack_name) + #self.name = self.formatted_name(" ") + def set_name(self, slack_name): + self.name = slack_name def create_buffer(self): if not self.channel_buffer: super(SlackDMChannel, self).create_buffer() w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') + def update_color(self): + if config.colorize_nicks: + self.color_name = w.info_get('irc_nick_color_name', self.name.encode('utf-8')) + self.color = w.color(self.color_name) + else: + self.color = "" + self.color_name = "" + def formatted_name(self, prepend="", enable_color=True): + if config.colorize_nicks and enable_color: + print_color = self.color + else: + print_color = "" + return print_color + prepend + self.name + class SlackGroupChannel(SlackChannel): def __init__(self, eventrouter, **kwargs): super(SlackGroupChannel, self).__init__(eventrouter, **kwargs) + self.name = "#" + kwargs['name'] self.type = "group" + self.set_name(self.slack_name) + def set_name(self, slack_name): + self.name = "#" + slack_name class SlackMPDMChannel(SlackChannel): """ @@ -528,10 +663,11 @@ class SlackMPDMChannel(SlackChannel): We change the name to look less terrible in weechat. """ def __init__(self, eventrouter, **kwargs): - n = kwargs.get('name') - name = "|".join("-".join(n.split("-")[1:-1]).split("--")) - kwargs["name"] = name super(SlackMPDMChannel, self).__init__(eventrouter, **kwargs) + n = kwargs.get('name') + self.set_name(n) + def set_name(self, n): + self.name = "|".join("-".join(n.split("-")[1:-1]).split("--")) self.type = "group" class SlackUser(object): @@ -545,8 +681,19 @@ class SlackUser(object): self.name = kwargs["name"] for key, value in kwargs.items(): setattr(self, key, value) + self.update_color() def __repr__(self): return "Name:{} Identifier:{}".format(self.name, self.identifier) + def force_color(self, color_name): + self.color_name = color_name + self.color = w.color(self.color_name) + def update_color(self): + if config.colorize_nicks: + self.color_name = w.info_get('irc_nick_color_name', self.name.encode('utf-8')) + self.color = w.color(self.color_name) + else: + self.color = "" + self.color_name = "" def formatted_name(self, prepend="", enable_color=True): if config.colorize_nicks and enable_color: print_color = self.color @@ -554,28 +701,39 @@ class SlackUser(object): print_color = "" return print_color + prepend + self.name +class SlackBot(SlackUser): + def __init__(self, **kwargs): + super(SlackBot, self).__init__(**kwargs) + class SlackMessage(object): def __init__(self, message_json, team, channel): self.team = team self.channel = channel self.message_json = message_json self.sender = self.get_sender() + self.suffix = '' self.ts = message_json['ts'] - def render(self): - return render(self.message_json, self.team, self.channel) + def render(self, force=False): + return render(self.message_json, self.team, self.channel, force) + self.suffix def change_text(self, new_text): - dbg("should change text to {}".format(new_text), True) - return "meh" + self.message_json["text"] = new_text + dbg(self.message_json, True) + def change_suffix(self, new_suffix): + self.suffix = new_suffix + dbg(self.message_json, True) def get_sender(self, utf8=True): + name = u"" if 'bot_id' in self.message_json and self.message_json['bot_id'] is not None: - name = u"{} :]".format(self.server.bots.find(self.message_json["bot_id"]).formatted_name()) + name = u"{} :]".format(self.team.bots[self.message_json["bot_id"]].formatted_name()) elif 'user' in self.message_json: - if self.message_json['user'] in self.team.users: + if self.message_json['user'] == self.team.myidentifier: + name = self.team.users[self.team.myidentifier].name + elif self.message_json['user'] in self.team.users: u = self.team.users[self.message_json['user']] - if u.is_bot: - name = u"{} :]".format(u.formatted_name()) - else: - name = u.name + if u.is_bot: + name = u"{} :]".format(u.formatted_name()) + else: + name = u"{}".format(u.formatted_name()) elif 'username' in self.message_json: name = u"-{}-".format(self.message_json["username"]) elif 'service_name' in self.message_json: @@ -586,6 +744,27 @@ class SlackMessage(object): return name.encode('utf-8') else: return name + def add_reaction(self, reaction, user): + m = self.message_json.get('reactions', None) + if m: + found = False + for r in m: + if r["name"] == reaction and user not in r["users"]: + r["users"].append(user) + found = True + if not found: + self.message_json["reactions"].append({u"name": reaction, u"users": [user]}) + else: + self.message_json["reactions"] = [{u"name": reaction, u"users": [user]}] + def remove_reaction(self, reaction, user): + m = self.message_json.get('reactions', None) + if m: + for r in m: + if r["name"] == reaction and user in r["users"]: + r["users"].remove(user) + else: + pass + class WeeSlackMetadata(object): def __init__(self, meta): @@ -607,6 +786,10 @@ def handle_rtmstart(login_data, eventrouter): users[item["id"]] = SlackUser(**item) #users.append(SlackUser(**item)) + bots = {} + for item in login_data["bots"]: + bots[item["id"]] = SlackBot(**item) + channels = {} for item in login_data["channels"]: channels[item["id"]] = SlackChannel(eventrouter, **item) @@ -621,11 +804,13 @@ def handle_rtmstart(login_data, eventrouter): channels[item["id"]] = SlackGroupChannel(eventrouter, **item) t = SlackTeam( + eventrouter, metadata.token, login_data["team"]["domain"], login_data["self"]["name"], login_data["self"]["id"], users, + bots, channels, ) eventrouter.register_team(t) @@ -659,7 +844,7 @@ 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] kwargs['channel'] = kwargs['team'].channels[request_metadata.channel_identifier] - for message in message_json["messages"]: + for message in reversed(message_json["messages"]): process_message(message, eventrouter, **kwargs) ###### New/converted process_ and subprocess_ methods @@ -683,8 +868,8 @@ def process_message(message_json, eventrouter, store=True, **kwargs): known_subtypes = [ #'thread_message', #'message_replied', - #'message_changed', - #'message_deleted', + 'message_changed', + 'message_deleted', #'channel_join', #'channel_leave', #'channel_topic', @@ -693,10 +878,10 @@ def process_message(message_json, eventrouter, store=True, **kwargs): ] if "thread_ts" in message_json and "reply_count" not in message_json: message_json["subtype"] = "thread_message" - if "subtype" in message_json: - if message_json["subtype"] in known_subtypes: - f = eval('subprocess_' + message_json["subtype"]) - f(message_json, eventrouter, channel, team) + subtype = message_json.get("subtype", None) + if subtype and subtype in known_subtypes: + f = eval('subprocess_' + subtype) + f(message_json, eventrouter, channel, team) else: message = SlackMessage(message_json, team, channel) @@ -743,32 +928,37 @@ def subprocess_thread_message(message_json, eventrouter, channel, team): def subprocess_message_changed(message_json, eventrouter, channel, team): - m = message_json["message"] - if "message" in message_json: - if "attachments" in m: - message_json["attachments"] = m["attachments"] - if "text" in m: - if "text" in message_json: - message_json["text"] += m["text"] - dbg("added text!") - else: - message_json["text"] = m["text"] - if "fallback" in m: - if "fallback" in message_json: - message_json["fallback"] += m["fallback"] - else: - message_json["fallback"] = m["fallback"] - - text_before = (len(m['text']) > 0) - m["text"] += unwrap_attachments(message_json, text_before) - if "edited" in m: - channel.change_message(m["ts"], m["text"], ' (edited)') + m = message_json.get("message", None) + if m: + new_message = m + #message = SlackMessage(new_message, team, channel) + #if "attachments" in m: + # message_json["attachments"] = m["attachments"] + #if "text" in m: + # if "text" in message_json: + # message_json["text"] += m["text"] + # dbg("added text!") + # else: + # message_json["text"] = m["text"] + #if "fallback" in m: + # if "fallback" in message_json: + # message_json["fallback"] += m["fallback"] + # else: + # message_json["fallback"] = m["fallback"] + + text_before = (len(new_message['text']) > 0) + new_message["text"] += unwrap_attachments(message_json, text_before) + if "edited" in new_message: + channel.change_message(new_message["ts"], new_message["text"], ' (edited)') else: - channel.change_message(m["ts"], m["text"]) + channel.change_message(new_message["ts"], new_message["text"]) + +def subprocess_message_deleted(message_json, eventrouter, channel, team): + channel.change_message(message_json["deleted_ts"], "(deleted)", '') def process_reply(message_json, eventrouter, **kwargs): dbg('processing reply') - dbg(message_json, True) + #dbg(message_json, True) team = kwargs["team"] identifier = message_json["reply_to"] try: @@ -779,8 +969,8 @@ def process_reply(message_json, eventrouter, **kwargs): else: dbg("no reply ts {}".format(message_json)) - if "channel" in original_message_json: - channel = team.channels[original_message_json["channel"]] + c = original_message_json.get('channel', None) + channel = team.channels[c] m = SlackMessage(original_message_json, team, channel) # m = Message(message_json, server=server) dbg(m, True) @@ -803,14 +993,44 @@ def process_channel_marked(message_json, eventrouter, **kwargs): #w.buffer_set(channel.channel_buffer, "hotlist", "-1") def process_channel_joined(message_json, eventrouter, **kwargs): - print message_json - print kwargs -# server = servers.find(message_json["_server"]) -# channel = server.channels.find(message_json["channel"]) -# text = unfurl_refs(message_json["text"], ignore_alt_text=False) -# channel.buffer_prnt(w.prefix("join").rstrip(), text, message_json["ts"]) -# channel.user_join(message_json["user"]) + item = message_json["channel"] + kwargs['team'].channels[item["id"]].update_from_message_json(item) + kwargs['team'].channels[item["id"]].open_if_we_should() + +def process_channel_created(message_json, eventrouter, **kwargs): + item = message_json["channel"] + c = SlackChannel(eventrouter, team=kwargs["team"], **item) + kwargs['team'].channels[item["id"]] = c + +def process_im_open(message_json, eventrouter, **kwargs): + item = message_json + kwargs['team'].channels[item["channel"]].open_if_we_should(True) + +def process_im_close(message_json, eventrouter, **kwargs): + item = message_json + cbuf = kwargs['team'].channels[item["channel"]].channel_buffer + print cbuf + eventrouter.unregister_weechat_buffer(cbuf, False, True) + +def process_reaction_added(message_json, eventrouter, **kwargs): + channel = kwargs['team'].channels[message_json["item"]["channel"]] + if message_json["item"].get("type") == "message": + ts = message_json['item']["ts"] + + channel.messages[ts].add_reaction(message_json["reaction"], message_json["user"]) + channel.change_message(ts) + else: + dbg("reaction to item type not supported: " + str(message_json)) +def process_reaction_removed(message_json, eventrouter, **kwargs): + channel = kwargs['team'].channels[message_json["item"]["channel"]] + if message_json["item"].get("type") == "message": + ts = message_json['item']["ts"] + + channel.messages[ts].remove_reaction(message_json["reaction"], message_json["user"]) + channel.change_message(ts) + else: + dbg("Reaction to item type not supported: " + str(message_json)) ###### New module/global methods @@ -847,27 +1067,39 @@ def render(message_json, team, channel, force=False): # text += " [Replies: {} Thread ID: {} ] ".format(len(self.threads), self.thread_id) # #for thread in self.threads: - if "reactions" in message_json: - text += create_reaction_string(message_json["reactions"]) + text += create_reaction_string(message_json.get("reactions", "")) message_json["_rendered_text"] = text return text -def linkify_text(message): +def linkify_text(message, team, channel): + # The get_username_map function is a bit heavy, but this whole + # function is only called on message send.. + usernames = team.get_username_map() + channels = team.get_channel_map() message = message.split(' ') for item in enumerate(message): - targets = re.match('.*([@#])([\w.]+\w)(\W*)', item[1]) + targets = re.match('.*([@#])([\w.-]+[\w. -])(\W*)', item[1]) + print targets if targets and targets.groups()[0] == '@': + print targets.groups() named = targets.groups() if named[1] in ["group", "channel", "here"]: message[item[0]] = "".format(named[1]) - if self.server.users.find(named[1]): - message[item[0]] = "<@{}>{}".format(self.server.users.find(named[1]).identifier, named[2]) + try: + if usernames[named[1]]: + message[item[0]] = "<@{}>{}".format(usernames[named[1]], named[2]) + except: + message[item[0]] = "@{}{}".format(named[1], named[2]) if targets and targets.groups()[0] == '#': named = targets.groups() - if self.server.channels.find(named[1]): - message[item[0]] = "<#{}|{}>{}".format(self.server.channels.find(named[1]).identifier, named[1], named[2]) - dbg(message) + try: + if channels[named[1]]: + message[item[0]] = "<#{}|{}>{}".format(channels[named[1]], named[1], named[2]) + except: + message[item[0]] = "#{}{}".format(named[1], named[2]) + + #dbg(message) return " ".join(message) def unfurl_refs(text, ignore_alt_text=False): @@ -904,10 +1136,11 @@ def unfurl_ref(ref, ignore_alt_text=False): def unwrap_attachments(message_json, text_before): attachment_text = '' - if "attachments" in message_json: + a = message_json.get("attachments", None) + if a: if text_before: attachment_text = u'\n' - for attachment in message_json["attachments"]: + for attachment in a: # Attachments should be rendered roughly like: # # $pretext @@ -920,26 +1153,31 @@ def unwrap_attachments(message_json, text_before): prepend_title_text = attachment['author_name'] + ": " if 'pretext' in attachment: t.append(attachment['pretext']) - if "title" in attachment: - if 'title_link' in attachment: - t.append('%s%s (%s)' % (prepend_title_text, attachment["title"], attachment["title_link"],)) - else: - t.append(prepend_title_text + attachment["title"]) + title = attachment.get('title', None) + title_link = attachment.get('title_link', None) + if title and title_link: + t.append('%s%s (%s)' % (prepend_title_text, title, title_link,)) + prepend_title_text = '' + elif title and not title_link: + t.append(prepend_title_text + title) prepend_title_text = '' - elif "from_url" in attachment: - t.append(attachment["from_url"]) - if "text" in attachment: - tx = re.sub(r' *\n[\n ]+', '\n', attachment["text"]) + t.append(attachment.get("from_url", "")) + + atext = attachment.get("text", None) + if atext: + tx = re.sub(r' *\n[\n ]+', '\n', atext) t.append(prepend_title_text + tx) prepend_title_text = '' - if 'fields' in attachment: - for f in attachment['fields']: + fields = attachment.get("fields", None) + if fields: + for f in fields: if f['title'] != '': t.append('%s %s' % (f['title'], f['value'],)) else: t.append(f['value']) - if t == [] and "fallback" in attachment: - t.append(attachment["fallback"]) + fallback = attachment.get("fallback", None) + if t == [] and fallback: + t.append(fallback) attachment_text += "\n".join([x.strip() for x in t if x]) return attachment_text @@ -988,6 +1226,73 @@ def create_reaction_string(reactions): reaction_string = '' return reaction_string +def modify_buffer_line(buffer, new_line, timestamp, time_id): + # get a pointer to this buffer's lines + own_lines = w.hdata_pointer(w.hdata_get('buffer'), buffer, 'own_lines') + if own_lines: + # get a pointer to the last line + line_pointer = w.hdata_pointer(w.hdata_get('lines'), own_lines, 'last_line') + # hold the structure of a line and of line data + struct_hdata_line = w.hdata_get('line') + struct_hdata_line_data = w.hdata_get('line_data') + + while line_pointer: + # get a pointer to the data in line_pointer via layout of struct_hdata_line + data = w.hdata_pointer(struct_hdata_line, line_pointer, 'data') + if data: + line_timestamp = w.hdata_time(struct_hdata_line_data, data, 'date') + line_time_id = w.hdata_integer(struct_hdata_line_data, data, 'date_printed') + # prefix = w.hdata_string(struct_hdata_line_data, data, 'prefix') + + if timestamp == int(line_timestamp) and int(time_id) == line_time_id: + # w.prnt("", "found matching time date is {}, time is {} ".format(timestamp, line_timestamp)) + w.hdata_update(struct_hdata_line_data, data, {"message": new_line}) + break + else: + pass + # move backwards one line and try again - exit the while if you hit the end + line_pointer = w.hdata_move(struct_hdata_line, line_pointer, -1) + return w.WEECHAT_RC_OK + + +def modify_print_time(buffer, new_id, time): + """ + This overloads the time printed field to let us store the slack + per message unique id that comes after the "." in a slack ts + """ + # get a pointer to this buffer's lines + own_lines = w.hdata_pointer(w.hdata_get('buffer'), buffer, 'own_lines') + if own_lines: + # get a pointer to the last line + line_pointer = w.hdata_pointer(w.hdata_get('lines'), own_lines, 'last_line') + # hold the structure of a line and of line data + struct_hdata_line = w.hdata_get('line') + struct_hdata_line_data = w.hdata_get('line_data') + + # get a pointer to the data in line_pointer via layout of struct_hdata_line + data = w.hdata_pointer(struct_hdata_line, line_pointer, 'data') + if data: + w.hdata_update(struct_hdata_line_data, data, {"date_printed": new_id}) + + return w.WEECHAT_RC_OK + +###### New/converted command_ commands + +def slack_command_cb(data, current_buffer, args): + a = args.split(' ', 1) + if len(a) > 1: + function_name, args = a[0], " ".join(a[1:]) + else: + function_name, args = a[0], None + + try: + cmds[function_name](current_buffer, args) + except KeyError: + w.prnt("", "Command not found: " + function_name) + return w.WEECHAT_RC_OK + +def command_p(current_buffer, args): + w.prnt("", "{}".format(eval(args))) ###### NEW EXCEPTIONS @@ -1040,11 +1345,11 @@ def dbg(message, main_buffer=False, fout=False): if fout: file('/tmp/debug.log', 'a+').writelines(message + '\n') if main_buffer: - w.prnt("", "---------") + #w.prnt("", "---------") w.prnt("", "slack: " + message) else: if slack_debug and (not debug_string or debug_string in message): - w.prnt(slack_debug, "---------") + #w.prnt(slack_debug, "---------") w.prnt(slack_debug, message) @@ -1170,30 +1475,31 @@ if __name__ == "__main__": # attach to the weechat hooks we need #w.hook_timer(1000, 0, 0, "typing_update_cb", "") - #w.hook_timer(1000, 0, 0, "buffer_list_update_cb", "") + w.hook_timer(1000, 0, 0, "buffer_list_update_callback", "EVENTROUTER") w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") w.hook_signal('buffer_closing', "buffer_closing_callback", "EVENTROUTER") - w.hook_signal('buffer_opened', "buffer_opened_cb", "") + #w.hook_signal('buffer_opened', "buffer_opened_cb", "") w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") w.hook_signal('window_switch', "buffer_switch_callback", "EVENTROUTER") #w.hook_signal('input_text_changed', "typing_notification_cb", "") w.hook_signal('quit', "quit_notification_cb", "") w.hook_signal('window_scrolled', "scrolled_cb", "") - #w.hook_command( - # # Command name and description - # 'slack', 'Plugin to allow typing notification and sync of read markers for slack.com', - # # Usage - # '[command] [command options]', - # # Description of arguments - # 'Commands:\n' + - # '\n'.join(cmds.keys()) + - # '\nUse /slack help [command] to find out more\n', - # # Completions - # '|'.join(cmds.keys()), - # # Function name - # 'slack_command_cb', '') - # w.hook_command('me', 'me_command_cb', '') + cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} + w.hook_command( + # Command name and description + 'slack', 'Plugin to allow typing notification and sync of read markers for slack.com', + # Usage + '[command] [command options]', + # Description of arguments + 'Commands:\n' + + '\n'.join(cmds.keys()) + + '\nUse /slack help [command] to find out more\n', + # Completions + '|'.join(cmds.keys()), + # Function name + 'slack_command_cb', '') + w.hook_command('me', 'me_command_cb', '') w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') w.hook_command_run('/query', 'join_command_cb', '') w.hook_command_run('/join', 'join_command_cb', '') @@ -1210,8 +1516,6 @@ if __name__ == "__main__": tok = config.slack_api_token.split(',')[0] s = SlackRequest(tok, 'rtm.start', {}) - #async_slack_api_request("slack.com", self.token, "rtm.start", {"ts": t}) - #s = SlackRequest('xoxoxoxox', "blah.get", {"meh": "blah"}) global EVENTROUTER EVENTROUTER = EventRouter() EVENTROUTER.receive(s) -- cgit From 0403bd65ba35744d6adaa3946f1a79d52fd981cc Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 11:27:31 -0800 Subject: add rtmstart --- _pytest/data/http/rtm.start | 861 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 861 insertions(+) create mode 100644 _pytest/data/http/rtm.start diff --git a/_pytest/data/http/rtm.start b/_pytest/data/http/rtm.start new file mode 100644 index 0000000..d9cc464 --- /dev/null +++ b/_pytest/data/http/rtm.start @@ -0,0 +1,861 @@ +{ + "ok": true, + "self": { + "id": "U3ZKBBDL5", + "name": "bob", + "prefs": { + "highlight_words": "", + "user_colors": "", + "color_names_in_list": true, + "growls_enabled": true, + "tz": "America\/Los_Angeles", + "push_dm_alert": true, + "push_mention_alert": true, + "push_everything": true, + "push_show_preview": true, + "push_idle_wait": 2, + "push_sound": "b2.mp3", + "push_loud_channels": "", + "push_mention_channels": "", + "push_loud_channels_set": "", + "threads_everything": false, + "email_alerts": "instant", + "email_alerts_sleep_until": 0, + "email_misc": false, + "email_weekly": true, + "welcome_message_hidden": false, + "all_channels_loud": true, + "loud_channels": "", + "never_channels": "", + "loud_channels_set": "", + "search_sort": "timestamp", + "expand_inline_imgs": true, + "expand_internal_inline_imgs": true, + "expand_snippets": false, + "posts_formatting_guide": true, + "seen_welcome_2": true, + "seen_ssb_prompt": false, + "spaces_new_xp_banner_dismissed": false, + "search_only_my_channels": false, + "search_only_current_team": false, + "emoji_mode": "default", + "emoji_use": "", + "has_invited": false, + "has_uploaded": false, + "has_created_channel": false, + "has_searched": false, + "search_exclude_channels": "", + "messages_theme": "default", + "webapp_spellcheck": true, + "no_joined_overlays": false, + "no_created_overlays": false, + "dropbox_enabled": false, + "seen_domain_invite_reminder": false, + "seen_member_invite_reminder": false, + "mute_sounds": false, + "arrow_history": false, + "tab_ui_return_selects": true, + "obey_inline_img_limit": true, + "new_msg_snd": "knock_brush.mp3", + "require_at": false, + "ssb_space_window": "", + "mac_ssb_bounce": "", + "mac_ssb_bullet": true, + "expand_non_media_attachments": true, + "show_typing": true, + "pagekeys_handled": true, + "last_snippet_type": "", + "display_real_names_override": 0, + "display_preferred_names": true, + "time24": false, + "enter_is_special_in_tbt": false, + "graphic_emoticons": false, + "convert_emoticons": true, + "ss_emojis": true, + "sidebar_behavior": "", + "seen_onboarding_start": false, + "onboarding_cancelled": true, + "seen_onboarding_slackbot_conversation": false, + "seen_onboarding_channels": false, + "seen_onboarding_direct_messages": false, + "seen_onboarding_invites": false, + "seen_onboarding_search": false, + "seen_onboarding_recent_mentions": false, + "seen_onboarding_starred_items": false, + "seen_onboarding_private_groups": false, + "onboarding_slackbot_conversation_step": 0, + "dnd_enabled": true, + "dnd_start_hour": "22:00", + "dnd_end_hour": "08:00", + "mark_msgs_read_immediately": true, + "start_scroll_at_oldest": true, + "snippet_editor_wrap_long_lines": false, + "ls_disabled": false, + "sidebar_theme": "default", + "sidebar_theme_custom_values": "", + "f_key_search": false, + "k_key_omnibox": true, + "speak_growls": false, + "mac_speak_voice": "com.apple.speech.synthesis.voice.Alex", + "mac_speak_speed": 250, + "at_channel_suppressed_channels": "", + "push_at_channel_suppressed_channels": "", + "prompted_for_email_disabling": false, + "full_text_extracts": false, + "no_text_in_notifications": false, + "muted_channels": "", + "no_macelectron_banner": false, + "no_macssb1_banner": false, + "no_macssb2_banner": false, + "no_winssb1_banner": false, + "no_invites_widget_in_sidebar": false, + "no_omnibox_in_channels": false, + "k_key_omnibox_auto_hide_count": 0, + "prev_next_btn": false, + "hide_user_group_info_pane": false, + "mentions_exclude_at_user_groups": false, + "privacy_policy_seen": true, + "enterprise_migration_seen": true, + "last_tos_acknowledged": "tos_oct2016", + "search_exclude_bots": false, + "load_lato_2": false, + "fuller_timestamps": false, + "last_seen_at_channel_warning": 0, + "msg_preview": false, + "msg_preview_persistent": true, + "emoji_autocomplete_big": false, + "winssb_run_from_tray": true, + "winssb_window_flash_behavior": "idle", + "two_factor_auth_enabled": false, + "two_factor_type": null, + "two_factor_backup_type": null, + "hide_hex_swatch": false, + "client_logs_pri": "", + "enhanced_debugging": false, + "thin_channel_membership_fe": false, + "flannel_server_pool": "random", + "mentions_exclude_at_channels": true, + "confirm_clear_all_unreads": true, + "confirm_user_marked_away": true, + "box_enabled": false, + "seen_single_emoji_msg": false, + "confirm_sh_call_start": true, + "preferred_skin_tone": "", + "show_all_skin_tones": false, + "separate_private_channels": false, + "whats_new_read": 1485969645, + "hotness": false, + "frecency_jumper": "", + "frecency_ent_jumper": "", + "jumbomoji": true, + "newxp_seen_last_message": "1", + "attachments_with_borders": false, + "show_memory_instrument": false, + "enable_unread_view": false, + "seen_unread_view_coachmark": false, + "seen_calls_video_beta_coachmark": false, + "seen_calls_video_ga_coachmark": false, + "seen_calls_ss_window_coachmark": false, + "measure_css_usage": false, + "enable_react_emoji_picker": true, + "seen_replies_coachmark": false, + "seen_custom_status_badge": false, + "all_unreads_sort_order": "alphabetical", + "locale": "en_US", + "gdrive_authed": true, + "gdrive_enabled": true, + "seen_gdrive_coachmark": false, + "channel_sort": "{\"is_custom_sorted\":false, \"priority_display\":false, \"priority_type\":\"\", \"sorts\":[]}", + "overloaded_message_enabled": true, + "seen_highlights_coachmark": false, + "seen_highlights_arrows_coachmark": false, + "a11y_font_size": "normal", + "a11y_animations": true, + "intro_to_apps_message_seen": false + }, + "created": 1485969645, + "manual_presence": "active" + }, + "team": { + "id": "T3YS5EAL9", + "name": "weeslacktest", + "email_domain": "", + "domain": "weeslacktest", + "msg_edit_window_mins": -1, + "prefs": { + "invites_only_admins": false, + "default_channels": [ + "C407ABS94", + "C3ZEQAYN7" + ], + "display_email_addresses": false, + "hide_referers": true, + "msg_edit_window_mins": -1, + "allow_message_deletion": true, + "calling_app_name": "Slack", + "allow_calls": true, + "display_real_names": false, + "who_can_at_everyone": "regular", + "who_can_at_channel": "ra", + "who_can_create_channels": "regular", + "who_can_archive_channels": "regular", + "who_can_create_groups": "ra", + "who_can_post_general": "ra", + "who_can_kick_channels": "admin", + "who_can_kick_groups": "regular", + "retention_type": 0, + "retention_duration": 0, + "group_retention_type": 0, + "group_retention_duration": 0, + "dm_retention_type": 0, + "dm_retention_duration": 0, + "file_retention_duration": 0, + "file_retention_type": 0, + "allow_retention_override": true, + "require_at_for_mention": false, + "default_rxns": [ + "simple_smile", + "thumbsup", + "white_check_mark", + "heart", + "eyes" + ], + "team_handy_rxns": { + "restrict": false, + "list": [ + { + "name": "slightly_smiling_face", + "title": "I'm happy!" + }, + { + "name": "+1", + "title": "good!" + }, + { + "name": "white_check_mark", + "title": "done!" + }, + { + "name": "heart", + "title": "love it!" + }, + { + "name": "eyes", + "title": "looking..." + } + ] + }, + "channel_handy_rxns": null, + "compliance_export_start": 0, + "warn_before_at_channel": "always", + "disallow_public_file_urls": false, + "who_can_create_delete_user_groups": "admin", + "who_can_edit_user_groups": "admin", + "who_can_change_team_profile": "admin", + "allow_shared_channels": false, + "who_has_team_visibility": "ra", + "disable_file_uploads": "allow_all", + "disable_file_editing": false, + "disable_file_deleting": false, + "who_can_create_shared_channels": "admin", + "who_can_manage_shared_channels": { + "type": [ + "admin" + ] + }, + "who_can_post_in_shared_channels": { + "type": [ + "regular", + "ra" + ] + }, + "allow_shared_channel_perms_override": false, + "gdrive_enabled_team": true, + "dnd_enabled": true, + "dnd_start_hour": "22:00", + "dnd_end_hour": "08:00", + "auth_mode": "normal", + "who_can_manage_integrations": { + "type": [ + "regular" + ] + }, + "invites_limit": true + }, + "icon": { + "image_34": "https:\/\/a.slack-edge.com\/0180\/img\/avatars-teams\/ava_0014-34.png", + "image_44": "https:\/\/a.slack-edge.com\/0180\/img\/avatars-teams\/ava_0014-44.png", + "image_68": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-68.png", + "image_88": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-88.png", + "image_102": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-102.png", + "image_132": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-132.png", + "image_230": "https:\/\/a.slack-edge.com\/bfaba\/img\/avatars-teams\/ava_0014-230.png", + "image_default": true + }, + "over_storage_limit": false, + "plan": "", + "avatar_base_url": "https:\/\/ca.slack-edge.com\/", + "over_integrations_limit": true + }, + "latest_event_ts": "1485976462.000000", + "channels": [ + { + "id": "C407ABS94", + "name": "general", + "is_channel": true, + "created": 1485969592, + "creator": "U407ABLLW", + "is_archived": false, + "is_general": true, + "has_pins": false, + "is_member": true, + "last_read": "1485976236.000019", + "latest": { + "type": "message", + "subtype": "file_comment", + "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.", + "file": { + "id": "F3ZJQTA66", + "created": 1485975955, + "timestamp": 1485975955, + "name": "1x1.png", + "title": "1x1.png", + "mimetype": "image\/png", + "filetype": "png", + "pretty_type": "PNG", + "user": "U407ABLLW", + "editable": false, + "size": 68, + "mode": "hosted", + "is_external": false, + "external_type": "", + "is_public": true, + "public_url_shared": false, + "display_as_bot": false, + "username": "", + "url_private": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZJQTA66\/1x1.png", + "url_private_download": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZJQTA66\/download\/1x1.png", + "thumb_64": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_64.png", + "thumb_80": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_80.png", + "thumb_360": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_360.png", + "thumb_360_w": 1, + "thumb_360_h": 1, + "thumb_160": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_160.png", + "image_exif_rotation": 1, + "original_w": 1, + "original_h": 1, + "permalink": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZJQTA66\/1x1.png", + "permalink_public": "https:\/\/slack-files.com\/T3YS5EAL9-F3ZJQTA66-5d747593d2", + "channels": [ + "C407ABS94" + ], + "groups": [], + "ims": [], + "comments_count": 1 + }, + "comment": { + "id": "Fc3ZMDRQLV", + "created": 1485976236, + "timestamp": 1485976236, + "user": "U407ABLLW", + "is_intro": false, + "comment": "first comment! now i edited it.", + "channel": "" + }, + "is_intro": false, + "ts": "1485976236.000019" + }, + "unread_count": 0, + "unread_count_display": 0, + "members": [ + "U3ZKBBDL5", + "U407ABLLW", + "U4096CBHC" + ], + "topic": { + "value": "Company-wide announcements and work-based matters", + "creator": "", + "last_set": 0 + }, + "purpose": { + "value": "This channel is for team-wide communication and announcements. All team members are in this channel.", + "creator": "", + "last_set": 0 + }, + "previous_names": [] + }, + { + "id": "C3ZM2GMGU", + "name": "made-to-be-archived", + "is_channel": true, + "created": 1485976107, + "creator": "U407ABLLW", + "is_archived": false, + "is_general": false, + "has_pins": false, + "is_member": false, + "previous_names": [] + }, + { + "id": "C3ZEQAYN7", + "name": "random", + "is_channel": true, + "created": 1485969592, + "creator": "U407ABLLW", + "is_archived": false, + "is_general": false, + "has_pins": false, + "is_member": true, + "last_read": "1485969592.000002", + "latest": { + "user": "U4096CBHC", + "text": "<@U4096CBHC|charles> has joined the channel", + "type": "message", + "subtype": "channel_join", + "ts": "1485975606.000004" + }, + "unread_count": 2, + "unread_count_display": 0, + "members": [ + "U3ZKBBDL5", + "U407ABLLW", + "U4096CBHC" + ], + "topic": { + "value": "Non-work banter and water cooler conversation", + "creator": "", + "last_set": 0 + }, + "purpose": { + "value": "A place for non-work-related flimflam, faffing, hodge-podge or jibber-jabber you'd prefer to keep out of more focused work-related channels.", + "creator": "", + "last_set": 0 + }, + "previous_names": [] + }, + { + "id": "C3ZM8JTD3", + "name": "some-channel2-renamed", + "is_channel": true, + "created": 1485975747, + "creator": "U407ABLLW", + "is_archived": false, + "is_general": false, + "has_pins": false, + "is_member": false, + "previous_names": [ + "some-channel2" + ] + } + ], + "groups": [ + { + "id": "G3ZGMF4RZ", + "name": "mpdm-bob--alice--charles-1", + "is_group": true, + "created": 1485975690, + "creator": "U407ABLLW", + "is_archived": false, + "is_mpim": true, + "has_pins": false, + "is_open": true, + "last_read": "0000000000.000000", + "latest": { + "type": "message", + "subtype": "file_share", + "text": "<@U407ABLLW|alice> uploaded a file: ", + "file": { + "id": "F3ZLY6K5J", + "created": 1485975715, + "timestamp": 1485975715, + "name": "-.txt", + "title": "Untitled", + "mimetype": "text\/plain", + "filetype": "text", + "pretty_type": "Plain Text", + "user": "U407ABLLW", + "editable": true, + "size": 14, + "mode": "snippet", + "is_external": false, + "external_type": "", + "is_public": false, + "public_url_shared": false, + "display_as_bot": false, + "username": "", + "url_private": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZLY6K5J\/-.txt", + "url_private_download": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZLY6K5J\/download\/-.txt", + "permalink": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZLY6K5J\/-.txt", + "permalink_public": "https:\/\/slack-files.com\/T3YS5EAL9-F3ZLY6K5J-39c2c4f739", + "edit_link": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZLY6K5J\/-.txt\/edit", + "preview": "some code here", + "preview_highlight": "
\n
\n
some code here<\/pre><\/div>\n<\/div>\n<\/div>\n",
+                    "lines": 1,
+                    "lines_more": 0,
+                    "preview_is_truncated": false,
+                    "channels": [],
+                    "groups": [
+                        "G3ZGMF4RZ"
+                    ],
+                    "ims": [],
+                    "comments_count": 0
+                },
+                "user": "U407ABLLW",
+                "upload": true,
+                "display_as_bot": false,
+                "username": "<@U407ABLLW|alice>",
+                "bot_id": null,
+                "ts": "1485975715.000005"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "members": [
+                "U407ABLLW",
+                "U3ZKBBDL5",
+                "U4096CBHC"
+            ],
+            "topic": {
+                "value": "Group messaging",
+                "creator": "U407ABLLW",
+                "last_set": 1485975690
+            },
+            "purpose": {
+                "value": "Group messaging with: @bob @alice @charles",
+                "creator": "U407ABLLW",
+                "last_set": 1485975690
+            }
+        },
+        {
+            "id": "G3ZJKP7GA",
+            "name": "some-private-channel",
+            "is_group": true,
+            "created": 1485975492,
+            "creator": "U407ABLLW",
+            "is_archived": false,
+            "is_mpim": false,
+            "has_pins": false,
+            "is_open": true,
+            "last_read": "1485975492.000002",
+            "latest": {
+                "user": "U407ABLLW",
+                "purpose": "seekret",
+                "text": "<@U407ABLLW|alice> set the channel's purpose: seekret",
+                "type": "message",
+                "subtype": "group_purpose",
+                "ts": "1485975492.000004"
+            },
+            "unread_count": 2,
+            "unread_count_display": 1,
+            "members": [
+                "U3ZKBBDL5",
+                "U407ABLLW"
+            ],
+            "topic": {
+                "value": "",
+                "creator": "",
+                "last_set": 0
+            },
+            "purpose": {
+                "value": "seekret",
+                "creator": "U407ABLLW",
+                "last_set": 1485975493
+            }
+        },
+        {
+            "id": "G409GKN9M",
+            "name": "some-channel",
+            "is_group": true,
+            "created": 1485975458,
+            "creator": "U407ABLLW",
+            "is_archived": true,
+            "is_mpim": false,
+            "has_pins": false,
+            "is_open": false,
+            "last_read": "1485975476.876631",
+            "latest": {
+                "user": "U407ABLLW",
+                "text": "<@U407ABLLW|alice> archived the private channel",
+                "type": "message",
+                "subtype": "group_archive",
+                "ts": "1485975735.000006"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "members": [
+                "U3ZKBBDL5",
+                "U407ABLLW"
+            ],
+            "topic": {
+                "value": "",
+                "creator": "U407ABLLW",
+                "last_set": 1485975476
+            },
+            "purpose": {
+                "value": "who knows?",
+                "creator": "U407ABLLW",
+                "last_set": 1485975476
+            }
+        }
+    ],
+    "ims": [
+        {
+            "id": "D3ZK1D8JY",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "USLACKBOT",
+            "has_pins": false,
+            "last_read": "1485969667.000002",
+            "latest": {
+                "type": "message",
+                "user": "USLACKBOT",
+                "text": "<@U407ABLLW|alice> archived the private channel ",
+                "ts": "1485975735.000002"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "is_open": true
+        },
+        {
+            "id": "D3ZEQULFM",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U3ZKBBDL5",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": null,
+            "unread_count": 0,
+            "unread_count_display": 0,
+            "is_open": true
+        },
+        {
+            "id": "D3ZEQULHZ",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U407ABLLW",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": {
+                "type": "message",
+                "user": "U407ABLLW",
+                "text": "hi bob",
+                "ts": "1485975421.000002"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "is_open": true
+        },
+        {
+            "id": "D409J34CF",
+            "created": 1485975606,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U4096CBHC",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": null,
+            "unread_count": 0,
+            "unread_count_display": 0,
+            "is_open": true
+        }
+    ],
+    "cache_ts": 1485977062,
+    "read_only_channels": [],
+    "can_manage_shared_channels": false,
+    "subteams": {
+        "self": [],
+        "all": []
+    },
+    "dnd": {
+        "dnd_enabled": true,
+        "dnd_debug": {
+            "next_dnd_start_date": "Wed, 01 Feb 2017 22:00:00 -0800",
+            "next_dnd_end_date": "Thu, 02 Feb 2017 08:00:00 -0800",
+            "now": "Wed, 01 Feb 2017 11:24:22 -0800",
+            "type": "slack"
+        },
+        "next_dnd_start_ts": 1486015200,
+        "next_dnd_end_ts": 1486051200,
+        "snooze_enabled": false
+    },
+    "users": [
+        {
+            "id": "U407ABLLW",
+            "team_id": "T3YS5EAL9",
+            "name": "alice",
+            "deleted": false,
+            "status": null,
+            "color": "9f69e7",
+            "real_name": "First Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "First",
+                "last_name": "Testuser",
+                "avatar_hash": "gfd0ce7a168d",
+                "real_name": "First Testuser",
+                "real_name_normalized": "First Testuser",
+                "email": "redacted1@gmail.com",
+                "image_24": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-24.png",
+                "image_32": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-32.png",
+                "image_48": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-48.png",
+                "image_72": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png",
+                "image_192": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png",
+                "image_512": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-512.png",
+                "fields": null
+            },
+            "is_admin": true,
+            "is_owner": true,
+            "is_primary_owner": true,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "away"
+        },
+        {
+            "id": "U3ZKBBDL5",
+            "team_id": "T3YS5EAL9",
+            "name": "bob",
+            "deleted": false,
+            "status": null,
+            "color": "4bbe2e",
+            "real_name": "Second Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "Second",
+                "last_name": "Testuser",
+                "avatar_hash": "g6f7a5bf7eb7",
+                "real_name": "Second Testuser",
+                "real_name_normalized": "Second Testuser",
+                "email": "redacted2@gmail.com",
+                "image_24": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-24.png",
+                "image_32": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-32.png",
+                "image_48": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-48.png",
+                "image_72": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png",
+                "image_192": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0019-192.png",
+                "image_512": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0019-512.png",
+                "fields": null
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "has_2fa": false,
+            "presence": "active"
+        },
+        {
+            "id": "U4096CBHC",
+            "team_id": "T3YS5EAL9",
+            "name": "charles",
+            "deleted": false,
+            "status": null,
+            "color": "e7392d",
+            "real_name": "Charles Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "Charles",
+                "last_name": "Testuser",
+                "avatar_hash": "5af404f7d4b7",
+                "fields": [],
+                "image_24": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_32": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_48": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_72": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_192": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_512": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_1024": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_original": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_original.png",
+                "real_name": "Charles Testuser",
+                "real_name_normalized": "Charles Testuser",
+                "email": "redacted3@gmail.com"
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "away"
+        },
+        {
+            "id": "USLACKBOT",
+            "team_id": "T3YS5EAL9",
+            "name": "slackbot",
+            "deleted": false,
+            "status": null,
+            "color": "757575",
+            "real_name": "slackbot",
+            "tz": null,
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "slackbot",
+                "last_name": "",
+                "image_24": "https:\/\/a.slack-edge.com\/0180\/img\/slackbot_24.png",
+                "image_32": "https:\/\/a.slack-edge.com\/2fac\/plugins\/slackbot\/assets\/service_32.png",
+                "image_48": "https:\/\/a.slack-edge.com\/2fac\/plugins\/slackbot\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/0180\/img\/slackbot_72.png",
+                "image_192": "https:\/\/a.slack-edge.com\/66f9\/img\/slackbot_192.png",
+                "image_512": "https:\/\/a.slack-edge.com\/1801\/img\/slackbot_512.png",
+                "avatar_hash": "sv1444671949",
+                "real_name": "slackbot",
+                "real_name_normalized": "slackbot",
+                "fields": null
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "active"
+        }
+    ],
+    "cache_version": "v15-koala",
+    "cache_ts_version": "v1-cat",
+    "bots": [
+        {
+            "id": "B3YTBU6L8",
+            "deleted": false,
+            "name": "incoming-webhook",
+            "app_id": "A0F7XDUAZ",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_72.png"
+            }
+        },
+        {
+            "id": "B3ZESMZKM",
+            "deleted": false,
+            "name": "Slack API Tester",
+            "app_id": "A02",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_72.png"
+            }
+        },
+        {
+            "id": "B407MVCA3",
+            "deleted": false,
+            "name": "gdrive",
+            "app_id": "A0F7YS32P",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_72.png"
+            }
+        }
+    ],
+    "url": "wss:\/\/mpmulti-9ry9.slack-msgs.com\/websocket\/U4n-6Yw8mpc9C64E74gYXGbojir25QCCcLJ9eK57KDURr0o5so6x4qZU-pKn-LX0bYt-ZmIaFcN4TxoKPry1eRtOwiCNSyGyIGxnYrUtrsvMvkFr5C2-osZVL85WqzIWRoE00sBma3U8BkKfi8oUmMoYGKFFVeJ2WPI_ygHRPOo="
+}
-- 
cgit 


From e63a037a323221e24ba0f422d8d81ee77f0b3ce0 Mon Sep 17 00:00:00 2001
From: Ryan Huber 
Date: Wed, 1 Feb 2017 11:44:02 -0800
Subject: json not str(dict)

---
 _pytest/data/http/rtm.start                        | 861 ---------------------
 _pytest/data/http/rtm.start.json                   | 861 +++++++++++++++++++++
 .../websocket/1485975367.79-reconnect_url.json     |   2 +-
 .../data/websocket/1485975408.19-user_typing.json  |   2 +-
 .../data/websocket/1485975412.74-user_typing.json  |   2 +-
 .../data/websocket/1485975420.36-user_typing.json  |   2 +-
 _pytest/data/websocket/1485975421.33-message.json  |   2 +-
 .../1485975421.42-desktop_notification.json        |   2 +-
 .../websocket/1485975426.55-reaction_added.json    |   2 +-
 .../websocket/1485975428.76-reaction_removed.json  |   2 +-
 .../websocket/1485975458.35-channel_created.json   |   2 +-
 .../websocket/1485975458.87-channel_joined.json    |   2 +-
 _pytest/data/websocket/1485975458.87-message.json  |   2 +-
 .../1485975462.62-update_thread_state.json         |   2 +-
 .../data/websocket/1485975462.62-user_typing.json  |   2 +-
 .../websocket/1485975476.62-channel_deleted.json   |   2 +-
 .../data/websocket/1485975476.86-group_join.json   |   2 +-
 .../data/websocket/1485975476.86-group_joined.json |   2 +-
 .../1485975487.69-group_history_changed.json       |   2 +-
 .../data/websocket/1485975487.69-group_join.json   |   2 +-
 .../data/websocket/1485975493.19-group_joined.json |   2 +-
 _pytest/data/websocket/1485975493.23-message.json  |   2 +-
 _pytest/data/websocket/1485975547.75-message.json  |   2 +-
 .../1485975547.75-update_thread_state.json         |   2 +-
 .../websocket/1485975547.83-reconnect_url.json     |   2 +-
 .../data/websocket/1485975606.59-team_join.json    |   2 +-
 _pytest/data/websocket/1485975606.67-message.json  |   2 +-
 .../data/websocket/1485975606.75-im_created.json   |   2 +-
 _pytest/data/websocket/1485975606.75-im_open.json  |   2 +-
 .../websocket/1485975611.29-presence_change.json   |   2 +-
 .../data/websocket/1485975640.5-user_change.json   |   2 +-
 .../data/websocket/1485975669.14-user_change.json  |   2 +-
 .../websocket/1485975675.81-presence_change.json   |   2 +-
 .../data/websocket/1485975690.59-mpim_joined.json  |   2 +-
 .../data/websocket/1485975690.67-group_joined.json |   2 +-
 .../1485975690.67-update_thread_state.json         |   2 +-
 .../data/websocket/1485975697.58-user_typing.json  |   2 +-
 _pytest/data/websocket/1485975698.45-message.json  |   2 +-
 .../websocket/1485975701.72-reaction_added.json    |   2 +-
 .../websocket/1485975703.22-reaction_removed.json  |   2 +-
 _pytest/data/websocket/1485975715.79-message.json  |   2 +-
 .../data/websocket/1485975715.87-file_shared.json  |   2 +-
 _pytest/data/websocket/1485975723.85-message.json  |   2 +-
 .../websocket/1485975727.84-reconnect_url.json     |   2 +-
 .../websocket/1485975735.91-group_archive.json     |   2 +-
 _pytest/data/websocket/1485975735.99-message.json  |   2 +-
 _pytest/data/websocket/1485975736.08-message.json  |   2 +-
 .../1485975738.1-desktop_notification.json         |   2 +-
 .../websocket/1485975747.17-channel_created.json   |   2 +-
 .../websocket/1485975757.63-channel_rename.json    |   2 +-
 .../websocket/1485975771.6-presence_change.json    |   2 +-
 .../websocket/1485975774.62-presence_change.json   |   2 +-
 .../data/websocket/1485975822.17-user_typing.json  |   2 +-
 _pytest/data/websocket/1485975824.48-message.json  |   2 +-
 _pytest/data/websocket/1485975836.23-message.json  |   2 +-
 _pytest/data/websocket/1485975836.31-message.json  |   2 +-
 _pytest/data/websocket/1485975842.1-message.json   |   2 +-
 _pytest/data/websocket/1485975842.18-message.json  |   2 +-
 .../data/websocket/1485975850.32-mpim_open.json    |   2 +-
 .../data/websocket/1485975850.45-group_open.json   |   2 +-
 .../websocket/1485975858.56-reaction_added.json    |   2 +-
 _pytest/data/websocket/1485975890.7-bot_added.json |   2 +-
 _pytest/data/websocket/1485975890.78-message.json  |   2 +-
 _pytest/data/websocket/1485975896.16-message.json  |   2 +-
 .../websocket/1485975907.84-reconnect_url.json     |   2 +-
 .../data/websocket/1485975926.59-user_change.json  |   2 +-
 .../data/websocket/1485975930.81-user_change.json  |   2 +-
 .../data/websocket/1485975956.38-file_public.json  |   2 +-
 .../data/websocket/1485975956.47-file_shared.json  |   2 +-
 _pytest/data/websocket/1485975956.47-message.json  |   2 +-
 _pytest/data/websocket/1485975978.79-message.json  |   2 +-
 .../data/websocket/1485975978.91-file_public.json  |   2 +-
 .../data/websocket/1485975978.91-file_shared.json  |   2 +-
 .../data/websocket/1485975995.98-file_change.json  |   2 +-
 .../data/websocket/1485975998.13-file_change.json  |   2 +-
 .../data/websocket/1485976002.57-file_deleted.json |   2 +-
 _pytest/data/websocket/1485976002.66-message.json  |   2 +-
 .../data/websocket/1485976024.46-user_typing.json  |   2 +-
 .../data/websocket/1485976028.31-user_typing.json  |   2 +-
 .../websocket/1485976039.32-reaction_added.json    |   2 +-
 .../websocket/1485976040.8-reaction_removed.json   |   2 +-
 .../websocket/1485976087.84-reconnect_url.json     |   2 +-
 .../websocket/1485976107.57-channel_created.json   |   2 +-
 .../data/websocket/1485976114.72-user_typing.json  |   2 +-
 .../data/websocket/1485976117.99-user_typing.json  |   2 +-
 .../data/websocket/1485976124.9-user_typing.json   |   2 +-
 _pytest/data/websocket/1485976124.98-message.json  |   2 +-
 .../1485976125.06-desktop_notification.json        |   2 +-
 .../data/websocket/1485976125.95-user_typing.json  |   2 +-
 .../data/websocket/1485976129.49-user_typing.json  |   2 +-
 .../data/websocket/1485976133.0-user_typing.json   |   2 +-
 .../data/websocket/1485976137.51-user_typing.json  |   2 +-
 _pytest/data/websocket/1485976137.95-message.json  |   2 +-
 .../data/websocket/1485976138.73-user_typing.json  |   2 +-
 .../data/websocket/1485976141.79-user_typing.json  |   2 +-
 .../data/websocket/1485976145.8-user_typing.json   |   2 +-
 .../data/websocket/1485976149.89-user_typing.json  |   2 +-
 _pytest/data/websocket/1485976151.6-message.json   |   2 +-
 _pytest/data/websocket/1485976157.18-message.json  |   2 +-
 .../data/websocket/1485976157.8-user_typing.json   |   2 +-
 .../data/websocket/1485976161.29-user_typing.json  |   2 +-
 _pytest/data/websocket/1485976161.75-message.json  |   2 +-
 .../websocket/1485976182.59-channel_archive.json   |   2 +-
 .../websocket/1485976186.62-channel_unarchive.json |   2 +-
 _pytest/data/websocket/1485976236.58-message.json  |   2 +-
 .../1485976236.67-file_comment_added.json          |   2 +-
 _pytest/data/websocket/1485976248.57-message.json  |   2 +-
 .../1485976248.65-file_comment_edited.json         |   2 +-
 .../websocket/1485976267.81-reconnect_url.json     |   2 +-
 109 files changed, 968 insertions(+), 968 deletions(-)
 delete mode 100644 _pytest/data/http/rtm.start
 create mode 100644 _pytest/data/http/rtm.start.json

diff --git a/_pytest/data/http/rtm.start b/_pytest/data/http/rtm.start
deleted file mode 100644
index d9cc464..0000000
--- a/_pytest/data/http/rtm.start
+++ /dev/null
@@ -1,861 +0,0 @@
-{
-    "ok": true,
-    "self": {
-        "id": "U3ZKBBDL5",
-        "name": "bob",
-        "prefs": {
-            "highlight_words": "",
-            "user_colors": "",
-            "color_names_in_list": true,
-            "growls_enabled": true,
-            "tz": "America\/Los_Angeles",
-            "push_dm_alert": true,
-            "push_mention_alert": true,
-            "push_everything": true,
-            "push_show_preview": true,
-            "push_idle_wait": 2,
-            "push_sound": "b2.mp3",
-            "push_loud_channels": "",
-            "push_mention_channels": "",
-            "push_loud_channels_set": "",
-            "threads_everything": false,
-            "email_alerts": "instant",
-            "email_alerts_sleep_until": 0,
-            "email_misc": false,
-            "email_weekly": true,
-            "welcome_message_hidden": false,
-            "all_channels_loud": true,
-            "loud_channels": "",
-            "never_channels": "",
-            "loud_channels_set": "",
-            "search_sort": "timestamp",
-            "expand_inline_imgs": true,
-            "expand_internal_inline_imgs": true,
-            "expand_snippets": false,
-            "posts_formatting_guide": true,
-            "seen_welcome_2": true,
-            "seen_ssb_prompt": false,
-            "spaces_new_xp_banner_dismissed": false,
-            "search_only_my_channels": false,
-            "search_only_current_team": false,
-            "emoji_mode": "default",
-            "emoji_use": "",
-            "has_invited": false,
-            "has_uploaded": false,
-            "has_created_channel": false,
-            "has_searched": false,
-            "search_exclude_channels": "",
-            "messages_theme": "default",
-            "webapp_spellcheck": true,
-            "no_joined_overlays": false,
-            "no_created_overlays": false,
-            "dropbox_enabled": false,
-            "seen_domain_invite_reminder": false,
-            "seen_member_invite_reminder": false,
-            "mute_sounds": false,
-            "arrow_history": false,
-            "tab_ui_return_selects": true,
-            "obey_inline_img_limit": true,
-            "new_msg_snd": "knock_brush.mp3",
-            "require_at": false,
-            "ssb_space_window": "",
-            "mac_ssb_bounce": "",
-            "mac_ssb_bullet": true,
-            "expand_non_media_attachments": true,
-            "show_typing": true,
-            "pagekeys_handled": true,
-            "last_snippet_type": "",
-            "display_real_names_override": 0,
-            "display_preferred_names": true,
-            "time24": false,
-            "enter_is_special_in_tbt": false,
-            "graphic_emoticons": false,
-            "convert_emoticons": true,
-            "ss_emojis": true,
-            "sidebar_behavior": "",
-            "seen_onboarding_start": false,
-            "onboarding_cancelled": true,
-            "seen_onboarding_slackbot_conversation": false,
-            "seen_onboarding_channels": false,
-            "seen_onboarding_direct_messages": false,
-            "seen_onboarding_invites": false,
-            "seen_onboarding_search": false,
-            "seen_onboarding_recent_mentions": false,
-            "seen_onboarding_starred_items": false,
-            "seen_onboarding_private_groups": false,
-            "onboarding_slackbot_conversation_step": 0,
-            "dnd_enabled": true,
-            "dnd_start_hour": "22:00",
-            "dnd_end_hour": "08:00",
-            "mark_msgs_read_immediately": true,
-            "start_scroll_at_oldest": true,
-            "snippet_editor_wrap_long_lines": false,
-            "ls_disabled": false,
-            "sidebar_theme": "default",
-            "sidebar_theme_custom_values": "",
-            "f_key_search": false,
-            "k_key_omnibox": true,
-            "speak_growls": false,
-            "mac_speak_voice": "com.apple.speech.synthesis.voice.Alex",
-            "mac_speak_speed": 250,
-            "at_channel_suppressed_channels": "",
-            "push_at_channel_suppressed_channels": "",
-            "prompted_for_email_disabling": false,
-            "full_text_extracts": false,
-            "no_text_in_notifications": false,
-            "muted_channels": "",
-            "no_macelectron_banner": false,
-            "no_macssb1_banner": false,
-            "no_macssb2_banner": false,
-            "no_winssb1_banner": false,
-            "no_invites_widget_in_sidebar": false,
-            "no_omnibox_in_channels": false,
-            "k_key_omnibox_auto_hide_count": 0,
-            "prev_next_btn": false,
-            "hide_user_group_info_pane": false,
-            "mentions_exclude_at_user_groups": false,
-            "privacy_policy_seen": true,
-            "enterprise_migration_seen": true,
-            "last_tos_acknowledged": "tos_oct2016",
-            "search_exclude_bots": false,
-            "load_lato_2": false,
-            "fuller_timestamps": false,
-            "last_seen_at_channel_warning": 0,
-            "msg_preview": false,
-            "msg_preview_persistent": true,
-            "emoji_autocomplete_big": false,
-            "winssb_run_from_tray": true,
-            "winssb_window_flash_behavior": "idle",
-            "two_factor_auth_enabled": false,
-            "two_factor_type": null,
-            "two_factor_backup_type": null,
-            "hide_hex_swatch": false,
-            "client_logs_pri": "",
-            "enhanced_debugging": false,
-            "thin_channel_membership_fe": false,
-            "flannel_server_pool": "random",
-            "mentions_exclude_at_channels": true,
-            "confirm_clear_all_unreads": true,
-            "confirm_user_marked_away": true,
-            "box_enabled": false,
-            "seen_single_emoji_msg": false,
-            "confirm_sh_call_start": true,
-            "preferred_skin_tone": "",
-            "show_all_skin_tones": false,
-            "separate_private_channels": false,
-            "whats_new_read": 1485969645,
-            "hotness": false,
-            "frecency_jumper": "",
-            "frecency_ent_jumper": "",
-            "jumbomoji": true,
-            "newxp_seen_last_message": "1",
-            "attachments_with_borders": false,
-            "show_memory_instrument": false,
-            "enable_unread_view": false,
-            "seen_unread_view_coachmark": false,
-            "seen_calls_video_beta_coachmark": false,
-            "seen_calls_video_ga_coachmark": false,
-            "seen_calls_ss_window_coachmark": false,
-            "measure_css_usage": false,
-            "enable_react_emoji_picker": true,
-            "seen_replies_coachmark": false,
-            "seen_custom_status_badge": false,
-            "all_unreads_sort_order": "alphabetical",
-            "locale": "en_US",
-            "gdrive_authed": true,
-            "gdrive_enabled": true,
-            "seen_gdrive_coachmark": false,
-            "channel_sort": "{\"is_custom_sorted\":false, \"priority_display\":false, \"priority_type\":\"\", \"sorts\":[]}",
-            "overloaded_message_enabled": true,
-            "seen_highlights_coachmark": false,
-            "seen_highlights_arrows_coachmark": false,
-            "a11y_font_size": "normal",
-            "a11y_animations": true,
-            "intro_to_apps_message_seen": false
-        },
-        "created": 1485969645,
-        "manual_presence": "active"
-    },
-    "team": {
-        "id": "T3YS5EAL9",
-        "name": "weeslacktest",
-        "email_domain": "",
-        "domain": "weeslacktest",
-        "msg_edit_window_mins": -1,
-        "prefs": {
-            "invites_only_admins": false,
-            "default_channels": [
-                "C407ABS94",
-                "C3ZEQAYN7"
-            ],
-            "display_email_addresses": false,
-            "hide_referers": true,
-            "msg_edit_window_mins": -1,
-            "allow_message_deletion": true,
-            "calling_app_name": "Slack",
-            "allow_calls": true,
-            "display_real_names": false,
-            "who_can_at_everyone": "regular",
-            "who_can_at_channel": "ra",
-            "who_can_create_channels": "regular",
-            "who_can_archive_channels": "regular",
-            "who_can_create_groups": "ra",
-            "who_can_post_general": "ra",
-            "who_can_kick_channels": "admin",
-            "who_can_kick_groups": "regular",
-            "retention_type": 0,
-            "retention_duration": 0,
-            "group_retention_type": 0,
-            "group_retention_duration": 0,
-            "dm_retention_type": 0,
-            "dm_retention_duration": 0,
-            "file_retention_duration": 0,
-            "file_retention_type": 0,
-            "allow_retention_override": true,
-            "require_at_for_mention": false,
-            "default_rxns": [
-                "simple_smile",
-                "thumbsup",
-                "white_check_mark",
-                "heart",
-                "eyes"
-            ],
-            "team_handy_rxns": {
-                "restrict": false,
-                "list": [
-                    {
-                        "name": "slightly_smiling_face",
-                        "title": "I'm happy!"
-                    },
-                    {
-                        "name": "+1",
-                        "title": "good!"
-                    },
-                    {
-                        "name": "white_check_mark",
-                        "title": "done!"
-                    },
-                    {
-                        "name": "heart",
-                        "title": "love it!"
-                    },
-                    {
-                        "name": "eyes",
-                        "title": "looking..."
-                    }
-                ]
-            },
-            "channel_handy_rxns": null,
-            "compliance_export_start": 0,
-            "warn_before_at_channel": "always",
-            "disallow_public_file_urls": false,
-            "who_can_create_delete_user_groups": "admin",
-            "who_can_edit_user_groups": "admin",
-            "who_can_change_team_profile": "admin",
-            "allow_shared_channels": false,
-            "who_has_team_visibility": "ra",
-            "disable_file_uploads": "allow_all",
-            "disable_file_editing": false,
-            "disable_file_deleting": false,
-            "who_can_create_shared_channels": "admin",
-            "who_can_manage_shared_channels": {
-                "type": [
-                    "admin"
-                ]
-            },
-            "who_can_post_in_shared_channels": {
-                "type": [
-                    "regular",
-                    "ra"
-                ]
-            },
-            "allow_shared_channel_perms_override": false,
-            "gdrive_enabled_team": true,
-            "dnd_enabled": true,
-            "dnd_start_hour": "22:00",
-            "dnd_end_hour": "08:00",
-            "auth_mode": "normal",
-            "who_can_manage_integrations": {
-                "type": [
-                    "regular"
-                ]
-            },
-            "invites_limit": true
-        },
-        "icon": {
-            "image_34": "https:\/\/a.slack-edge.com\/0180\/img\/avatars-teams\/ava_0014-34.png",
-            "image_44": "https:\/\/a.slack-edge.com\/0180\/img\/avatars-teams\/ava_0014-44.png",
-            "image_68": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-68.png",
-            "image_88": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-88.png",
-            "image_102": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-102.png",
-            "image_132": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-132.png",
-            "image_230": "https:\/\/a.slack-edge.com\/bfaba\/img\/avatars-teams\/ava_0014-230.png",
-            "image_default": true
-        },
-        "over_storage_limit": false,
-        "plan": "",
-        "avatar_base_url": "https:\/\/ca.slack-edge.com\/",
-        "over_integrations_limit": true
-    },
-    "latest_event_ts": "1485976462.000000",
-    "channels": [
-        {
-            "id": "C407ABS94",
-            "name": "general",
-            "is_channel": true,
-            "created": 1485969592,
-            "creator": "U407ABLLW",
-            "is_archived": false,
-            "is_general": true,
-            "has_pins": false,
-            "is_member": true,
-            "last_read": "1485976236.000019",
-            "latest": {
-                "type": "message",
-                "subtype": "file_comment",
-                "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.",
-                "file": {
-                    "id": "F3ZJQTA66",
-                    "created": 1485975955,
-                    "timestamp": 1485975955,
-                    "name": "1x1.png",
-                    "title": "1x1.png",
-                    "mimetype": "image\/png",
-                    "filetype": "png",
-                    "pretty_type": "PNG",
-                    "user": "U407ABLLW",
-                    "editable": false,
-                    "size": 68,
-                    "mode": "hosted",
-                    "is_external": false,
-                    "external_type": "",
-                    "is_public": true,
-                    "public_url_shared": false,
-                    "display_as_bot": false,
-                    "username": "",
-                    "url_private": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZJQTA66\/1x1.png",
-                    "url_private_download": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZJQTA66\/download\/1x1.png",
-                    "thumb_64": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_64.png",
-                    "thumb_80": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_80.png",
-                    "thumb_360": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_360.png",
-                    "thumb_360_w": 1,
-                    "thumb_360_h": 1,
-                    "thumb_160": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_160.png",
-                    "image_exif_rotation": 1,
-                    "original_w": 1,
-                    "original_h": 1,
-                    "permalink": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZJQTA66\/1x1.png",
-                    "permalink_public": "https:\/\/slack-files.com\/T3YS5EAL9-F3ZJQTA66-5d747593d2",
-                    "channels": [
-                        "C407ABS94"
-                    ],
-                    "groups": [],
-                    "ims": [],
-                    "comments_count": 1
-                },
-                "comment": {
-                    "id": "Fc3ZMDRQLV",
-                    "created": 1485976236,
-                    "timestamp": 1485976236,
-                    "user": "U407ABLLW",
-                    "is_intro": false,
-                    "comment": "first comment! now i edited it.",
-                    "channel": ""
-                },
-                "is_intro": false,
-                "ts": "1485976236.000019"
-            },
-            "unread_count": 0,
-            "unread_count_display": 0,
-            "members": [
-                "U3ZKBBDL5",
-                "U407ABLLW",
-                "U4096CBHC"
-            ],
-            "topic": {
-                "value": "Company-wide announcements and work-based matters",
-                "creator": "",
-                "last_set": 0
-            },
-            "purpose": {
-                "value": "This channel is for team-wide communication and announcements. All team members are in this channel.",
-                "creator": "",
-                "last_set": 0
-            },
-            "previous_names": []
-        },
-        {
-            "id": "C3ZM2GMGU",
-            "name": "made-to-be-archived",
-            "is_channel": true,
-            "created": 1485976107,
-            "creator": "U407ABLLW",
-            "is_archived": false,
-            "is_general": false,
-            "has_pins": false,
-            "is_member": false,
-            "previous_names": []
-        },
-        {
-            "id": "C3ZEQAYN7",
-            "name": "random",
-            "is_channel": true,
-            "created": 1485969592,
-            "creator": "U407ABLLW",
-            "is_archived": false,
-            "is_general": false,
-            "has_pins": false,
-            "is_member": true,
-            "last_read": "1485969592.000002",
-            "latest": {
-                "user": "U4096CBHC",
-                "text": "<@U4096CBHC|charles> has joined the channel",
-                "type": "message",
-                "subtype": "channel_join",
-                "ts": "1485975606.000004"
-            },
-            "unread_count": 2,
-            "unread_count_display": 0,
-            "members": [
-                "U3ZKBBDL5",
-                "U407ABLLW",
-                "U4096CBHC"
-            ],
-            "topic": {
-                "value": "Non-work banter and water cooler conversation",
-                "creator": "",
-                "last_set": 0
-            },
-            "purpose": {
-                "value": "A place for non-work-related flimflam, faffing, hodge-podge or jibber-jabber you'd prefer to keep out of more focused work-related channels.",
-                "creator": "",
-                "last_set": 0
-            },
-            "previous_names": []
-        },
-        {
-            "id": "C3ZM8JTD3",
-            "name": "some-channel2-renamed",
-            "is_channel": true,
-            "created": 1485975747,
-            "creator": "U407ABLLW",
-            "is_archived": false,
-            "is_general": false,
-            "has_pins": false,
-            "is_member": false,
-            "previous_names": [
-                "some-channel2"
-            ]
-        }
-    ],
-    "groups": [
-        {
-            "id": "G3ZGMF4RZ",
-            "name": "mpdm-bob--alice--charles-1",
-            "is_group": true,
-            "created": 1485975690,
-            "creator": "U407ABLLW",
-            "is_archived": false,
-            "is_mpim": true,
-            "has_pins": false,
-            "is_open": true,
-            "last_read": "0000000000.000000",
-            "latest": {
-                "type": "message",
-                "subtype": "file_share",
-                "text": "<@U407ABLLW|alice> uploaded a file: ",
-                "file": {
-                    "id": "F3ZLY6K5J",
-                    "created": 1485975715,
-                    "timestamp": 1485975715,
-                    "name": "-.txt",
-                    "title": "Untitled",
-                    "mimetype": "text\/plain",
-                    "filetype": "text",
-                    "pretty_type": "Plain Text",
-                    "user": "U407ABLLW",
-                    "editable": true,
-                    "size": 14,
-                    "mode": "snippet",
-                    "is_external": false,
-                    "external_type": "",
-                    "is_public": false,
-                    "public_url_shared": false,
-                    "display_as_bot": false,
-                    "username": "",
-                    "url_private": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZLY6K5J\/-.txt",
-                    "url_private_download": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZLY6K5J\/download\/-.txt",
-                    "permalink": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZLY6K5J\/-.txt",
-                    "permalink_public": "https:\/\/slack-files.com\/T3YS5EAL9-F3ZLY6K5J-39c2c4f739",
-                    "edit_link": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZLY6K5J\/-.txt\/edit",
-                    "preview": "some code here",
-                    "preview_highlight": "
\n
\n
some code here<\/pre><\/div>\n<\/div>\n<\/div>\n",
-                    "lines": 1,
-                    "lines_more": 0,
-                    "preview_is_truncated": false,
-                    "channels": [],
-                    "groups": [
-                        "G3ZGMF4RZ"
-                    ],
-                    "ims": [],
-                    "comments_count": 0
-                },
-                "user": "U407ABLLW",
-                "upload": true,
-                "display_as_bot": false,
-                "username": "<@U407ABLLW|alice>",
-                "bot_id": null,
-                "ts": "1485975715.000005"
-            },
-            "unread_count": 1,
-            "unread_count_display": 1,
-            "members": [
-                "U407ABLLW",
-                "U3ZKBBDL5",
-                "U4096CBHC"
-            ],
-            "topic": {
-                "value": "Group messaging",
-                "creator": "U407ABLLW",
-                "last_set": 1485975690
-            },
-            "purpose": {
-                "value": "Group messaging with: @bob @alice @charles",
-                "creator": "U407ABLLW",
-                "last_set": 1485975690
-            }
-        },
-        {
-            "id": "G3ZJKP7GA",
-            "name": "some-private-channel",
-            "is_group": true,
-            "created": 1485975492,
-            "creator": "U407ABLLW",
-            "is_archived": false,
-            "is_mpim": false,
-            "has_pins": false,
-            "is_open": true,
-            "last_read": "1485975492.000002",
-            "latest": {
-                "user": "U407ABLLW",
-                "purpose": "seekret",
-                "text": "<@U407ABLLW|alice> set the channel's purpose: seekret",
-                "type": "message",
-                "subtype": "group_purpose",
-                "ts": "1485975492.000004"
-            },
-            "unread_count": 2,
-            "unread_count_display": 1,
-            "members": [
-                "U3ZKBBDL5",
-                "U407ABLLW"
-            ],
-            "topic": {
-                "value": "",
-                "creator": "",
-                "last_set": 0
-            },
-            "purpose": {
-                "value": "seekret",
-                "creator": "U407ABLLW",
-                "last_set": 1485975493
-            }
-        },
-        {
-            "id": "G409GKN9M",
-            "name": "some-channel",
-            "is_group": true,
-            "created": 1485975458,
-            "creator": "U407ABLLW",
-            "is_archived": true,
-            "is_mpim": false,
-            "has_pins": false,
-            "is_open": false,
-            "last_read": "1485975476.876631",
-            "latest": {
-                "user": "U407ABLLW",
-                "text": "<@U407ABLLW|alice> archived the private channel",
-                "type": "message",
-                "subtype": "group_archive",
-                "ts": "1485975735.000006"
-            },
-            "unread_count": 1,
-            "unread_count_display": 1,
-            "members": [
-                "U3ZKBBDL5",
-                "U407ABLLW"
-            ],
-            "topic": {
-                "value": "",
-                "creator": "U407ABLLW",
-                "last_set": 1485975476
-            },
-            "purpose": {
-                "value": "who knows?",
-                "creator": "U407ABLLW",
-                "last_set": 1485975476
-            }
-        }
-    ],
-    "ims": [
-        {
-            "id": "D3ZK1D8JY",
-            "created": 1485969645,
-            "is_im": true,
-            "is_org_shared": false,
-            "user": "USLACKBOT",
-            "has_pins": false,
-            "last_read": "1485969667.000002",
-            "latest": {
-                "type": "message",
-                "user": "USLACKBOT",
-                "text": "<@U407ABLLW|alice> archived the private channel ",
-                "ts": "1485975735.000002"
-            },
-            "unread_count": 1,
-            "unread_count_display": 1,
-            "is_open": true
-        },
-        {
-            "id": "D3ZEQULFM",
-            "created": 1485969645,
-            "is_im": true,
-            "is_org_shared": false,
-            "user": "U3ZKBBDL5",
-            "has_pins": false,
-            "last_read": "0000000000.000000",
-            "latest": null,
-            "unread_count": 0,
-            "unread_count_display": 0,
-            "is_open": true
-        },
-        {
-            "id": "D3ZEQULHZ",
-            "created": 1485969645,
-            "is_im": true,
-            "is_org_shared": false,
-            "user": "U407ABLLW",
-            "has_pins": false,
-            "last_read": "0000000000.000000",
-            "latest": {
-                "type": "message",
-                "user": "U407ABLLW",
-                "text": "hi bob",
-                "ts": "1485975421.000002"
-            },
-            "unread_count": 1,
-            "unread_count_display": 1,
-            "is_open": true
-        },
-        {
-            "id": "D409J34CF",
-            "created": 1485975606,
-            "is_im": true,
-            "is_org_shared": false,
-            "user": "U4096CBHC",
-            "has_pins": false,
-            "last_read": "0000000000.000000",
-            "latest": null,
-            "unread_count": 0,
-            "unread_count_display": 0,
-            "is_open": true
-        }
-    ],
-    "cache_ts": 1485977062,
-    "read_only_channels": [],
-    "can_manage_shared_channels": false,
-    "subteams": {
-        "self": [],
-        "all": []
-    },
-    "dnd": {
-        "dnd_enabled": true,
-        "dnd_debug": {
-            "next_dnd_start_date": "Wed, 01 Feb 2017 22:00:00 -0800",
-            "next_dnd_end_date": "Thu, 02 Feb 2017 08:00:00 -0800",
-            "now": "Wed, 01 Feb 2017 11:24:22 -0800",
-            "type": "slack"
-        },
-        "next_dnd_start_ts": 1486015200,
-        "next_dnd_end_ts": 1486051200,
-        "snooze_enabled": false
-    },
-    "users": [
-        {
-            "id": "U407ABLLW",
-            "team_id": "T3YS5EAL9",
-            "name": "alice",
-            "deleted": false,
-            "status": null,
-            "color": "9f69e7",
-            "real_name": "First Testuser",
-            "tz": "America\/Los_Angeles",
-            "tz_label": "Pacific Standard Time",
-            "tz_offset": -28800,
-            "profile": {
-                "first_name": "First",
-                "last_name": "Testuser",
-                "avatar_hash": "gfd0ce7a168d",
-                "real_name": "First Testuser",
-                "real_name_normalized": "First Testuser",
-                "email": "redacted1@gmail.com",
-                "image_24": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-24.png",
-                "image_32": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-32.png",
-                "image_48": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-48.png",
-                "image_72": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png",
-                "image_192": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png",
-                "image_512": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-512.png",
-                "fields": null
-            },
-            "is_admin": true,
-            "is_owner": true,
-            "is_primary_owner": true,
-            "is_restricted": false,
-            "is_ultra_restricted": false,
-            "is_bot": false,
-            "presence": "away"
-        },
-        {
-            "id": "U3ZKBBDL5",
-            "team_id": "T3YS5EAL9",
-            "name": "bob",
-            "deleted": false,
-            "status": null,
-            "color": "4bbe2e",
-            "real_name": "Second Testuser",
-            "tz": "America\/Los_Angeles",
-            "tz_label": "Pacific Standard Time",
-            "tz_offset": -28800,
-            "profile": {
-                "first_name": "Second",
-                "last_name": "Testuser",
-                "avatar_hash": "g6f7a5bf7eb7",
-                "real_name": "Second Testuser",
-                "real_name_normalized": "Second Testuser",
-                "email": "redacted2@gmail.com",
-                "image_24": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-24.png",
-                "image_32": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-32.png",
-                "image_48": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-48.png",
-                "image_72": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png",
-                "image_192": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0019-192.png",
-                "image_512": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0019-512.png",
-                "fields": null
-            },
-            "is_admin": false,
-            "is_owner": false,
-            "is_primary_owner": false,
-            "is_restricted": false,
-            "is_ultra_restricted": false,
-            "is_bot": false,
-            "has_2fa": false,
-            "presence": "active"
-        },
-        {
-            "id": "U4096CBHC",
-            "team_id": "T3YS5EAL9",
-            "name": "charles",
-            "deleted": false,
-            "status": null,
-            "color": "e7392d",
-            "real_name": "Charles Testuser",
-            "tz": "America\/Los_Angeles",
-            "tz_label": "Pacific Standard Time",
-            "tz_offset": -28800,
-            "profile": {
-                "first_name": "Charles",
-                "last_name": "Testuser",
-                "avatar_hash": "5af404f7d4b7",
-                "fields": [],
-                "image_24": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
-                "image_32": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
-                "image_48": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
-                "image_72": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
-                "image_192": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
-                "image_512": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
-                "image_1024": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
-                "image_original": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_original.png",
-                "real_name": "Charles Testuser",
-                "real_name_normalized": "Charles Testuser",
-                "email": "redacted3@gmail.com"
-            },
-            "is_admin": false,
-            "is_owner": false,
-            "is_primary_owner": false,
-            "is_restricted": false,
-            "is_ultra_restricted": false,
-            "is_bot": false,
-            "presence": "away"
-        },
-        {
-            "id": "USLACKBOT",
-            "team_id": "T3YS5EAL9",
-            "name": "slackbot",
-            "deleted": false,
-            "status": null,
-            "color": "757575",
-            "real_name": "slackbot",
-            "tz": null,
-            "tz_label": "Pacific Standard Time",
-            "tz_offset": -28800,
-            "profile": {
-                "first_name": "slackbot",
-                "last_name": "",
-                "image_24": "https:\/\/a.slack-edge.com\/0180\/img\/slackbot_24.png",
-                "image_32": "https:\/\/a.slack-edge.com\/2fac\/plugins\/slackbot\/assets\/service_32.png",
-                "image_48": "https:\/\/a.slack-edge.com\/2fac\/plugins\/slackbot\/assets\/service_48.png",
-                "image_72": "https:\/\/a.slack-edge.com\/0180\/img\/slackbot_72.png",
-                "image_192": "https:\/\/a.slack-edge.com\/66f9\/img\/slackbot_192.png",
-                "image_512": "https:\/\/a.slack-edge.com\/1801\/img\/slackbot_512.png",
-                "avatar_hash": "sv1444671949",
-                "real_name": "slackbot",
-                "real_name_normalized": "slackbot",
-                "fields": null
-            },
-            "is_admin": false,
-            "is_owner": false,
-            "is_primary_owner": false,
-            "is_restricted": false,
-            "is_ultra_restricted": false,
-            "is_bot": false,
-            "presence": "active"
-        }
-    ],
-    "cache_version": "v15-koala",
-    "cache_ts_version": "v1-cat",
-    "bots": [
-        {
-            "id": "B3YTBU6L8",
-            "deleted": false,
-            "name": "incoming-webhook",
-            "app_id": "A0F7XDUAZ",
-            "icons": {
-                "image_36": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_36.png",
-                "image_48": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_48.png",
-                "image_72": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_72.png"
-            }
-        },
-        {
-            "id": "B3ZESMZKM",
-            "deleted": false,
-            "name": "Slack API Tester",
-            "app_id": "A02",
-            "icons": {
-                "image_36": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_36.png",
-                "image_48": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_48.png",
-                "image_72": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_72.png"
-            }
-        },
-        {
-            "id": "B407MVCA3",
-            "deleted": false,
-            "name": "gdrive",
-            "app_id": "A0F7YS32P",
-            "icons": {
-                "image_36": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_36.png",
-                "image_48": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_48.png",
-                "image_72": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_72.png"
-            }
-        }
-    ],
-    "url": "wss:\/\/mpmulti-9ry9.slack-msgs.com\/websocket\/U4n-6Yw8mpc9C64E74gYXGbojir25QCCcLJ9eK57KDURr0o5so6x4qZU-pKn-LX0bYt-ZmIaFcN4TxoKPry1eRtOwiCNSyGyIGxnYrUtrsvMvkFr5C2-osZVL85WqzIWRoE00sBma3U8BkKfi8oUmMoYGKFFVeJ2WPI_ygHRPOo="
-}
diff --git a/_pytest/data/http/rtm.start.json b/_pytest/data/http/rtm.start.json
new file mode 100644
index 0000000..d9cc464
--- /dev/null
+++ b/_pytest/data/http/rtm.start.json
@@ -0,0 +1,861 @@
+{
+    "ok": true,
+    "self": {
+        "id": "U3ZKBBDL5",
+        "name": "bob",
+        "prefs": {
+            "highlight_words": "",
+            "user_colors": "",
+            "color_names_in_list": true,
+            "growls_enabled": true,
+            "tz": "America\/Los_Angeles",
+            "push_dm_alert": true,
+            "push_mention_alert": true,
+            "push_everything": true,
+            "push_show_preview": true,
+            "push_idle_wait": 2,
+            "push_sound": "b2.mp3",
+            "push_loud_channels": "",
+            "push_mention_channels": "",
+            "push_loud_channels_set": "",
+            "threads_everything": false,
+            "email_alerts": "instant",
+            "email_alerts_sleep_until": 0,
+            "email_misc": false,
+            "email_weekly": true,
+            "welcome_message_hidden": false,
+            "all_channels_loud": true,
+            "loud_channels": "",
+            "never_channels": "",
+            "loud_channels_set": "",
+            "search_sort": "timestamp",
+            "expand_inline_imgs": true,
+            "expand_internal_inline_imgs": true,
+            "expand_snippets": false,
+            "posts_formatting_guide": true,
+            "seen_welcome_2": true,
+            "seen_ssb_prompt": false,
+            "spaces_new_xp_banner_dismissed": false,
+            "search_only_my_channels": false,
+            "search_only_current_team": false,
+            "emoji_mode": "default",
+            "emoji_use": "",
+            "has_invited": false,
+            "has_uploaded": false,
+            "has_created_channel": false,
+            "has_searched": false,
+            "search_exclude_channels": "",
+            "messages_theme": "default",
+            "webapp_spellcheck": true,
+            "no_joined_overlays": false,
+            "no_created_overlays": false,
+            "dropbox_enabled": false,
+            "seen_domain_invite_reminder": false,
+            "seen_member_invite_reminder": false,
+            "mute_sounds": false,
+            "arrow_history": false,
+            "tab_ui_return_selects": true,
+            "obey_inline_img_limit": true,
+            "new_msg_snd": "knock_brush.mp3",
+            "require_at": false,
+            "ssb_space_window": "",
+            "mac_ssb_bounce": "",
+            "mac_ssb_bullet": true,
+            "expand_non_media_attachments": true,
+            "show_typing": true,
+            "pagekeys_handled": true,
+            "last_snippet_type": "",
+            "display_real_names_override": 0,
+            "display_preferred_names": true,
+            "time24": false,
+            "enter_is_special_in_tbt": false,
+            "graphic_emoticons": false,
+            "convert_emoticons": true,
+            "ss_emojis": true,
+            "sidebar_behavior": "",
+            "seen_onboarding_start": false,
+            "onboarding_cancelled": true,
+            "seen_onboarding_slackbot_conversation": false,
+            "seen_onboarding_channels": false,
+            "seen_onboarding_direct_messages": false,
+            "seen_onboarding_invites": false,
+            "seen_onboarding_search": false,
+            "seen_onboarding_recent_mentions": false,
+            "seen_onboarding_starred_items": false,
+            "seen_onboarding_private_groups": false,
+            "onboarding_slackbot_conversation_step": 0,
+            "dnd_enabled": true,
+            "dnd_start_hour": "22:00",
+            "dnd_end_hour": "08:00",
+            "mark_msgs_read_immediately": true,
+            "start_scroll_at_oldest": true,
+            "snippet_editor_wrap_long_lines": false,
+            "ls_disabled": false,
+            "sidebar_theme": "default",
+            "sidebar_theme_custom_values": "",
+            "f_key_search": false,
+            "k_key_omnibox": true,
+            "speak_growls": false,
+            "mac_speak_voice": "com.apple.speech.synthesis.voice.Alex",
+            "mac_speak_speed": 250,
+            "at_channel_suppressed_channels": "",
+            "push_at_channel_suppressed_channels": "",
+            "prompted_for_email_disabling": false,
+            "full_text_extracts": false,
+            "no_text_in_notifications": false,
+            "muted_channels": "",
+            "no_macelectron_banner": false,
+            "no_macssb1_banner": false,
+            "no_macssb2_banner": false,
+            "no_winssb1_banner": false,
+            "no_invites_widget_in_sidebar": false,
+            "no_omnibox_in_channels": false,
+            "k_key_omnibox_auto_hide_count": 0,
+            "prev_next_btn": false,
+            "hide_user_group_info_pane": false,
+            "mentions_exclude_at_user_groups": false,
+            "privacy_policy_seen": true,
+            "enterprise_migration_seen": true,
+            "last_tos_acknowledged": "tos_oct2016",
+            "search_exclude_bots": false,
+            "load_lato_2": false,
+            "fuller_timestamps": false,
+            "last_seen_at_channel_warning": 0,
+            "msg_preview": false,
+            "msg_preview_persistent": true,
+            "emoji_autocomplete_big": false,
+            "winssb_run_from_tray": true,
+            "winssb_window_flash_behavior": "idle",
+            "two_factor_auth_enabled": false,
+            "two_factor_type": null,
+            "two_factor_backup_type": null,
+            "hide_hex_swatch": false,
+            "client_logs_pri": "",
+            "enhanced_debugging": false,
+            "thin_channel_membership_fe": false,
+            "flannel_server_pool": "random",
+            "mentions_exclude_at_channels": true,
+            "confirm_clear_all_unreads": true,
+            "confirm_user_marked_away": true,
+            "box_enabled": false,
+            "seen_single_emoji_msg": false,
+            "confirm_sh_call_start": true,
+            "preferred_skin_tone": "",
+            "show_all_skin_tones": false,
+            "separate_private_channels": false,
+            "whats_new_read": 1485969645,
+            "hotness": false,
+            "frecency_jumper": "",
+            "frecency_ent_jumper": "",
+            "jumbomoji": true,
+            "newxp_seen_last_message": "1",
+            "attachments_with_borders": false,
+            "show_memory_instrument": false,
+            "enable_unread_view": false,
+            "seen_unread_view_coachmark": false,
+            "seen_calls_video_beta_coachmark": false,
+            "seen_calls_video_ga_coachmark": false,
+            "seen_calls_ss_window_coachmark": false,
+            "measure_css_usage": false,
+            "enable_react_emoji_picker": true,
+            "seen_replies_coachmark": false,
+            "seen_custom_status_badge": false,
+            "all_unreads_sort_order": "alphabetical",
+            "locale": "en_US",
+            "gdrive_authed": true,
+            "gdrive_enabled": true,
+            "seen_gdrive_coachmark": false,
+            "channel_sort": "{\"is_custom_sorted\":false, \"priority_display\":false, \"priority_type\":\"\", \"sorts\":[]}",
+            "overloaded_message_enabled": true,
+            "seen_highlights_coachmark": false,
+            "seen_highlights_arrows_coachmark": false,
+            "a11y_font_size": "normal",
+            "a11y_animations": true,
+            "intro_to_apps_message_seen": false
+        },
+        "created": 1485969645,
+        "manual_presence": "active"
+    },
+    "team": {
+        "id": "T3YS5EAL9",
+        "name": "weeslacktest",
+        "email_domain": "",
+        "domain": "weeslacktest",
+        "msg_edit_window_mins": -1,
+        "prefs": {
+            "invites_only_admins": false,
+            "default_channels": [
+                "C407ABS94",
+                "C3ZEQAYN7"
+            ],
+            "display_email_addresses": false,
+            "hide_referers": true,
+            "msg_edit_window_mins": -1,
+            "allow_message_deletion": true,
+            "calling_app_name": "Slack",
+            "allow_calls": true,
+            "display_real_names": false,
+            "who_can_at_everyone": "regular",
+            "who_can_at_channel": "ra",
+            "who_can_create_channels": "regular",
+            "who_can_archive_channels": "regular",
+            "who_can_create_groups": "ra",
+            "who_can_post_general": "ra",
+            "who_can_kick_channels": "admin",
+            "who_can_kick_groups": "regular",
+            "retention_type": 0,
+            "retention_duration": 0,
+            "group_retention_type": 0,
+            "group_retention_duration": 0,
+            "dm_retention_type": 0,
+            "dm_retention_duration": 0,
+            "file_retention_duration": 0,
+            "file_retention_type": 0,
+            "allow_retention_override": true,
+            "require_at_for_mention": false,
+            "default_rxns": [
+                "simple_smile",
+                "thumbsup",
+                "white_check_mark",
+                "heart",
+                "eyes"
+            ],
+            "team_handy_rxns": {
+                "restrict": false,
+                "list": [
+                    {
+                        "name": "slightly_smiling_face",
+                        "title": "I'm happy!"
+                    },
+                    {
+                        "name": "+1",
+                        "title": "good!"
+                    },
+                    {
+                        "name": "white_check_mark",
+                        "title": "done!"
+                    },
+                    {
+                        "name": "heart",
+                        "title": "love it!"
+                    },
+                    {
+                        "name": "eyes",
+                        "title": "looking..."
+                    }
+                ]
+            },
+            "channel_handy_rxns": null,
+            "compliance_export_start": 0,
+            "warn_before_at_channel": "always",
+            "disallow_public_file_urls": false,
+            "who_can_create_delete_user_groups": "admin",
+            "who_can_edit_user_groups": "admin",
+            "who_can_change_team_profile": "admin",
+            "allow_shared_channels": false,
+            "who_has_team_visibility": "ra",
+            "disable_file_uploads": "allow_all",
+            "disable_file_editing": false,
+            "disable_file_deleting": false,
+            "who_can_create_shared_channels": "admin",
+            "who_can_manage_shared_channels": {
+                "type": [
+                    "admin"
+                ]
+            },
+            "who_can_post_in_shared_channels": {
+                "type": [
+                    "regular",
+                    "ra"
+                ]
+            },
+            "allow_shared_channel_perms_override": false,
+            "gdrive_enabled_team": true,
+            "dnd_enabled": true,
+            "dnd_start_hour": "22:00",
+            "dnd_end_hour": "08:00",
+            "auth_mode": "normal",
+            "who_can_manage_integrations": {
+                "type": [
+                    "regular"
+                ]
+            },
+            "invites_limit": true
+        },
+        "icon": {
+            "image_34": "https:\/\/a.slack-edge.com\/0180\/img\/avatars-teams\/ava_0014-34.png",
+            "image_44": "https:\/\/a.slack-edge.com\/0180\/img\/avatars-teams\/ava_0014-44.png",
+            "image_68": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-68.png",
+            "image_88": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-88.png",
+            "image_102": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-102.png",
+            "image_132": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-132.png",
+            "image_230": "https:\/\/a.slack-edge.com\/bfaba\/img\/avatars-teams\/ava_0014-230.png",
+            "image_default": true
+        },
+        "over_storage_limit": false,
+        "plan": "",
+        "avatar_base_url": "https:\/\/ca.slack-edge.com\/",
+        "over_integrations_limit": true
+    },
+    "latest_event_ts": "1485976462.000000",
+    "channels": [
+        {
+            "id": "C407ABS94",
+            "name": "general",
+            "is_channel": true,
+            "created": 1485969592,
+            "creator": "U407ABLLW",
+            "is_archived": false,
+            "is_general": true,
+            "has_pins": false,
+            "is_member": true,
+            "last_read": "1485976236.000019",
+            "latest": {
+                "type": "message",
+                "subtype": "file_comment",
+                "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.",
+                "file": {
+                    "id": "F3ZJQTA66",
+                    "created": 1485975955,
+                    "timestamp": 1485975955,
+                    "name": "1x1.png",
+                    "title": "1x1.png",
+                    "mimetype": "image\/png",
+                    "filetype": "png",
+                    "pretty_type": "PNG",
+                    "user": "U407ABLLW",
+                    "editable": false,
+                    "size": 68,
+                    "mode": "hosted",
+                    "is_external": false,
+                    "external_type": "",
+                    "is_public": true,
+                    "public_url_shared": false,
+                    "display_as_bot": false,
+                    "username": "",
+                    "url_private": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZJQTA66\/1x1.png",
+                    "url_private_download": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZJQTA66\/download\/1x1.png",
+                    "thumb_64": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_64.png",
+                    "thumb_80": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_80.png",
+                    "thumb_360": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_360.png",
+                    "thumb_360_w": 1,
+                    "thumb_360_h": 1,
+                    "thumb_160": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_160.png",
+                    "image_exif_rotation": 1,
+                    "original_w": 1,
+                    "original_h": 1,
+                    "permalink": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZJQTA66\/1x1.png",
+                    "permalink_public": "https:\/\/slack-files.com\/T3YS5EAL9-F3ZJQTA66-5d747593d2",
+                    "channels": [
+                        "C407ABS94"
+                    ],
+                    "groups": [],
+                    "ims": [],
+                    "comments_count": 1
+                },
+                "comment": {
+                    "id": "Fc3ZMDRQLV",
+                    "created": 1485976236,
+                    "timestamp": 1485976236,
+                    "user": "U407ABLLW",
+                    "is_intro": false,
+                    "comment": "first comment! now i edited it.",
+                    "channel": ""
+                },
+                "is_intro": false,
+                "ts": "1485976236.000019"
+            },
+            "unread_count": 0,
+            "unread_count_display": 0,
+            "members": [
+                "U3ZKBBDL5",
+                "U407ABLLW",
+                "U4096CBHC"
+            ],
+            "topic": {
+                "value": "Company-wide announcements and work-based matters",
+                "creator": "",
+                "last_set": 0
+            },
+            "purpose": {
+                "value": "This channel is for team-wide communication and announcements. All team members are in this channel.",
+                "creator": "",
+                "last_set": 0
+            },
+            "previous_names": []
+        },
+        {
+            "id": "C3ZM2GMGU",
+            "name": "made-to-be-archived",
+            "is_channel": true,
+            "created": 1485976107,
+            "creator": "U407ABLLW",
+            "is_archived": false,
+            "is_general": false,
+            "has_pins": false,
+            "is_member": false,
+            "previous_names": []
+        },
+        {
+            "id": "C3ZEQAYN7",
+            "name": "random",
+            "is_channel": true,
+            "created": 1485969592,
+            "creator": "U407ABLLW",
+            "is_archived": false,
+            "is_general": false,
+            "has_pins": false,
+            "is_member": true,
+            "last_read": "1485969592.000002",
+            "latest": {
+                "user": "U4096CBHC",
+                "text": "<@U4096CBHC|charles> has joined the channel",
+                "type": "message",
+                "subtype": "channel_join",
+                "ts": "1485975606.000004"
+            },
+            "unread_count": 2,
+            "unread_count_display": 0,
+            "members": [
+                "U3ZKBBDL5",
+                "U407ABLLW",
+                "U4096CBHC"
+            ],
+            "topic": {
+                "value": "Non-work banter and water cooler conversation",
+                "creator": "",
+                "last_set": 0
+            },
+            "purpose": {
+                "value": "A place for non-work-related flimflam, faffing, hodge-podge or jibber-jabber you'd prefer to keep out of more focused work-related channels.",
+                "creator": "",
+                "last_set": 0
+            },
+            "previous_names": []
+        },
+        {
+            "id": "C3ZM8JTD3",
+            "name": "some-channel2-renamed",
+            "is_channel": true,
+            "created": 1485975747,
+            "creator": "U407ABLLW",
+            "is_archived": false,
+            "is_general": false,
+            "has_pins": false,
+            "is_member": false,
+            "previous_names": [
+                "some-channel2"
+            ]
+        }
+    ],
+    "groups": [
+        {
+            "id": "G3ZGMF4RZ",
+            "name": "mpdm-bob--alice--charles-1",
+            "is_group": true,
+            "created": 1485975690,
+            "creator": "U407ABLLW",
+            "is_archived": false,
+            "is_mpim": true,
+            "has_pins": false,
+            "is_open": true,
+            "last_read": "0000000000.000000",
+            "latest": {
+                "type": "message",
+                "subtype": "file_share",
+                "text": "<@U407ABLLW|alice> uploaded a file: ",
+                "file": {
+                    "id": "F3ZLY6K5J",
+                    "created": 1485975715,
+                    "timestamp": 1485975715,
+                    "name": "-.txt",
+                    "title": "Untitled",
+                    "mimetype": "text\/plain",
+                    "filetype": "text",
+                    "pretty_type": "Plain Text",
+                    "user": "U407ABLLW",
+                    "editable": true,
+                    "size": 14,
+                    "mode": "snippet",
+                    "is_external": false,
+                    "external_type": "",
+                    "is_public": false,
+                    "public_url_shared": false,
+                    "display_as_bot": false,
+                    "username": "",
+                    "url_private": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZLY6K5J\/-.txt",
+                    "url_private_download": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZLY6K5J\/download\/-.txt",
+                    "permalink": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZLY6K5J\/-.txt",
+                    "permalink_public": "https:\/\/slack-files.com\/T3YS5EAL9-F3ZLY6K5J-39c2c4f739",
+                    "edit_link": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZLY6K5J\/-.txt\/edit",
+                    "preview": "some code here",
+                    "preview_highlight": "
\n
\n
some code here<\/pre><\/div>\n<\/div>\n<\/div>\n",
+                    "lines": 1,
+                    "lines_more": 0,
+                    "preview_is_truncated": false,
+                    "channels": [],
+                    "groups": [
+                        "G3ZGMF4RZ"
+                    ],
+                    "ims": [],
+                    "comments_count": 0
+                },
+                "user": "U407ABLLW",
+                "upload": true,
+                "display_as_bot": false,
+                "username": "<@U407ABLLW|alice>",
+                "bot_id": null,
+                "ts": "1485975715.000005"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "members": [
+                "U407ABLLW",
+                "U3ZKBBDL5",
+                "U4096CBHC"
+            ],
+            "topic": {
+                "value": "Group messaging",
+                "creator": "U407ABLLW",
+                "last_set": 1485975690
+            },
+            "purpose": {
+                "value": "Group messaging with: @bob @alice @charles",
+                "creator": "U407ABLLW",
+                "last_set": 1485975690
+            }
+        },
+        {
+            "id": "G3ZJKP7GA",
+            "name": "some-private-channel",
+            "is_group": true,
+            "created": 1485975492,
+            "creator": "U407ABLLW",
+            "is_archived": false,
+            "is_mpim": false,
+            "has_pins": false,
+            "is_open": true,
+            "last_read": "1485975492.000002",
+            "latest": {
+                "user": "U407ABLLW",
+                "purpose": "seekret",
+                "text": "<@U407ABLLW|alice> set the channel's purpose: seekret",
+                "type": "message",
+                "subtype": "group_purpose",
+                "ts": "1485975492.000004"
+            },
+            "unread_count": 2,
+            "unread_count_display": 1,
+            "members": [
+                "U3ZKBBDL5",
+                "U407ABLLW"
+            ],
+            "topic": {
+                "value": "",
+                "creator": "",
+                "last_set": 0
+            },
+            "purpose": {
+                "value": "seekret",
+                "creator": "U407ABLLW",
+                "last_set": 1485975493
+            }
+        },
+        {
+            "id": "G409GKN9M",
+            "name": "some-channel",
+            "is_group": true,
+            "created": 1485975458,
+            "creator": "U407ABLLW",
+            "is_archived": true,
+            "is_mpim": false,
+            "has_pins": false,
+            "is_open": false,
+            "last_read": "1485975476.876631",
+            "latest": {
+                "user": "U407ABLLW",
+                "text": "<@U407ABLLW|alice> archived the private channel",
+                "type": "message",
+                "subtype": "group_archive",
+                "ts": "1485975735.000006"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "members": [
+                "U3ZKBBDL5",
+                "U407ABLLW"
+            ],
+            "topic": {
+                "value": "",
+                "creator": "U407ABLLW",
+                "last_set": 1485975476
+            },
+            "purpose": {
+                "value": "who knows?",
+                "creator": "U407ABLLW",
+                "last_set": 1485975476
+            }
+        }
+    ],
+    "ims": [
+        {
+            "id": "D3ZK1D8JY",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "USLACKBOT",
+            "has_pins": false,
+            "last_read": "1485969667.000002",
+            "latest": {
+                "type": "message",
+                "user": "USLACKBOT",
+                "text": "<@U407ABLLW|alice> archived the private channel ",
+                "ts": "1485975735.000002"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "is_open": true
+        },
+        {
+            "id": "D3ZEQULFM",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U3ZKBBDL5",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": null,
+            "unread_count": 0,
+            "unread_count_display": 0,
+            "is_open": true
+        },
+        {
+            "id": "D3ZEQULHZ",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U407ABLLW",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": {
+                "type": "message",
+                "user": "U407ABLLW",
+                "text": "hi bob",
+                "ts": "1485975421.000002"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "is_open": true
+        },
+        {
+            "id": "D409J34CF",
+            "created": 1485975606,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U4096CBHC",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": null,
+            "unread_count": 0,
+            "unread_count_display": 0,
+            "is_open": true
+        }
+    ],
+    "cache_ts": 1485977062,
+    "read_only_channels": [],
+    "can_manage_shared_channels": false,
+    "subteams": {
+        "self": [],
+        "all": []
+    },
+    "dnd": {
+        "dnd_enabled": true,
+        "dnd_debug": {
+            "next_dnd_start_date": "Wed, 01 Feb 2017 22:00:00 -0800",
+            "next_dnd_end_date": "Thu, 02 Feb 2017 08:00:00 -0800",
+            "now": "Wed, 01 Feb 2017 11:24:22 -0800",
+            "type": "slack"
+        },
+        "next_dnd_start_ts": 1486015200,
+        "next_dnd_end_ts": 1486051200,
+        "snooze_enabled": false
+    },
+    "users": [
+        {
+            "id": "U407ABLLW",
+            "team_id": "T3YS5EAL9",
+            "name": "alice",
+            "deleted": false,
+            "status": null,
+            "color": "9f69e7",
+            "real_name": "First Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "First",
+                "last_name": "Testuser",
+                "avatar_hash": "gfd0ce7a168d",
+                "real_name": "First Testuser",
+                "real_name_normalized": "First Testuser",
+                "email": "redacted1@gmail.com",
+                "image_24": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-24.png",
+                "image_32": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-32.png",
+                "image_48": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-48.png",
+                "image_72": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png",
+                "image_192": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png",
+                "image_512": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-512.png",
+                "fields": null
+            },
+            "is_admin": true,
+            "is_owner": true,
+            "is_primary_owner": true,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "away"
+        },
+        {
+            "id": "U3ZKBBDL5",
+            "team_id": "T3YS5EAL9",
+            "name": "bob",
+            "deleted": false,
+            "status": null,
+            "color": "4bbe2e",
+            "real_name": "Second Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "Second",
+                "last_name": "Testuser",
+                "avatar_hash": "g6f7a5bf7eb7",
+                "real_name": "Second Testuser",
+                "real_name_normalized": "Second Testuser",
+                "email": "redacted2@gmail.com",
+                "image_24": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-24.png",
+                "image_32": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-32.png",
+                "image_48": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-48.png",
+                "image_72": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png",
+                "image_192": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0019-192.png",
+                "image_512": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0019-512.png",
+                "fields": null
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "has_2fa": false,
+            "presence": "active"
+        },
+        {
+            "id": "U4096CBHC",
+            "team_id": "T3YS5EAL9",
+            "name": "charles",
+            "deleted": false,
+            "status": null,
+            "color": "e7392d",
+            "real_name": "Charles Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "Charles",
+                "last_name": "Testuser",
+                "avatar_hash": "5af404f7d4b7",
+                "fields": [],
+                "image_24": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_32": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_48": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_72": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_192": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_512": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_1024": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_original": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_original.png",
+                "real_name": "Charles Testuser",
+                "real_name_normalized": "Charles Testuser",
+                "email": "redacted3@gmail.com"
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "away"
+        },
+        {
+            "id": "USLACKBOT",
+            "team_id": "T3YS5EAL9",
+            "name": "slackbot",
+            "deleted": false,
+            "status": null,
+            "color": "757575",
+            "real_name": "slackbot",
+            "tz": null,
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "slackbot",
+                "last_name": "",
+                "image_24": "https:\/\/a.slack-edge.com\/0180\/img\/slackbot_24.png",
+                "image_32": "https:\/\/a.slack-edge.com\/2fac\/plugins\/slackbot\/assets\/service_32.png",
+                "image_48": "https:\/\/a.slack-edge.com\/2fac\/plugins\/slackbot\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/0180\/img\/slackbot_72.png",
+                "image_192": "https:\/\/a.slack-edge.com\/66f9\/img\/slackbot_192.png",
+                "image_512": "https:\/\/a.slack-edge.com\/1801\/img\/slackbot_512.png",
+                "avatar_hash": "sv1444671949",
+                "real_name": "slackbot",
+                "real_name_normalized": "slackbot",
+                "fields": null
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "active"
+        }
+    ],
+    "cache_version": "v15-koala",
+    "cache_ts_version": "v1-cat",
+    "bots": [
+        {
+            "id": "B3YTBU6L8",
+            "deleted": false,
+            "name": "incoming-webhook",
+            "app_id": "A0F7XDUAZ",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_72.png"
+            }
+        },
+        {
+            "id": "B3ZESMZKM",
+            "deleted": false,
+            "name": "Slack API Tester",
+            "app_id": "A02",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_72.png"
+            }
+        },
+        {
+            "id": "B407MVCA3",
+            "deleted": false,
+            "name": "gdrive",
+            "app_id": "A0F7YS32P",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_72.png"
+            }
+        }
+    ],
+    "url": "wss:\/\/mpmulti-9ry9.slack-msgs.com\/websocket\/U4n-6Yw8mpc9C64E74gYXGbojir25QCCcLJ9eK57KDURr0o5so6x4qZU-pKn-LX0bYt-ZmIaFcN4TxoKPry1eRtOwiCNSyGyIGxnYrUtrsvMvkFr5C2-osZVL85WqzIWRoE00sBma3U8BkKfi8oUmMoYGKFFVeJ2WPI_ygHRPOo="
+}
diff --git a/_pytest/data/websocket/1485975367.79-reconnect_url.json b/_pytest/data/websocket/1485975367.79-reconnect_url.json
index 4b53a4e..94cacca 100644
--- a/_pytest/data/websocket/1485975367.79-reconnect_url.json
+++ b/_pytest/data/websocket/1485975367.79-reconnect_url.json
@@ -1 +1 @@
-{u'url': u'wss://mpmulti-gvop.slack-msgs.com/websocket/tamYoLuX4lU-WBS7cFe2RCh8kqeO86F0Mi1RKFVfk7FM-QNu1KD7HiMBStfdLkwkKHmtyTACNE6SONtoQRTbnC0q9fAoLHPl76Y7y9IhCve6VKs2KNLmRH37WutXTBsj3b9HvF79VySlPgAwVXZeH0lgfDDk_RAY9l_dJ8u-jSs=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'}
\ No newline at end of file
+{"url": "wss://mpmulti-gvop.slack-msgs.com/websocket/tamYoLuX4lU-WBS7cFe2RCh8kqeO86F0Mi1RKFVfk7FM-QNu1KD7HiMBStfdLkwkKHmtyTACNE6SONtoQRTbnC0q9fAoLHPl76Y7y9IhCve6VKs2KNLmRH37WutXTBsj3b9HvF79VySlPgAwVXZeH0lgfDDk_RAY9l_dJ8u-jSs=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"}
diff --git a/_pytest/data/websocket/1485975408.19-user_typing.json b/_pytest/data/websocket/1485975408.19-user_typing.json
index 572b0fd..422e30b 100644
--- a/_pytest/data/websocket/1485975408.19-user_typing.json
+++ b/_pytest/data/websocket/1485975408.19-user_typing.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'D3ZEQULHZ'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "D3ZEQULHZ"}
diff --git a/_pytest/data/websocket/1485975412.74-user_typing.json b/_pytest/data/websocket/1485975412.74-user_typing.json
index 4e53cd4..df57e72 100644
--- a/_pytest/data/websocket/1485975412.74-user_typing.json
+++ b/_pytest/data/websocket/1485975412.74-user_typing.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"}
diff --git a/_pytest/data/websocket/1485975420.36-user_typing.json b/_pytest/data/websocket/1485975420.36-user_typing.json
index 572b0fd..422e30b 100644
--- a/_pytest/data/websocket/1485975420.36-user_typing.json
+++ b/_pytest/data/websocket/1485975420.36-user_typing.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'D3ZEQULHZ'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "D3ZEQULHZ"}
diff --git a/_pytest/data/websocket/1485975421.33-message.json b/_pytest/data/websocket/1485975421.33-message.json
index 5dd2924..9a85771 100644
--- a/_pytest/data/websocket/1485975421.33-message.json
+++ b/_pytest/data/websocket/1485975421.33-message.json
@@ -1 +1 @@
-{u'text': u'hi bob', u'ts': u'1485975421.000002', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'D3ZEQULHZ'}
\ No newline at end of file
+{"text": "hi bob", "ts": "1485975421.000002", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "D3ZEQULHZ"}
diff --git a/_pytest/data/websocket/1485975421.42-desktop_notification.json b/_pytest/data/websocket/1485975421.42-desktop_notification.json
index fedda07..ee6739a 100644
--- a/_pytest/data/websocket/1485975421.42-desktop_notification.json
+++ b/_pytest/data/websocket/1485975421.42-desktop_notification.json
@@ -1 +1 @@
-{u'launchUri': u'slack://channel?id=D3ZEQULHZ&message=1485975421000002&team=T3YS5EAL9', u'subtitle': u'alice', u'is_shared': False, u'title': u'weeslacktest', u'ssbFilename': u'knock_brush.mp3', u'avatarImage': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png', u'imageUri': None, u'content': u'hi bob', u'event_ts': u'1485975421.875655', u'msg': u'1485975421.000002', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'desktop_notification', u'channel': u'D3ZEQULHZ'}
\ No newline at end of file
+{"avatarImage": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png", "subtitle": "alice", "is_shared": false, "title": "weeslacktest", "ssbFilename": "knock_brush.mp3", "imageUri": null, "launchUri": "slack://channel?id=D3ZEQULHZ&message=1485975421000002&team=T3YS5EAL9", "event_ts": "1485975421.875655", "msg": "1485975421.000002", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "content": "hi bob", "type": "desktop_notification", "channel": "D3ZEQULHZ"}
diff --git a/_pytest/data/websocket/1485975426.55-reaction_added.json b/_pytest/data/websocket/1485975426.55-reaction_added.json
index 2a1347c..b51cfdb 100644
--- a/_pytest/data/websocket/1485975426.55-reaction_added.json
+++ b/_pytest/data/websocket/1485975426.55-reaction_added.json
@@ -1 +1 @@
-{u'reaction': u'grinning', u'event_ts': u'1485975426.875724', u'ts': u'1485975426.000003', u'item': {u'type': u'message', u'ts': u'1485975421.000002', u'channel': u'D3ZEQULHZ'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_added'}
\ No newline at end of file
+{"reaction": "grinning", "event_ts": "1485975426.875724", "ts": "1485975426.000003", "item": {"type": "message", "ts": "1485975421.000002", "channel": "D3ZEQULHZ"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_added"}
diff --git a/_pytest/data/websocket/1485975428.76-reaction_removed.json b/_pytest/data/websocket/1485975428.76-reaction_removed.json
index efa1605..b3b90ac 100644
--- a/_pytest/data/websocket/1485975428.76-reaction_removed.json
+++ b/_pytest/data/websocket/1485975428.76-reaction_removed.json
@@ -1 +1 @@
-{u'reaction': u'grinning', u'event_ts': u'1485975428.875759', u'ts': u'1485975428.000004', u'item': {u'type': u'message', u'ts': u'1485975421.000002', u'channel': u'D3ZEQULHZ'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_removed'}
\ No newline at end of file
+{"reaction": "grinning", "event_ts": "1485975428.875759", "ts": "1485975428.000004", "item": {"type": "message", "ts": "1485975421.000002", "channel": "D3ZEQULHZ"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_removed"}
diff --git a/_pytest/data/websocket/1485975458.35-channel_created.json b/_pytest/data/websocket/1485975458.35-channel_created.json
index bd197df..ca225d8 100644
--- a/_pytest/data/websocket/1485975458.35-channel_created.json
+++ b/_pytest/data/websocket/1485975458.35-channel_created.json
@@ -1 +1 @@
-{u'event_ts': u'1485975458.876318', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_created', u'channel': {u'is_shared': False, u'is_channel': True, u'created': 1485975458, u'creator': u'U407ABLLW', u'is_org_shared': False, u'id': u'C3ZJKCGTU', u'name': u'some-channel'}}
\ No newline at end of file
+{"event_ts": "1485975458.876318", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_created", "channel": {"is_shared": false, "is_channel": true, "creator": "U407ABLLW", "created": 1485975458, "is_org_shared": false, "id": "C3ZJKCGTU", "name": "some-channel"}}
diff --git a/_pytest/data/websocket/1485975458.87-channel_joined.json b/_pytest/data/websocket/1485975458.87-channel_joined.json
index fffa5c5..8991446 100644
--- a/_pytest/data/websocket/1485975458.87-channel_joined.json
+++ b/_pytest/data/websocket/1485975458.87-channel_joined.json
@@ -1 +1 @@
-{u'event_ts': u'1485975458.876336', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_joined', u'channel': {u'topic': {u'last_set': 0, u'value': u'', u'creator': u''}, u'is_general': False, u'name': u'some-channel', u'is_channel': True, u'created': 1485975458, u'is_member': True, u'is_archived': False, u'creator': u'U407ABLLW', u'members': [u'U3ZKBBDL5', u'U407ABLLW'], u'unread_count': 0, u'previous_names': [], u'purpose': {u'last_set': 1485975458, u'value': u'who knows?', u'creator': u'U407ABLLW'}, u'unread_count_display': 0, u'last_read': u'1485975458.000003', u'id': u'C3ZJKCGTU', u'latest': {u'text': u'<@U407ABLLW|alice> set the channel purpose: who knows?', u'ts': u'1485975458.000003', u'subtype': u'channel_purpose', u'user': u'U407ABLLW', u'type': u'message', u'purpose': u'who knows?'}}}
\ No newline at end of file
+{"event_ts": "1485975458.876336", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_joined", "channel": {"previous_names": [], "is_general": false, "name": "some-channel", "is_channel": true, "created": 1485975458, "is_member": true, "unread_count_display": 0, "is_archived": false, "creator": "U407ABLLW", "topic": {"last_set": 0, "value": "", "creator": ""}, "unread_count": 0, "purpose": {"last_set": 1485975458, "value": "who knows?", "creator": "U407ABLLW"}, "members": ["U3ZKBBDL5", "U407ABLLW"], "last_read": "1485975458.000003", "id": "C3ZJKCGTU", "latest": {"text": "<@U407ABLLW|alice> set the channel purpose: who knows?", "ts": "1485975458.000003", "subtype": "channel_purpose", "purpose": "who knows?", "type": "message", "user": "U407ABLLW"}}}
diff --git a/_pytest/data/websocket/1485975458.87-message.json b/_pytest/data/websocket/1485975458.87-message.json
index 80e961f..63f611c 100644
--- a/_pytest/data/websocket/1485975458.87-message.json
+++ b/_pytest/data/websocket/1485975458.87-message.json
@@ -1 +1 @@
-{u'user_profile': {u'avatar_hash': u'g6f7a5bf7eb7', u'first_name': u'Second', u'real_name': u'Second Testuser', u'name': u'bob', u'image_72': u'https://secure.gravatar.com/avatar/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png'}, u'inviter': u'U407ABLLW', u'ts': u'1485975458.000004', u'subtype': u'channel_join', u'text': u'<@U3ZKBBDL5|bob> has joined the channel', u'user': u'U3ZKBBDL5', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C3ZJKCGTU'}
\ No newline at end of file
+{"type": "message", "user_profile": {"avatar_hash": "g6f7a5bf7eb7", "first_name": "Second", "image_72": "https://secure.gravatar.com/avatar/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png", "name": "bob", "real_name": "Second Testuser"}, "text": "<@U3ZKBBDL5|bob> has joined the channel", "ts": "1485975458.000004", "subtype": "channel_join", "user": "U3ZKBBDL5", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "inviter": "U407ABLLW", "channel": "C3ZJKCGTU"}
diff --git a/_pytest/data/websocket/1485975462.62-update_thread_state.json b/_pytest/data/websocket/1485975462.62-update_thread_state.json
index 6a74d95..7a5411a 100644
--- a/_pytest/data/websocket/1485975462.62-update_thread_state.json
+++ b/_pytest/data/websocket/1485975462.62-update_thread_state.json
@@ -1 +1 @@
-{u'mention_count': 0, u'event_ts': u'1485975458.876340', u'timestamp': u'1485975458.741354', u'has_unreads': False, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'update_thread_state'}
\ No newline at end of file
+{"mention_count": 0, "event_ts": "1485975458.876340", "timestamp": "1485975458.741354", "has_unreads": false, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "update_thread_state"}
diff --git a/_pytest/data/websocket/1485975462.62-user_typing.json b/_pytest/data/websocket/1485975462.62-user_typing.json
index 5169af3..ec19e94 100644
--- a/_pytest/data/websocket/1485975462.62-user_typing.json
+++ b/_pytest/data/websocket/1485975462.62-user_typing.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C3ZJKCGTU'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C3ZJKCGTU"}
diff --git a/_pytest/data/websocket/1485975476.62-channel_deleted.json b/_pytest/data/websocket/1485975476.62-channel_deleted.json
index fe1ad9e..8dd1cb8 100644
--- a/_pytest/data/websocket/1485975476.62-channel_deleted.json
+++ b/_pytest/data/websocket/1485975476.62-channel_deleted.json
@@ -1 +1 @@
-{u'event_ts': u'1485975476.876638', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_deleted', u'channel': u'C3ZJKCGTU'}
\ No newline at end of file
+{"event_ts": "1485975476.876638", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_deleted", "channel": "C3ZJKCGTU"}
diff --git a/_pytest/data/websocket/1485975476.86-group_join.json b/_pytest/data/websocket/1485975476.86-group_join.json
index fdc6878..dc72eeb 100644
--- a/_pytest/data/websocket/1485975476.86-group_join.json
+++ b/_pytest/data/websocket/1485975476.86-group_join.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_join', u'user': u'U407ABLLW', u'channel': u'G409GKN9M', u'ts': u'1485975476.000002'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_join", "user": "U407ABLLW", "channel": "G409GKN9M", "ts": "1485975476.000002"}
diff --git a/_pytest/data/websocket/1485975476.86-group_joined.json b/_pytest/data/websocket/1485975476.86-group_joined.json
index b4316f3..bbd971c 100644
--- a/_pytest/data/websocket/1485975476.86-group_joined.json
+++ b/_pytest/data/websocket/1485975476.86-group_joined.json
@@ -1 +1 @@
-{u'event_ts': u'1485975476.876646', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_joined', u'channel': {u'name': u'some-channel', u'creator': u'U407ABLLW', u'is_mpim': False, u'is_archived': False, u'created': 1485975458, u'is_group': True, u'topic': {u'last_set': 1485975476, u'value': u'', u'creator': u'U407ABLLW'}, u'purpose': {u'last_set': 1485975476, u'value': u'who knows?', u'creator': u'U407ABLLW'}, u'members': [u'U407ABLLW', u'U3ZKBBDL5'], u'is_starred': False, u'id': u'G409GKN9M'}}
\ No newline at end of file
+{"event_ts": "1485975476.876646", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_joined", "channel": {"name": "some-channel", "created": 1485975458, "is_mpim": false, "is_archived": false, "creator": "U407ABLLW", "is_group": true, "topic": {"last_set": 1485975476, "value": "", "creator": "U407ABLLW"}, "purpose": {"last_set": 1485975476, "value": "who knows?", "creator": "U407ABLLW"}, "members": ["U407ABLLW", "U3ZKBBDL5"], "is_starred": false, "id": "G409GKN9M"}}
diff --git a/_pytest/data/websocket/1485975487.69-group_history_changed.json b/_pytest/data/websocket/1485975487.69-group_history_changed.json
index 8eabb23..0196401 100644
--- a/_pytest/data/websocket/1485975487.69-group_history_changed.json
+++ b/_pytest/data/websocket/1485975487.69-group_history_changed.json
@@ -1 +1 @@
-{u'event_ts': u'1485975476.876650', u'is_mpim': False, u'ts': u'1485975476.000004', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_history_changed', u'channel': u'G409GKN9M', u'latest': u'1485975476.876632'}
\ No newline at end of file
+{"event_ts": "1485975476.876650", "is_mpim": false, "ts": "1485975476.000004", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_history_changed", "channel": "G409GKN9M", "latest": "1485975476.876632"}
diff --git a/_pytest/data/websocket/1485975487.69-group_join.json b/_pytest/data/websocket/1485975487.69-group_join.json
index 4732dd4..0fdb54c 100644
--- a/_pytest/data/websocket/1485975487.69-group_join.json
+++ b/_pytest/data/websocket/1485975487.69-group_join.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_join', u'user': u'U3ZKBBDL5', u'channel': u'G409GKN9M', u'ts': u'1485975476.000003'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_join", "user": "U3ZKBBDL5", "channel": "G409GKN9M", "ts": "1485975476.000003"}
diff --git a/_pytest/data/websocket/1485975493.19-group_joined.json b/_pytest/data/websocket/1485975493.19-group_joined.json
index b4431c2..76e7b55 100644
--- a/_pytest/data/websocket/1485975493.19-group_joined.json
+++ b/_pytest/data/websocket/1485975493.19-group_joined.json
@@ -1 +1 @@
-{u'event_ts': u'1485975492.876964', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_joined', u'channel': {u'topic': {u'last_set': 0, u'value': u'', u'creator': u''}, u'name': u'some-private-channel', u'last_read': u'1485975492.000002', u'creator': u'U407ABLLW', u'is_mpim': False, u'is_archived': False, u'created': 1485975492, u'is_group': True, u'members': [u'U3ZKBBDL5', u'U407ABLLW'], u'unread_count': 0, u'is_open': True, u'purpose': {u'last_set': 0, u'value': u'', u'creator': u''}, u'unread_count_display': 0, u'id': u'G3ZJKP7GA', u'latest': {u'text': u'<@U407ABLLW|alice> has joined the group', u'type': u'message', u'user': u'U407ABLLW', u'ts': u'1485975492.000002', u'subtype': u'group_join'}}}
\ No newline at end of file
+{"event_ts": "1485975492.876964", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_joined", "channel": {"name": "some-private-channel", "last_read": "1485975492.000002", "creator": "U407ABLLW", "is_mpim": false, "is_archived": false, "created": 1485975492, "is_group": true, "topic": {"last_set": 0, "value": "", "creator": ""}, "unread_count": 0, "is_open": true, "purpose": {"last_set": 0, "value": "", "creator": ""}, "members": ["U3ZKBBDL5", "U407ABLLW"], "latest": {"text": "<@U407ABLLW|alice> has joined the group", "subtype": "group_join", "type": "message", "user": "U407ABLLW", "ts": "1485975492.000002"}, "id": "G3ZJKP7GA", "unread_count_display": 0}}
diff --git a/_pytest/data/websocket/1485975493.23-message.json b/_pytest/data/websocket/1485975493.23-message.json
index e82d057..c6dad6c 100644
--- a/_pytest/data/websocket/1485975493.23-message.json
+++ b/_pytest/data/websocket/1485975493.23-message.json
@@ -1 +1 @@
-{u'user_profile': {u'avatar_hash': u'g6f7a5bf7eb7', u'first_name': u'Second', u'real_name': u'Second Testuser', u'name': u'bob', u'image_72': u'https://secure.gravatar.com/avatar/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png'}, u'inviter': u'U407ABLLW', u'ts': u'1485975492.000003', u'subtype': u'group_join', u'text': u'<@U3ZKBBDL5|bob> has joined the group', u'user': u'U3ZKBBDL5', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'G3ZJKP7GA'}
\ No newline at end of file
+{"type": "message", "user_profile": {"avatar_hash": "g6f7a5bf7eb7", "first_name": "Second", "image_72": "https://secure.gravatar.com/avatar/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png", "name": "bob", "real_name": "Second Testuser"}, "text": "<@U3ZKBBDL5|bob> has joined the group", "ts": "1485975492.000003", "subtype": "group_join", "user": "U3ZKBBDL5", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "inviter": "U407ABLLW", "channel": "G3ZJKP7GA"}
diff --git a/_pytest/data/websocket/1485975547.75-message.json b/_pytest/data/websocket/1485975547.75-message.json
index 80b233b..4826952 100644
--- a/_pytest/data/websocket/1485975547.75-message.json
+++ b/_pytest/data/websocket/1485975547.75-message.json
@@ -1 +1 @@
-{u'user_profile': {u'avatar_hash': u'gfd0ce7a168d', u'first_name': u'First', u'real_name': u'First Testuser', u'name': u'alice', u'image_72': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png'}, u'text': u"<@U407ABLLW|alice> set the channel's purpose: seekret", u'ts': u'1485975492.000004', u'subtype': u'group_purpose', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'G3ZJKP7GA', u'purpose': u'seekret'}
\ No newline at end of file
+{"user_profile": {"avatar_hash": "gfd0ce7a168d", "first_name": "First", "image_72": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png", "name": "alice", "real_name": "First Testuser"}, "text": "<@U407ABLLW|alice> set the channel's purpose: seekret", "ts": "1485975492.000004", "subtype": "group_purpose", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "G3ZJKP7GA", "purpose": "seekret"}
diff --git a/_pytest/data/websocket/1485975547.75-update_thread_state.json b/_pytest/data/websocket/1485975547.75-update_thread_state.json
index 08de51c..4d91b20 100644
--- a/_pytest/data/websocket/1485975547.75-update_thread_state.json
+++ b/_pytest/data/websocket/1485975547.75-update_thread_state.json
@@ -1 +1 @@
-{u'mention_count': 0, u'event_ts': u'1485975492.876967', u'timestamp': u'1485975493.013414', u'has_unreads': False, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'update_thread_state'}
\ No newline at end of file
+{"mention_count": 0, "event_ts": "1485975492.876967", "timestamp": "1485975493.013414", "has_unreads": false, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "update_thread_state"}
diff --git a/_pytest/data/websocket/1485975547.83-reconnect_url.json b/_pytest/data/websocket/1485975547.83-reconnect_url.json
index f8ec065..42da6c5 100644
--- a/_pytest/data/websocket/1485975547.83-reconnect_url.json
+++ b/_pytest/data/websocket/1485975547.83-reconnect_url.json
@@ -1 +1 @@
-{u'url': u'wss://mpmulti-t63z.slack-msgs.com/websocket/I4VgKhpT11zUwKt_Wajw_phIbuzM7xTy0V0DVLb6dyPDkqGU5-497Jn3bOGncotPX4QhfZepKVtlUkrSWCqMcbjH_vjRo91HBvzvk4nJoScjn6KJatZ6vpzokG44Ee-vNMsxXLeedcbraFAzCrLlODGRqvdqxUQDQFMVES0XHP8=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'}
\ No newline at end of file
+{"url": "wss://mpmulti-t63z.slack-msgs.com/websocket/I4VgKhpT11zUwKt_Wajw_phIbuzM7xTy0V0DVLb6dyPDkqGU5-497Jn3bOGncotPX4QhfZepKVtlUkrSWCqMcbjH_vjRo91HBvzvk4nJoScjn6KJatZ6vpzokG44Ee-vNMsxXLeedcbraFAzCrLlODGRqvdqxUQDQFMVES0XHP8=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"}
diff --git a/_pytest/data/websocket/1485975606.59-team_join.json b/_pytest/data/websocket/1485975606.59-team_join.json
index 23f780f..3af908e 100644
--- a/_pytest/data/websocket/1485975606.59-team_join.json
+++ b/_pytest/data/websocket/1485975606.59-team_join.json
@@ -1 +1 @@
-{u'event_ts': u'1485975606.879253', u'cache_ts': 1485975606, u'type': u'team_join', u'user': {u'status': None, u'profile': {u'first_name': u'Chuck', u'last_name': u'Testuser', u'fields': None, u'real_name': u'Chuck Testuser', u'image_24': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png', u'real_name_normalized': u'Chuck Testuser', u'image_512': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png', u'image_32': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png', u'image_48': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png', u'image_72': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png', u'avatar_hash': u'g7f87f7015f8', u'image_192': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png'}, u'tz': u'America/Los_Angeles', u'name': u'chuck', u'presence': u'away', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Chuck Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}}
\ No newline at end of file
+{"event_ts": "1485975606.879253", "cache_ts": 1485975606, "type": "team_join", "user": {"status": null, "profile": {"first_name": "Chuck", "last_name": "Testuser", "fields": null, "real_name": "Chuck Testuser", "image_24": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png", "real_name_normalized": "Chuck Testuser", "image_512": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png", "image_32": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png", "image_48": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png", "avatar_hash": "g7f87f7015f8", "image_72": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png", "image_192": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png"}, "tz": "America/Los_Angeles", "name": "chuck", "presence": "away", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Chuck Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}}
diff --git a/_pytest/data/websocket/1485975606.67-message.json b/_pytest/data/websocket/1485975606.67-message.json
index 8aec187..cfc8e5f 100644
--- a/_pytest/data/websocket/1485975606.67-message.json
+++ b/_pytest/data/websocket/1485975606.67-message.json
@@ -1 +1 @@
-{u'user_profile': {u'avatar_hash': u'g7f87f7015f8', u'first_name': u'Chuck', u'real_name': u'Chuck Testuser', u'name': u'chuck', u'image_72': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png'}, u'text': u'<@U4096CBHC|chuck> has joined the channel', u'ts': u'1485975606.000003', u'subtype': u'channel_join', u'user': u'U4096CBHC', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'}
\ No newline at end of file
+{"user_profile": {"avatar_hash": "g7f87f7015f8", "first_name": "Chuck", "image_72": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png", "name": "chuck", "real_name": "Chuck Testuser"}, "text": "<@U4096CBHC|chuck> has joined the channel", "ts": "1485975606.000003", "subtype": "channel_join", "user": "U4096CBHC", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"}
diff --git a/_pytest/data/websocket/1485975606.75-im_created.json b/_pytest/data/websocket/1485975606.75-im_created.json
index 295929f..fa360d8 100644
--- a/_pytest/data/websocket/1485975606.75-im_created.json
+++ b/_pytest/data/websocket/1485975606.75-im_created.json
@@ -1 +1 @@
-{u'event_ts': u'1485975606.879278', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'im_created', u'user': u'U4096CBHC', u'channel': {u'last_read': u'0000000000.000000', u'created': 1485975606, u'is_org_shared': False, u'unread_count': 0, u'is_open': False, u'user': u'U4096CBHC', u'unread_count_display': 0, u'is_im': True, u'id': u'D409J34CF', u'latest': None}}
\ No newline at end of file
+{"event_ts": "1485975606.879278", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "im_created", "user": "U4096CBHC", "channel": {"last_read": "0000000000.000000", "created": 1485975606, "is_org_shared": false, "unread_count": 0, "is_open": false, "user": "U4096CBHC", "unread_count_display": 0, "is_im": true, "id": "D409J34CF", "latest": null}}
diff --git a/_pytest/data/websocket/1485975606.75-im_open.json b/_pytest/data/websocket/1485975606.75-im_open.json
index 883a9ee..f2a9696 100644
--- a/_pytest/data/websocket/1485975606.75-im_open.json
+++ b/_pytest/data/websocket/1485975606.75-im_open.json
@@ -1 +1 @@
-{u'event_ts': u'1485975606.879280', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'im_open', u'user': u'U4096CBHC', u'channel': u'D409J34CF'}
\ No newline at end of file
+{"event_ts": "1485975606.879280", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "im_open", "user": "U4096CBHC", "channel": "D409J34CF"}
diff --git a/_pytest/data/websocket/1485975611.29-presence_change.json b/_pytest/data/websocket/1485975611.29-presence_change.json
index 2395dc0..ec41e5f 100644
--- a/_pytest/data/websocket/1485975611.29-presence_change.json
+++ b/_pytest/data/websocket/1485975611.29-presence_change.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'presence_change', u'user': u'U4096CBHC', u'presence': u'active'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U4096CBHC", "presence": "active"}
diff --git a/_pytest/data/websocket/1485975640.5-user_change.json b/_pytest/data/websocket/1485975640.5-user_change.json
index a722cfc..9c2b114 100644
--- a/_pytest/data/websocket/1485975640.5-user_change.json
+++ b/_pytest/data/websocket/1485975640.5-user_change.json
@@ -1 +1 @@
-{u'event_ts': u'1485975640.879906', u'cache_ts': 1485975640, u'type': u'user_change', u'user': {u'status': None, u'profile': {u'first_name': u'Charles', u'last_name': u'Testuser', u'fields': [], u'real_name': u'Charles Testuser', u'image_24': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png', u'real_name_normalized': u'Charles Testuser', u'image_512': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png', u'image_32': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png', u'image_48': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png', u'image_72': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png', u'avatar_hash': u'g7f87f7015f8', u'image_192': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png'}, u'tz': u'America/Los_Angeles', u'name': u'chuck', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Charles Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}}
\ No newline at end of file
+{"event_ts": "1485975640.879906", "cache_ts": 1485975640, "type": "user_change", "user": {"status": null, "profile": {"first_name": "Charles", "last_name": "Testuser", "fields": [], "real_name": "Charles Testuser", "image_24": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png", "real_name_normalized": "Charles Testuser", "image_512": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png", "image_32": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png", "image_48": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png", "avatar_hash": "g7f87f7015f8", "image_72": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png", "image_192": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png"}, "tz": "America/Los_Angeles", "name": "chuck", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Charles Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}}
diff --git a/_pytest/data/websocket/1485975669.14-user_change.json b/_pytest/data/websocket/1485975669.14-user_change.json
index 2faf007..0561553 100644
--- a/_pytest/data/websocket/1485975669.14-user_change.json
+++ b/_pytest/data/websocket/1485975669.14-user_change.json
@@ -1 +1 @@
-{u'event_ts': u'1485975668.880329', u'cache_ts': 1485975669, u'type': u'user_change', u'user': {u'status': None, u'profile': {u'first_name': u'Charles', u'last_name': u'Testuser', u'fields': [], u'real_name': u'Charles Testuser', u'image_24': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png', u'real_name_normalized': u'Charles Testuser', u'image_512': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png', u'image_32': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png', u'image_48': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png', u'image_72': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png', u'avatar_hash': u'g7f87f7015f8', u'image_192': u'https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png'}, u'tz': u'America/Los_Angeles', u'name': u'charles', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Charles Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}}
\ No newline at end of file
+{"event_ts": "1485975668.880329", "cache_ts": 1485975669, "type": "user_change", "user": {"status": null, "profile": {"first_name": "Charles", "last_name": "Testuser", "fields": [], "real_name": "Charles Testuser", "image_24": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png", "real_name_normalized": "Charles Testuser", "image_512": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png", "image_32": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png", "image_48": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png", "avatar_hash": "g7f87f7015f8", "image_72": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png", "image_192": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png"}, "tz": "America/Los_Angeles", "name": "charles", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Charles Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}}
diff --git a/_pytest/data/websocket/1485975675.81-presence_change.json b/_pytest/data/websocket/1485975675.81-presence_change.json
index 2395dc0..ec41e5f 100644
--- a/_pytest/data/websocket/1485975675.81-presence_change.json
+++ b/_pytest/data/websocket/1485975675.81-presence_change.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'presence_change', u'user': u'U4096CBHC', u'presence': u'active'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U4096CBHC", "presence": "active"}
diff --git a/_pytest/data/websocket/1485975690.59-mpim_joined.json b/_pytest/data/websocket/1485975690.59-mpim_joined.json
index c4e0bc0..d377778 100644
--- a/_pytest/data/websocket/1485975690.59-mpim_joined.json
+++ b/_pytest/data/websocket/1485975690.59-mpim_joined.json
@@ -1 +1 @@
-{u'event_ts': u'1485975690.880722', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'mpim_joined', u'channel': {u'topic': {u'last_set': 1485975690, u'value': u'Group messaging', u'creator': u'U407ABLLW'}, u'name': u'mpdm-bob--alice--charles-1', u'last_read': u'0000000000.000000', u'creator': u'U407ABLLW', u'is_mpim': True, u'is_archived': False, u'created': 1485975690, u'is_group': True, u'members': [u'U407ABLLW', u'U3ZKBBDL5', u'U4096CBHC'], u'unread_count': 0, u'is_open': False, u'purpose': {u'last_set': 1485975690, u'value': u'Group messaging with: @bob @alice @charles', u'creator': u'U407ABLLW'}, u'unread_count_display': 0, u'id': u'G3ZGMF4RZ', u'latest': None}}
\ No newline at end of file
+{"event_ts": "1485975690.880722", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "mpim_joined", "channel": {"name": "mpdm-bob--alice--charles-1", "last_read": "0000000000.000000", "creator": "U407ABLLW", "is_mpim": true, "is_archived": false, "created": 1485975690, "is_group": true, "topic": {"last_set": 1485975690, "value": "Group messaging", "creator": "U407ABLLW"}, "unread_count": 0, "is_open": false, "purpose": {"last_set": 1485975690, "value": "Group messaging with: @bob @alice @charles", "creator": "U407ABLLW"}, "members": ["U407ABLLW", "U3ZKBBDL5", "U4096CBHC"], "latest": null, "id": "G3ZGMF4RZ", "unread_count_display": 0}}
diff --git a/_pytest/data/websocket/1485975690.67-group_joined.json b/_pytest/data/websocket/1485975690.67-group_joined.json
index 3968cc3..b1c27dd 100644
--- a/_pytest/data/websocket/1485975690.67-group_joined.json
+++ b/_pytest/data/websocket/1485975690.67-group_joined.json
@@ -1 +1 @@
-{u'event_ts': u'1485975690.880723', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_joined', u'channel': {u'topic': {u'last_set': 1485975690, u'value': u'Group messaging', u'creator': u'U407ABLLW'}, u'name': u'mpdm-bob--alice--charles-1', u'last_read': u'0000000000.000000', u'creator': u'U407ABLLW', u'is_mpim': True, u'is_archived': False, u'created': 1485975690, u'is_group': True, u'members': [u'U407ABLLW', u'U3ZKBBDL5', u'U4096CBHC'], u'unread_count': 0, u'is_open': False, u'purpose': {u'last_set': 1485975690, u'value': u'Group messaging with: @bob @alice @charles', u'creator': u'U407ABLLW'}, u'unread_count_display': 0, u'id': u'G3ZGMF4RZ', u'latest': None}}
\ No newline at end of file
+{"event_ts": "1485975690.880723", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_joined", "channel": {"name": "mpdm-bob--alice--charles-1", "last_read": "0000000000.000000", "creator": "U407ABLLW", "is_mpim": true, "is_archived": false, "created": 1485975690, "is_group": true, "topic": {"last_set": 1485975690, "value": "Group messaging", "creator": "U407ABLLW"}, "unread_count": 0, "is_open": false, "purpose": {"last_set": 1485975690, "value": "Group messaging with: @bob @alice @charles", "creator": "U407ABLLW"}, "members": ["U407ABLLW", "U3ZKBBDL5", "U4096CBHC"], "latest": null, "id": "G3ZGMF4RZ", "unread_count_display": 0}}
diff --git a/_pytest/data/websocket/1485975690.67-update_thread_state.json b/_pytest/data/websocket/1485975690.67-update_thread_state.json
index c9ba02a..c745df6 100644
--- a/_pytest/data/websocket/1485975690.67-update_thread_state.json
+++ b/_pytest/data/websocket/1485975690.67-update_thread_state.json
@@ -1 +1 @@
-{u'mention_count': 0, u'event_ts': u'1485975690.880724', u'timestamp': u'1485975690.537094', u'has_unreads': False, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'update_thread_state'}
\ No newline at end of file
+{"mention_count": 0, "event_ts": "1485975690.880724", "timestamp": "1485975690.537094", "has_unreads": false, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "update_thread_state"}
diff --git a/_pytest/data/websocket/1485975697.58-user_typing.json b/_pytest/data/websocket/1485975697.58-user_typing.json
index b909fa3..b8239d4 100644
--- a/_pytest/data/websocket/1485975697.58-user_typing.json
+++ b/_pytest/data/websocket/1485975697.58-user_typing.json
@@ -1 +1 @@
-{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'G3ZGMF4RZ'}
\ No newline at end of file
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "G3ZGMF4RZ"}
diff --git a/_pytest/data/websocket/1485975698.45-message.json b/_pytest/data/websocket/1485975698.45-message.json
index 777d55f..2118ec7 100644
--- a/_pytest/data/websocket/1485975698.45-message.json
+++ b/_pytest/data/websocket/1485975698.45-message.json
@@ -1 +1 @@
-{u'text': u'surely', u'ts': u'1485975698.000002', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'G3ZGMF4RZ'}
\ No newline at end of file
+{"text": "surely", "ts": "1485975698.000002", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "G3ZGMF4RZ"}
diff --git a/_pytest/data/websocket/1485975701.72-reaction_added.json b/_pytest/data/websocket/1485975701.72-reaction_added.json
index e9328c7..b4d2282 100644
--- a/_pytest/data/websocket/1485975701.72-reaction_added.json
+++ b/_pytest/data/websocket/1485975701.72-reaction_added.json
@@ -1 +1 @@
-{u'reaction': u'unamused', u'event_ts': u'1485975701.880957', u'ts': u'1485975701.000003', u'item': {u'type': u'message', u'ts': u'1485975698.000002', u'channel': u'G3ZGMF4RZ'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_added'}
\ No newline at end of file
+{"reaction": "unamused", "event_ts": "1485975701.880957", "ts": "1485975701.000003", "item": {"type": "message", "ts": "1485975698.000002", "channel": "G3ZGMF4RZ"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_added"}
diff --git a/_pytest/data/websocket/1485975703.22-reaction_removed.json b/_pytest/data/websocket/1485975703.22-reaction_removed.json
index 1e0e0df..3deafb2 100644
--- a/_pytest/data/websocket/1485975703.22-reaction_removed.json
+++ b/_pytest/data/websocket/1485975703.22-reaction_removed.json
@@ -1 +1 @@
-{u'reaction': u'unamused', u'event_ts': u'1485975703.880979', u'ts': u'1485975703.000004', u'item': {u'type': u'message', u'ts': u'1485975698.000002', u'channel': u'G3ZGMF4RZ'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_removed'}
\ No newline at end of file
+{"reaction": "unamused", "event_ts": "1485975703.880979", "ts": "1485975703.000004", "item": {"type": "message", "ts": "1485975698.000002", "channel": "G3ZGMF4RZ"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_removed"}
diff --git a/_pytest/data/websocket/1485975715.79-message.json b/_pytest/data/websocket/1485975715.79-message.json
index 2668055..5d3c51c 100644
--- a/_pytest/data/websocket/1485975715.79-message.json
+++ b/_pytest/data/websocket/1485975715.79-message.json
@@ -1 +1 @@
-{u'username': u'<@U407ABLLW|alice>', u'source_team': u'T3YS5EAL9', u'display_as_bot': False, u'text': u'<@U407ABLLW|alice> uploaded a file: ', u'channel': u'G3ZGMF4RZ', u'user_profile': {u'avatar_hash': u'gfd0ce7a168d', u'first_name': u'First', u'real_name': u'First Testuser', u'name': u'alice', u'image_72': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png'}, u'upload': True, u'ts': u'1485975715.000005', u'subtype': u'file_share', u'user': u'U407ABLLW', u'file': {u'filetype': u'text', u'lines_more': 0, u'channels': [], u'display_as_bot': False, u'id': u'F3ZLY6K5J', u'size': 14, u'title': u'Untitled', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZLY6K5J/-.txt', u'ims': [], u'preview': u'some code here', u'external_type': u'', u'edit_link': u'https://weeslacktest.slack.com/files/alice/F3ZLY6K5J/-.txt/edit', u'username': u'', u'timestamp': 1485975715, u'public_url_shared': False, u'editable': True, u'preview_is_truncated': False, u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZLY6K5J/download/-.txt', u'user': u'U407ABLLW', u'groups': [], u'is_public': False, u'pretty_type': u'Plain Text', u'name': u'-.txt', u'mimetype': u'text/plain', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZLY6K5J-39c2c4f739', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZLY6K5J/-.txt', u'is_external': False, u'created': 1485975715, u'lines': 1, u'comments_count': 0, u'mode': u'snippet', u'preview_highlight': u'
\n
\n
some code here
\n
\n
\n'}, u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'user_team': u'T3YS5EAL9', u'bot_id': None} \ No newline at end of file +{"username": "<@U407ABLLW|alice>", "source_team": "T3YS5EAL9", "user_profile": {"avatar_hash": "gfd0ce7a168d", "first_name": "First", "image_72": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png", "name": "alice", "real_name": "First Testuser"}, "text": "<@U407ABLLW|alice> uploaded a file: ", "team": "T3YS5EAL9", "upload": true, "ts": "1485975715.000005", "display_as_bot": false, "user": "U407ABLLW", "file": {"filetype": "text", "lines_more": 0, "channels": [], "display_as_bot": false, "id": "F3ZLY6K5J", "size": 14, "title": "Untitled", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZLY6K5J/-.txt", "ims": [], "preview": "some code here", "external_type": "", "edit_link": "https://weeslacktest.slack.com/files/alice/F3ZLY6K5J/-.txt/edit", "username": "", "timestamp": 1485975715, "public_url_shared": false, "editable": true, "preview_highlight": "
\n
\n
some code here
\n
\n
\n", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZLY6K5J/download/-.txt", "user": "U407ABLLW", "groups": [], "is_public": false, "pretty_type": "Plain Text", "is_external": false, "mimetype": "text/plain", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZLY6K5J-39c2c4f739", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZLY6K5J/-.txt", "name": "-.txt", "created": 1485975715, "lines": 1, "comments_count": 0, "mode": "snippet", "preview_is_truncated": false}, "subtype": "file_share", "user_team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "G3ZGMF4RZ", "bot_id": null} diff --git a/_pytest/data/websocket/1485975715.87-file_shared.json b/_pytest/data/websocket/1485975715.87-file_shared.json index 2149f8c..526f72c 100644 --- a/_pytest/data/websocket/1485975715.87-file_shared.json +++ b/_pytest/data/websocket/1485975715.87-file_shared.json @@ -1 +1 @@ -{u'user_id': u'U407ABLLW', u'event_ts': u'1485975715.881302', u'file_id': u'F3ZLY6K5J', u'file': {u'id': u'F3ZLY6K5J'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_shared'} \ No newline at end of file +{"user_id": "U407ABLLW", "event_ts": "1485975715.881302", "file_id": "F3ZLY6K5J", "file": {"id": "F3ZLY6K5J"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_shared"} diff --git a/_pytest/data/websocket/1485975723.85-message.json b/_pytest/data/websocket/1485975723.85-message.json index 0e52b67..d06d025 100644 --- a/_pytest/data/websocket/1485975723.85-message.json +++ b/_pytest/data/websocket/1485975723.85-message.json @@ -1 +1 @@ -{u'event_ts': u'1485975723.881563', u'ts': u'1485975723.000006', u'subtype': u'message_deleted', u'hidden': True, u'deleted_ts': u'1485975698.000002', u'type': u'message', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'channel': u'G3ZGMF4RZ', u'previous_message': {u'text': u'surely', u'type': u'message', u'user': u'U407ABLLW', u'ts': u'1485975698.000002'}} \ No newline at end of file +{"event_ts": "1485975723.881563", "ts": "1485975723.000006", "subtype": "message_deleted", "hidden": true, "previous_message": {"text": "surely", "type": "message", "user": "U407ABLLW", "ts": "1485975698.000002"}, "type": "message", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "channel": "G3ZGMF4RZ", "deleted_ts": "1485975698.000002"} diff --git a/_pytest/data/websocket/1485975727.84-reconnect_url.json b/_pytest/data/websocket/1485975727.84-reconnect_url.json index d1417e2..3630d02 100644 --- a/_pytest/data/websocket/1485975727.84-reconnect_url.json +++ b/_pytest/data/websocket/1485975727.84-reconnect_url.json @@ -1 +1 @@ -{u'url': u'wss://mpmulti-824w.slack-msgs.com/websocket/Pl7CWLLi3w1S0gHESj79_JySeV0ho6vZRl1usylFoDVBWTj332mKkaLBR6OdrDqRDlYPnwwnh9TqlqOMVajfyHMH_Q02oVU14YJS_ao5_nEJBBd58vDB3XkKfGdPRZNgP2Id05xTpEy9izt9EF_BdDaDaTU9nGc5c2ggnuS9gos=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file +{"url": "wss://mpmulti-824w.slack-msgs.com/websocket/Pl7CWLLi3w1S0gHESj79_JySeV0ho6vZRl1usylFoDVBWTj332mKkaLBR6OdrDqRDlYPnwwnh9TqlqOMVajfyHMH_Q02oVU14YJS_ao5_nEJBBd58vDB3XkKfGdPRZNgP2Id05xTpEy9izt9EF_BdDaDaTU9nGc5c2ggnuS9gos=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} diff --git a/_pytest/data/websocket/1485975735.91-group_archive.json b/_pytest/data/websocket/1485975735.91-group_archive.json index 05dd53e..61cfdc7 100644 --- a/_pytest/data/websocket/1485975735.91-group_archive.json +++ b/_pytest/data/websocket/1485975735.91-group_archive.json @@ -1 +1 @@ -{u'event_ts': u'1485975735.881780', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_archive', u'ts': u'1485975735.000005', u'channel': u'G409GKN9M'} \ No newline at end of file +{"event_ts": "1485975735.881780", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_archive", "ts": "1485975735.000005", "channel": "G409GKN9M"} diff --git a/_pytest/data/websocket/1485975735.99-message.json b/_pytest/data/websocket/1485975735.99-message.json index 3ca5b71..10f541f 100644 --- a/_pytest/data/websocket/1485975735.99-message.json +++ b/_pytest/data/websocket/1485975735.99-message.json @@ -1 +1 @@ -{u'user_profile': {u'avatar_hash': u'gfd0ce7a168d', u'first_name': u'First', u'real_name': u'First Testuser', u'name': u'alice', u'image_72': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png'}, u'text': u'<@U407ABLLW|alice> archived the private channel', u'ts': u'1485975735.000006', u'subtype': u'group_archive', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'G409GKN9M'} \ No newline at end of file +{"user_profile": {"avatar_hash": "gfd0ce7a168d", "first_name": "First", "image_72": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png", "name": "alice", "real_name": "First Testuser"}, "text": "<@U407ABLLW|alice> archived the private channel", "ts": "1485975735.000006", "subtype": "group_archive", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "G409GKN9M"} diff --git a/_pytest/data/websocket/1485975736.08-message.json b/_pytest/data/websocket/1485975736.08-message.json index 172f553..147dbb4 100644 --- a/_pytest/data/websocket/1485975736.08-message.json +++ b/_pytest/data/websocket/1485975736.08-message.json @@ -1 +1 @@ -{u'text': u'<@U407ABLLW|alice> archived the private channel ', u'ts': u'1485975735.000002', u'user': u'USLACKBOT', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'D3ZK1D8JY'} \ No newline at end of file +{"text": "<@U407ABLLW|alice> archived the private channel ", "ts": "1485975735.000002", "user": "USLACKBOT", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "D3ZK1D8JY"} diff --git a/_pytest/data/websocket/1485975738.1-desktop_notification.json b/_pytest/data/websocket/1485975738.1-desktop_notification.json index 56e72f9..da451bb 100644 --- a/_pytest/data/websocket/1485975738.1-desktop_notification.json +++ b/_pytest/data/websocket/1485975738.1-desktop_notification.json @@ -1 +1 @@ -{u'launchUri': u'slack://channel?id=D3ZK1D8JY&message=1485975735000002&team=T3YS5EAL9', u'subtitle': u'slackbot', u'is_shared': False, u'title': u'weeslacktest', u'ssbFilename': u'knock_brush.mp3', u'avatarImage': u'https://a.slack-edge.com/66f9/img/slackbot_192.png', u'imageUri': None, u'content': u'@alice archived the private channel some-channel', u'event_ts': u'1485975737.881829', u'msg': u'1485975735.000002', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'desktop_notification', u'channel': u'D3ZK1D8JY'} \ No newline at end of file +{"avatarImage": "https://a.slack-edge.com/66f9/img/slackbot_192.png", "subtitle": "slackbot", "is_shared": false, "title": "weeslacktest", "ssbFilename": "knock_brush.mp3", "imageUri": null, "launchUri": "slack://channel?id=D3ZK1D8JY&message=1485975735000002&team=T3YS5EAL9", "event_ts": "1485975737.881829", "msg": "1485975735.000002", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "content": "@alice archived the private channel some-channel", "type": "desktop_notification", "channel": "D3ZK1D8JY"} diff --git a/_pytest/data/websocket/1485975747.17-channel_created.json b/_pytest/data/websocket/1485975747.17-channel_created.json index 453552d..6bcafdb 100644 --- a/_pytest/data/websocket/1485975747.17-channel_created.json +++ b/_pytest/data/websocket/1485975747.17-channel_created.json @@ -1 +1 @@ -{u'event_ts': u'1485975746.882009', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_created', u'channel': {u'is_shared': False, u'is_channel': True, u'created': 1485975747, u'creator': u'U407ABLLW', u'is_org_shared': False, u'id': u'C3ZM8JTD3', u'name': u'some-channel2'}} \ No newline at end of file +{"event_ts": "1485975746.882009", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_created", "channel": {"is_shared": false, "is_channel": true, "creator": "U407ABLLW", "created": 1485975747, "is_org_shared": false, "id": "C3ZM8JTD3", "name": "some-channel2"}} diff --git a/_pytest/data/websocket/1485975757.63-channel_rename.json b/_pytest/data/websocket/1485975757.63-channel_rename.json index ff5e537..a921fed 100644 --- a/_pytest/data/websocket/1485975757.63-channel_rename.json +++ b/_pytest/data/websocket/1485975757.63-channel_rename.json @@ -1 +1 @@ -{u'event_ts': u'1485975757.882178', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_rename', u'channel': {u'created': u'1485975747', u'id': u'C3ZM8JTD3', u'is_channel': True, u'name': u'some-channel2-renamed'}} \ No newline at end of file +{"event_ts": "1485975757.882178", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_rename", "channel": {"name": "some-channel2-renamed", "id": "C3ZM8JTD3", "is_channel": true, "created": "1485975747"}} diff --git a/_pytest/data/websocket/1485975771.6-presence_change.json b/_pytest/data/websocket/1485975771.6-presence_change.json index e7252bc..e70421c 100644 --- a/_pytest/data/websocket/1485975771.6-presence_change.json +++ b/_pytest/data/websocket/1485975771.6-presence_change.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'presence_change', u'user': u'U407ABLLW', u'presence': u'away'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U407ABLLW", "presence": "away"} diff --git a/_pytest/data/websocket/1485975774.62-presence_change.json b/_pytest/data/websocket/1485975774.62-presence_change.json index a180d76..9728fae 100644 --- a/_pytest/data/websocket/1485975774.62-presence_change.json +++ b/_pytest/data/websocket/1485975774.62-presence_change.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'presence_change', u'user': u'U407ABLLW', u'presence': u'active'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U407ABLLW", "presence": "active"} diff --git a/_pytest/data/websocket/1485975822.17-user_typing.json b/_pytest/data/websocket/1485975822.17-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485975822.17-user_typing.json +++ b/_pytest/data/websocket/1485975822.17-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975824.48-message.json b/_pytest/data/websocket/1485975824.48-message.json index bdcee99..9fae542 100644 --- a/_pytest/data/websocket/1485975824.48-message.json +++ b/_pytest/data/websocket/1485975824.48-message.json @@ -1 +1 @@ -{u'text': u'generally, yep!', u'ts': u'1485975824.000004', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file +{"text": "generally, yep!", "ts": "1485975824.000004", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975836.23-message.json b/_pytest/data/websocket/1485975836.23-message.json index 5ac0bac..6715bed 100644 --- a/_pytest/data/websocket/1485975836.23-message.json +++ b/_pytest/data/websocket/1485975836.23-message.json @@ -1 +1 @@ -{u'thread_ts': u'1485975824.000004', u'text': u'have you met my friend threads?', u'ts': u'1485975835.000005', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file +{"thread_ts": "1485975824.000004", "text": "have you met my friend threads?", "ts": "1485975835.000005", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975836.31-message.json b/_pytest/data/websocket/1485975836.31-message.json index a90ce8b..3823c61 100644 --- a/_pytest/data/websocket/1485975836.31-message.json +++ b/_pytest/data/websocket/1485975836.31-message.json @@ -1 +1 @@ -{u'event_ts': u'1485975835.883772', u'ts': u'1485975835.000006', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'subtype': u'message_replied', u'message': {u'thread_ts': u'1485975824.000004', u'text': u'generally, yep!', u'ts': u'1485975824.000004', u'reply_count': 1, u'user': u'U407ABLLW', u'replies': [{u'user': u'U407ABLLW', u'ts': u'1485975835.000005'}], u'type': u'message'}, u'type': u'message', u'hidden': True, u'channel': u'C407ABS94'} \ No newline at end of file +{"hidden": true, "event_ts": "1485975835.883772", "ts": "1485975835.000006", "subtype": "message_replied", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "message": {"thread_ts": "1485975824.000004", "text": "generally, yep!", "ts": "1485975824.000004", "reply_count": 1, "user": "U407ABLLW", "replies": [{"user": "U407ABLLW", "ts": "1485975835.000005"}], "type": "message"}, "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975842.1-message.json b/_pytest/data/websocket/1485975842.1-message.json index c8b202b..b94a913 100644 --- a/_pytest/data/websocket/1485975842.1-message.json +++ b/_pytest/data/websocket/1485975842.1-message.json @@ -1 +1 @@ -{u'thread_ts': u'1485975824.000004', u'text': u'react if yes', u'ts': u'1485975841.000007', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file +{"thread_ts": "1485975824.000004", "text": "react if yes", "ts": "1485975841.000007", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975842.18-message.json b/_pytest/data/websocket/1485975842.18-message.json index d77fd22..7bc2b38 100644 --- a/_pytest/data/websocket/1485975842.18-message.json +++ b/_pytest/data/websocket/1485975842.18-message.json @@ -1 +1 @@ -{u'event_ts': u'1485975841.883922', u'ts': u'1485975841.000008', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'subtype': u'message_replied', u'message': {u'thread_ts': u'1485975824.000004', u'text': u'generally, yep!', u'ts': u'1485975824.000004', u'reply_count': 2, u'user': u'U407ABLLW', u'replies': [{u'user': u'U407ABLLW', u'ts': u'1485975835.000005'}, {u'user': u'U407ABLLW', u'ts': u'1485975841.000007'}], u'type': u'message'}, u'type': u'message', u'hidden': True, u'channel': u'C407ABS94'} \ No newline at end of file +{"hidden": true, "event_ts": "1485975841.883922", "ts": "1485975841.000008", "subtype": "message_replied", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "message": {"thread_ts": "1485975824.000004", "text": "generally, yep!", "ts": "1485975824.000004", "reply_count": 2, "user": "U407ABLLW", "replies": [{"user": "U407ABLLW", "ts": "1485975835.000005"}, {"user": "U407ABLLW", "ts": "1485975841.000007"}], "type": "message"}, "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975850.32-mpim_open.json b/_pytest/data/websocket/1485975850.32-mpim_open.json index 908ee49..ebce13e 100644 --- a/_pytest/data/websocket/1485975850.32-mpim_open.json +++ b/_pytest/data/websocket/1485975850.32-mpim_open.json @@ -1 +1 @@ -{u'event_ts': u'1485975849.884091', u'is_mpim': True, u'user': u'U3ZKBBDL5', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'mpim_open', u'channel': u'G3ZGMF4RZ'} \ No newline at end of file +{"event_ts": "1485975849.884091", "is_mpim": true, "user": "U3ZKBBDL5", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "mpim_open", "channel": "G3ZGMF4RZ"} diff --git a/_pytest/data/websocket/1485975850.45-group_open.json b/_pytest/data/websocket/1485975850.45-group_open.json index 60dd1d4..beb27d3 100644 --- a/_pytest/data/websocket/1485975850.45-group_open.json +++ b/_pytest/data/websocket/1485975850.45-group_open.json @@ -1 +1 @@ -{u'event_ts': u'1485975849.884092', u'is_mpim': True, u'user': u'U3ZKBBDL5', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'group_open', u'channel': u'G3ZGMF4RZ'} \ No newline at end of file +{"event_ts": "1485975849.884092", "is_mpim": true, "user": "U3ZKBBDL5", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_open", "channel": "G3ZGMF4RZ"} diff --git a/_pytest/data/websocket/1485975858.56-reaction_added.json b/_pytest/data/websocket/1485975858.56-reaction_added.json index 6b10607..978d30f 100644 --- a/_pytest/data/websocket/1485975858.56-reaction_added.json +++ b/_pytest/data/websocket/1485975858.56-reaction_added.json @@ -1 +1 @@ -{u'reaction': u'stuck_out_tongue', u'event_ts': u'1485975858.884268', u'item': {u'type': u'message', u'ts': u'1485975841.000007', u'channel': u'C407ABS94'}, u'user': u'U4096CBHC', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_added'} \ No newline at end of file +{"reaction": "stuck_out_tongue", "event_ts": "1485975858.884268", "item": {"type": "message", "ts": "1485975841.000007", "channel": "C407ABS94"}, "user": "U4096CBHC", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_added"} diff --git a/_pytest/data/websocket/1485975890.7-bot_added.json b/_pytest/data/websocket/1485975890.7-bot_added.json index 8ca953b..91254cf 100644 --- a/_pytest/data/websocket/1485975890.7-bot_added.json +++ b/_pytest/data/websocket/1485975890.7-bot_added.json @@ -1 +1 @@ -{u'event_ts': u'1485975890.885679', u'bot': {u'deleted': False, u'icons': {u'image_36': u'https://a.slack-edge.com/12b5a/plugins/tester/assets/service_36.png', u'image_48': u'https://a.slack-edge.com/12b5a/plugins/tester/assets/service_48.png', u'image_72': u'https://a.slack-edge.com/12b5a/plugins/tester/assets/service_72.png'}, u'app_id': u'A0F7XDUAZ', u'id': u'B3YTBU6L8', u'name': u'incoming-webhook'}, u'cache_ts': 1485975890, u'type': u'bot_added', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file +{"event_ts": "1485975890.885679", "type": "bot_added", "cache_ts": 1485975890, "bot": {"deleted": false, "id": "B3YTBU6L8", "app_id": "A0F7XDUAZ", "name": "incoming-webhook", "icons": {"image_36": "https://a.slack-edge.com/12b5a/plugins/tester/assets/service_36.png", "image_48": "https://a.slack-edge.com/12b5a/plugins/tester/assets/service_48.png", "image_72": "https://a.slack-edge.com/12b5a/plugins/tester/assets/service_72.png"}}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}} diff --git a/_pytest/data/websocket/1485975890.78-message.json b/_pytest/data/websocket/1485975890.78-message.json index 414f544..785faa6 100644 --- a/_pytest/data/websocket/1485975890.78-message.json +++ b/_pytest/data/websocket/1485975890.78-message.json @@ -1 +1 @@ -{u'text': u'added an integration to this channel: ', u'ts': u'1485975890.000009', u'subtype': u'bot_add', u'user': u'U4096CBHC', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94', u'bot_id': u'B3YTBU6L8'} \ No newline at end of file +{"text": "added an integration to this channel: ", "ts": "1485975890.000009", "subtype": "bot_add", "user": "U4096CBHC", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94", "bot_id": "B3YTBU6L8"} diff --git a/_pytest/data/websocket/1485975896.16-message.json b/_pytest/data/websocket/1485975896.16-message.json index 2d0499f..3a9db89 100644 --- a/_pytest/data/websocket/1485975896.16-message.json +++ b/_pytest/data/websocket/1485975896.16-message.json @@ -1 +1 @@ -{u'text': u'disabled an integration in this channel: ', u'ts': u'1485975896.000010', u'subtype': u'bot_disable', u'user': u'U4096CBHC', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94', u'bot_id': u'B3YTBU6L8'} \ No newline at end of file +{"text": "disabled an integration in this channel: ", "ts": "1485975896.000010", "subtype": "bot_disable", "user": "U4096CBHC", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94", "bot_id": "B3YTBU6L8"} diff --git a/_pytest/data/websocket/1485975907.84-reconnect_url.json b/_pytest/data/websocket/1485975907.84-reconnect_url.json index f26d883..a2f99c1 100644 --- a/_pytest/data/websocket/1485975907.84-reconnect_url.json +++ b/_pytest/data/websocket/1485975907.84-reconnect_url.json @@ -1 +1 @@ -{u'url': u'wss://mpmulti-1r8c.slack-msgs.com/websocket/8atJCMnYZ10YaqNai-zwEaNMfkwh85XwRpc0MYVVOzAojnPcrx8SboF-NBWeep6Hy7arytqEffr_Fh7mPrDagwEGwbOeX-OH3OLlubVjpC2cCLWwm2jN3rEZcq8A4j0tpPP56GZ84jbLn4BKvDhKkkKhRXQhuYtC7kskaXXLTbk=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file +{"url": "wss://mpmulti-1r8c.slack-msgs.com/websocket/8atJCMnYZ10YaqNai-zwEaNMfkwh85XwRpc0MYVVOzAojnPcrx8SboF-NBWeep6Hy7arytqEffr_Fh7mPrDagwEGwbOeX-OH3OLlubVjpC2cCLWwm2jN3rEZcq8A4j0tpPP56GZ84jbLn4BKvDhKkkKhRXQhuYtC7kskaXXLTbk=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} diff --git a/_pytest/data/websocket/1485975926.59-user_change.json b/_pytest/data/websocket/1485975926.59-user_change.json index f3ef251..179a67d 100644 --- a/_pytest/data/websocket/1485975926.59-user_change.json +++ b/_pytest/data/websocket/1485975926.59-user_change.json @@ -1 +1 @@ -{u'event_ts': u'1485975926.886492', u'cache_ts': 1485975926, u'type': u'user_change', u'user': {u'status': None, u'profile': {u'first_name': u'Charles', u'last_name': u'Testuser', u'fields': [], u'real_name': u'Charles Testuser', u'image_24': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_original': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_original.png', u'real_name_normalized': u'Charles Testuser', u'image_512': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_32': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_48': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_72': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'avatar_hash': u'5af404f7d4b7', u'image_1024': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_192': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png'}, u'tz': u'America/Los_Angeles', u'name': u'charles', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Charles Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file +{"event_ts": "1485975926.886492", "cache_ts": 1485975926, "type": "user_change", "user": {"status": null, "profile": {"first_name": "Charles", "last_name": "Testuser", "fields": [], "real_name": "Charles Testuser", "image_24": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_original": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_original.png", "real_name_normalized": "Charles Testuser", "image_512": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_32": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_48": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "avatar_hash": "5af404f7d4b7", "image_72": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_1024": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_192": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png"}, "tz": "America/Los_Angeles", "name": "charles", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Charles Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}} diff --git a/_pytest/data/websocket/1485975930.81-user_change.json b/_pytest/data/websocket/1485975930.81-user_change.json index 40ed3da..afbbb1b 100644 --- a/_pytest/data/websocket/1485975930.81-user_change.json +++ b/_pytest/data/websocket/1485975930.81-user_change.json @@ -1 +1 @@ -{u'event_ts': u'1485975930.886576', u'cache_ts': 1485975930, u'type': u'user_change', u'user': {u'status': None, u'profile': {u'first_name': u'Charles', u'last_name': u'Testuser', u'fields': [], u'real_name': u'Charles Testuser', u'image_24': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_original': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_original.png', u'real_name_normalized': u'Charles Testuser', u'image_512': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_32': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_48': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_72': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'avatar_hash': u'5af404f7d4b7', u'image_1024': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png', u'image_192': u'https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png'}, u'tz': u'America/Los_Angeles', u'name': u'charles', u'deleted': False, u'is_bot': False, u'tz_label': u'Pacific Standard Time', u'real_name': u'Charles Testuser', u'color': u'e7392d', u'team_id': u'T3YS5EAL9', u'is_admin': False, u'is_ultra_restricted': False, u'is_restricted': False, u'is_owner': False, u'tz_offset': -28800, u'id': u'U4096CBHC', u'is_primary_owner': False}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}} \ No newline at end of file +{"event_ts": "1485975930.886576", "cache_ts": 1485975930, "type": "user_change", "user": {"status": null, "profile": {"first_name": "Charles", "last_name": "Testuser", "fields": [], "real_name": "Charles Testuser", "image_24": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_original": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_original.png", "real_name_normalized": "Charles Testuser", "image_512": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_32": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_48": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "avatar_hash": "5af404f7d4b7", "image_72": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_1024": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_192": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png"}, "tz": "America/Los_Angeles", "name": "charles", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Charles Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}} diff --git a/_pytest/data/websocket/1485975956.38-file_public.json b/_pytest/data/websocket/1485975956.38-file_public.json index 3f44646..288b528 100644 --- a/_pytest/data/websocket/1485975956.38-file_public.json +++ b/_pytest/data/websocket/1485975956.38-file_public.json @@ -1 +1 @@ -{u'user_id': u'U407ABLLW', u'event_ts': u'1485975956.887078', u'file_id': u'F3ZJQTA66', u'file': {u'id': u'F3ZJQTA66'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_public'} \ No newline at end of file +{"user_id": "U407ABLLW", "event_ts": "1485975956.887078", "file_id": "F3ZJQTA66", "file": {"id": "F3ZJQTA66"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_public"} diff --git a/_pytest/data/websocket/1485975956.47-file_shared.json b/_pytest/data/websocket/1485975956.47-file_shared.json index 53296ba..1064ac7 100644 --- a/_pytest/data/websocket/1485975956.47-file_shared.json +++ b/_pytest/data/websocket/1485975956.47-file_shared.json @@ -1 +1 @@ -{u'user_id': u'U407ABLLW', u'event_ts': u'1485975956.887081', u'file_id': u'F3ZJQTA66', u'file': {u'id': u'F3ZJQTA66'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_shared'} \ No newline at end of file +{"user_id": "U407ABLLW", "event_ts": "1485975956.887081", "file_id": "F3ZJQTA66", "file": {"id": "F3ZJQTA66"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_shared"} diff --git a/_pytest/data/websocket/1485975956.47-message.json b/_pytest/data/websocket/1485975956.47-message.json index 03047f4..04e67a8 100644 --- a/_pytest/data/websocket/1485975956.47-message.json +++ b/_pytest/data/websocket/1485975956.47-message.json @@ -1 +1 @@ -{u'username': u'<@U407ABLLW|alice>', u'source_team': u'T3YS5EAL9', u'display_as_bot': False, u'text': u'<@U407ABLLW|alice> uploaded a file: ', u'channel': u'C407ABS94', u'user_profile': {u'avatar_hash': u'gfd0ce7a168d', u'first_name': u'First', u'real_name': u'First Testuser', u'name': u'alice', u'image_72': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png'}, u'upload': True, u'ts': u'1485975956.000011', u'subtype': u'file_share', u'user': u'U407ABLLW', u'file': {u'groups': [], u'filetype': u'png', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'thumb_64': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png', u'size': 68, u'original_h': 1, u'thumb_360_w': 1, u'title': u'1x1.png', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png', u'thumb_360': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png', u'id': u'F3ZJQTA66', u'ims': [], u'thumb_80': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png', u'thumb_360_h': 1, u'external_type': u'', u'username': u'', u'timestamp': 1485975955, u'public_url_shared': False, u'editable': False, u'thumb_160': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png', u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png', u'user': u'U407ABLLW', u'image_exif_rotation': 1, u'is_public': True, u'pretty_type': u'PNG', u'name': u'1x1.png', u'mimetype': u'image/png', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png', u'is_external': False, u'created': 1485975955, u'original_w': 1, u'comments_count': 0, u'mode': u'hosted'}, u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'user_team': u'T3YS5EAL9', u'bot_id': None} \ No newline at end of file +{"username": "<@U407ABLLW|alice>", "source_team": "T3YS5EAL9", "user_profile": {"avatar_hash": "gfd0ce7a168d", "first_name": "First", "image_72": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png", "name": "alice", "real_name": "First Testuser"}, "text": "<@U407ABLLW|alice> uploaded a file: ", "team": "T3YS5EAL9", "upload": true, "ts": "1485975956.000011", "display_as_bot": false, "user": "U407ABLLW", "file": {"image_exif_rotation": 1, "filetype": "png", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3ZJQTA66", "size": 68, "original_h": 1, "thumb_360_w": 1, "title": "1x1.png", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png", "thumb_360": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png", "thumb_64": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png", "ims": [], "thumb_80": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png", "thumb_360_h": 1, "external_type": "", "username": "", "timestamp": 1485975955, "public_url_shared": false, "editable": false, "thumb_160": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png", "user": "U407ABLLW", "groups": [], "is_public": true, "pretty_type": "PNG", "is_external": false, "mimetype": "image/png", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png", "name": "1x1.png", "created": 1485975955, "original_w": 1, "comments_count": 0, "mode": "hosted"}, "subtype": "file_share", "user_team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94", "bot_id": null} diff --git a/_pytest/data/websocket/1485975978.79-message.json b/_pytest/data/websocket/1485975978.79-message.json index 339504c..f7c1846 100644 --- a/_pytest/data/websocket/1485975978.79-message.json +++ b/_pytest/data/websocket/1485975978.79-message.json @@ -1 +1 @@ -{u'username': u'<@U407ABLLW|alice>', u'display_as_bot': False, u'text': u'<@U407ABLLW|alice> shared a file: ', u'upload': False, u'ts': u'1485975978.000012', u'subtype': u'file_share', u'user': u'U407ABLLW', u'file': {u'filetype': u'space', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'id': u'F3YTCL8TA', u'size': 73, u'title': u'some post here', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/some_post_here', u'ims': [], u'state': u'locked', u'editor': u'U407ABLLW', u'preview': None, u'external_type': u'', u'username': u'', u'updated': 1485975959, u'timestamp': 1485975967, u'public_url_shared': False, u'editable': True, u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/download/some_post_here', u'user': u'U407ABLLW', u'groups': [], u'is_public': False, u'last_editor': u'U407ABLLW', u'pretty_type': u'Post', u'name': u'some_post_here', u'mimetype': u'text/plain', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3YTCL8TA-9d9391a713', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3YTCL8TA/some_post_here', u'is_external': False, u'created': 1485975959, u'comments_count': 0, u'mode': u'space'}, u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94', u'bot_id': None} \ No newline at end of file +{"username": "<@U407ABLLW|alice>", "subtype": "file_share", "text": "<@U407ABLLW|alice> shared a file: ", "upload": false, "ts": "1485975978.000012", "display_as_bot": false, "user": "U407ABLLW", "file": {"filetype": "space", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3YTCL8TA", "size": 73, "title": "some post here", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/some_post_here", "ims": [], "state": "locked", "editor": "U407ABLLW", "preview": null, "external_type": "", "username": "", "updated": 1485975959, "timestamp": 1485975967, "public_url_shared": false, "editable": true, "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/download/some_post_here", "user": "U407ABLLW", "groups": [], "is_public": false, "last_editor": "U407ABLLW", "pretty_type": "Post", "is_external": false, "mimetype": "text/plain", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3YTCL8TA-9d9391a713", "permalink": "https://weeslacktest.slack.com/files/alice/F3YTCL8TA/some_post_here", "name": "some_post_here", "created": 1485975959, "comments_count": 0, "mode": "space"}, "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94", "bot_id": null} diff --git a/_pytest/data/websocket/1485975978.91-file_public.json b/_pytest/data/websocket/1485975978.91-file_public.json index 39ad780..820f802 100644 --- a/_pytest/data/websocket/1485975978.91-file_public.json +++ b/_pytest/data/websocket/1485975978.91-file_public.json @@ -1 +1 @@ -{u'user_id': u'U407ABLLW', u'event_ts': u'1485975978.887563', u'file_id': u'F3YTCL8TA', u'file': {u'id': u'F3YTCL8TA'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_public'} \ No newline at end of file +{"user_id": "U407ABLLW", "event_ts": "1485975978.887563", "file_id": "F3YTCL8TA", "file": {"id": "F3YTCL8TA"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_public"} diff --git a/_pytest/data/websocket/1485975978.91-file_shared.json b/_pytest/data/websocket/1485975978.91-file_shared.json index e41c2be..ede98ba 100644 --- a/_pytest/data/websocket/1485975978.91-file_shared.json +++ b/_pytest/data/websocket/1485975978.91-file_shared.json @@ -1 +1 @@ -{u'user_id': u'U407ABLLW', u'event_ts': u'1485975978.887560', u'file_id': u'F3YTCL8TA', u'file': {u'id': u'F3YTCL8TA'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_shared'} \ No newline at end of file +{"user_id": "U407ABLLW", "event_ts": "1485975978.887560", "file_id": "F3YTCL8TA", "file": {"id": "F3YTCL8TA"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_shared"} diff --git a/_pytest/data/websocket/1485975995.98-file_change.json b/_pytest/data/websocket/1485975995.98-file_change.json index ff4ea71..9744692 100644 --- a/_pytest/data/websocket/1485975995.98-file_change.json +++ b/_pytest/data/websocket/1485975995.98-file_change.json @@ -1 +1 @@ -{u'user_id': u'U407ABLLW', u'event_ts': u'1485975995.887846', u'file_id': u'F3YTCL8TA', u'file': {u'id': u'F3YTCL8TA'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_change'} \ No newline at end of file +{"user_id": "U407ABLLW", "event_ts": "1485975995.887846", "file_id": "F3YTCL8TA", "file": {"id": "F3YTCL8TA"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_change"} diff --git a/_pytest/data/websocket/1485975998.13-file_change.json b/_pytest/data/websocket/1485975998.13-file_change.json index bbbc4ec..8933dd7 100644 --- a/_pytest/data/websocket/1485975998.13-file_change.json +++ b/_pytest/data/websocket/1485975998.13-file_change.json @@ -1 +1 @@ -{u'user_id': u'U407ABLLW', u'event_ts': u'1485975998.887906', u'file_id': u'F3YTCL8TA', u'file': {u'id': u'F3YTCL8TA'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_change'} \ No newline at end of file +{"user_id": "U407ABLLW", "event_ts": "1485975998.887906", "file_id": "F3YTCL8TA", "file": {"id": "F3YTCL8TA"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_change"} diff --git a/_pytest/data/websocket/1485976002.57-file_deleted.json b/_pytest/data/websocket/1485976002.57-file_deleted.json index a4f4c71..34719d0 100644 --- a/_pytest/data/websocket/1485976002.57-file_deleted.json +++ b/_pytest/data/websocket/1485976002.57-file_deleted.json @@ -1 +1 @@ -{u'event_ts': u'1485976002.888005', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_deleted', u'file_id': u'F3YTCL8TA'} \ No newline at end of file +{"event_ts": "1485976002.888005", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_deleted", "file_id": "F3YTCL8TA"} diff --git a/_pytest/data/websocket/1485976002.66-message.json b/_pytest/data/websocket/1485976002.66-message.json index b32f31a..cb7bc08 100644 --- a/_pytest/data/websocket/1485976002.66-message.json +++ b/_pytest/data/websocket/1485976002.66-message.json @@ -1 +1 @@ -{u'event_ts': u'1485976002.888006', u'ts': u'1485976002.000013', u'subtype': u'message_deleted', u'hidden': True, u'deleted_ts': u'1485975978.000012', u'type': u'message', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'channel': u'C407ABS94', u'previous_message': {u'username': u'<@U407ABLLW|alice>', u'display_as_bot': False, u'text': u'<@U407ABLLW|alice> shared a file: ', u'upload': False, u'ts': u'1485975978.000012', u'subtype': u'file_share', u'user': u'U407ABLLW', u'file': {u'filetype': u'space', u'channels': [], u'display_as_bot': False, u'id': u'F3YTCL8TA', u'size': 73, u'title': u'some post here', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/some_post_here', u'ims': [], u'state': u'locked', u'editor': u'U407ABLLW', u'preview': None, u'external_type': u'', u'username': u'', u'updated': 1485975959, u'timestamp': 1485975967, u'public_url_shared': False, u'editable': True, u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/download/some_post_here', u'user': u'U407ABLLW', u'groups': [], u'is_public': True, u'last_editor': u'U407ABLLW', u'pretty_type': u'Post', u'name': u'some_post_here', u'mimetype': u'text/plain', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3YTCL8TA-9d9391a713', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3YTCL8TA/some_post_here', u'is_external': False, u'created': 1485975959, u'comments_count': 0, u'mode': u'space'}, u'type': u'message', u'bot_id': None}} \ No newline at end of file +{"event_ts": "1485976002.888006", "ts": "1485976002.000013", "subtype": "message_deleted", "hidden": true, "previous_message": {"username": "<@U407ABLLW|alice>", "subtype": "file_share", "text": "<@U407ABLLW|alice> shared a file: ", "upload": false, "ts": "1485975978.000012", "display_as_bot": false, "user": "U407ABLLW", "file": {"filetype": "space", "channels": [], "display_as_bot": false, "id": "F3YTCL8TA", "size": 73, "title": "some post here", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/some_post_here", "ims": [], "state": "locked", "editor": "U407ABLLW", "preview": null, "external_type": "", "username": "", "updated": 1485975959, "timestamp": 1485975967, "public_url_shared": false, "editable": true, "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/download/some_post_here", "user": "U407ABLLW", "groups": [], "is_public": true, "last_editor": "U407ABLLW", "pretty_type": "Post", "is_external": false, "mimetype": "text/plain", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3YTCL8TA-9d9391a713", "permalink": "https://weeslacktest.slack.com/files/alice/F3YTCL8TA/some_post_here", "name": "some_post_here", "created": 1485975959, "comments_count": 0, "mode": "space"}, "type": "message", "bot_id": null}, "type": "message", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "channel": "C407ABS94", "deleted_ts": "1485975978.000012"} diff --git a/_pytest/data/websocket/1485976024.46-user_typing.json b/_pytest/data/websocket/1485976024.46-user_typing.json index b909fa3..b8239d4 100644 --- a/_pytest/data/websocket/1485976024.46-user_typing.json +++ b/_pytest/data/websocket/1485976024.46-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'G3ZGMF4RZ'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "G3ZGMF4RZ"} diff --git a/_pytest/data/websocket/1485976028.31-user_typing.json b/_pytest/data/websocket/1485976028.31-user_typing.json index b7f65a8..8307f14 100644 --- a/_pytest/data/websocket/1485976028.31-user_typing.json +++ b/_pytest/data/websocket/1485976028.31-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'G3ZJKP7GA'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "G3ZJKP7GA"} diff --git a/_pytest/data/websocket/1485976039.32-reaction_added.json b/_pytest/data/websocket/1485976039.32-reaction_added.json index acccc65..864ea68 100644 --- a/_pytest/data/websocket/1485976039.32-reaction_added.json +++ b/_pytest/data/websocket/1485976039.32-reaction_added.json @@ -1 +1 @@ -{u'reaction': u'unamused', u'event_ts': u'1485976039.888795', u'item': {u'type': u'message', u'ts': u'1485975841.000007', u'channel': u'C407ABS94'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_added'} \ No newline at end of file +{"reaction": "unamused", "event_ts": "1485976039.888795", "item": {"type": "message", "ts": "1485975841.000007", "channel": "C407ABS94"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_added"} diff --git a/_pytest/data/websocket/1485976040.8-reaction_removed.json b/_pytest/data/websocket/1485976040.8-reaction_removed.json index 2f39d43..3553a10 100644 --- a/_pytest/data/websocket/1485976040.8-reaction_removed.json +++ b/_pytest/data/websocket/1485976040.8-reaction_removed.json @@ -1 +1 @@ -{u'reaction': u'unamused', u'event_ts': u'1485976040.888829', u'item': {u'type': u'message', u'ts': u'1485975841.000007', u'channel': u'C407ABS94'}, u'user': u'U407ABLLW', u'item_user': u'U407ABLLW', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reaction_removed'} \ No newline at end of file +{"reaction": "unamused", "event_ts": "1485976040.888829", "item": {"type": "message", "ts": "1485975841.000007", "channel": "C407ABS94"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_removed"} diff --git a/_pytest/data/websocket/1485976087.84-reconnect_url.json b/_pytest/data/websocket/1485976087.84-reconnect_url.json index 018c2c5..40308ce 100644 --- a/_pytest/data/websocket/1485976087.84-reconnect_url.json +++ b/_pytest/data/websocket/1485976087.84-reconnect_url.json @@ -1 +1 @@ -{u'url': u'wss://mpmulti-clcz.slack-msgs.com/websocket/ZwLc8-LUIYLyvEwRO5WKs99SLtxBN9Cyu1W5qmqkre0AtWFl_H8xTE7HbF0E9LUvgI4bNkIRG8WgPxIzfr5gLJsU6vg8By36_mUUZYfH0TV65y_h2vV7NQe6s3A6WJdKRoJAe_EI2AN5L-VeL9rK1Ygc0nw3ngmuL78G6cm48xw=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file +{"url": "wss://mpmulti-clcz.slack-msgs.com/websocket/ZwLc8-LUIYLyvEwRO5WKs99SLtxBN9Cyu1W5qmqkre0AtWFl_H8xTE7HbF0E9LUvgI4bNkIRG8WgPxIzfr5gLJsU6vg8By36_mUUZYfH0TV65y_h2vV7NQe6s3A6WJdKRoJAe_EI2AN5L-VeL9rK1Ygc0nw3ngmuL78G6cm48xw=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} diff --git a/_pytest/data/websocket/1485976107.57-channel_created.json b/_pytest/data/websocket/1485976107.57-channel_created.json index fed2059..b061743 100644 --- a/_pytest/data/websocket/1485976107.57-channel_created.json +++ b/_pytest/data/websocket/1485976107.57-channel_created.json @@ -1 +1 @@ -{u'event_ts': u'1485976107.890695', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_created', u'channel': {u'is_shared': False, u'is_channel': True, u'created': 1485976107, u'creator': u'U407ABLLW', u'is_org_shared': False, u'id': u'C3ZM2GMGU', u'name': u'made-to-be-archived'}} \ No newline at end of file +{"event_ts": "1485976107.890695", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_created", "channel": {"is_shared": false, "is_channel": true, "creator": "U407ABLLW", "created": 1485976107, "is_org_shared": false, "id": "C3ZM2GMGU", "name": "made-to-be-archived"}} diff --git a/_pytest/data/websocket/1485976114.72-user_typing.json b/_pytest/data/websocket/1485976114.72-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976114.72-user_typing.json +++ b/_pytest/data/websocket/1485976114.72-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976117.99-user_typing.json b/_pytest/data/websocket/1485976117.99-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976117.99-user_typing.json +++ b/_pytest/data/websocket/1485976117.99-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976124.9-user_typing.json b/_pytest/data/websocket/1485976124.9-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976124.9-user_typing.json +++ b/_pytest/data/websocket/1485976124.9-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976124.98-message.json b/_pytest/data/websocket/1485976124.98-message.json index 95a8d77..26ce4e5 100644 --- a/_pytest/data/websocket/1485976124.98-message.json +++ b/_pytest/data/websocket/1485976124.98-message.json @@ -1 +1 @@ -{u'text': u'referencing someong by <@U3ZKBBDL5> name', u'ts': u'1485976124.000014', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file +{"text": "referencing someong by <@U3ZKBBDL5> name", "ts": "1485976124.000014", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976125.06-desktop_notification.json b/_pytest/data/websocket/1485976125.06-desktop_notification.json index 3a57bc0..ddd6dff 100644 --- a/_pytest/data/websocket/1485976125.06-desktop_notification.json +++ b/_pytest/data/websocket/1485976125.06-desktop_notification.json @@ -1 +1 @@ -{u'launchUri': u'slack://channel?id=C407ABS94&message=1485976124000014&team=T3YS5EAL9', u'subtitle': u'#general', u'is_shared': False, u'title': u'weeslacktest', u'ssbFilename': u'knock_brush.mp3', u'avatarImage': u'https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png', u'imageUri': None, u'content': u'alice: referencing someong by @bob name', u'event_ts': u'1485976124.891186', u'msg': u'1485976124.000014', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'desktop_notification', u'channel': u'C407ABS94'} \ No newline at end of file +{"avatarImage": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png", "subtitle": "#general", "is_shared": false, "title": "weeslacktest", "ssbFilename": "knock_brush.mp3", "imageUri": null, "launchUri": "slack://channel?id=C407ABS94&message=1485976124000014&team=T3YS5EAL9", "event_ts": "1485976124.891186", "msg": "1485976124.000014", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "content": "alice: referencing someong by @bob name", "type": "desktop_notification", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976125.95-user_typing.json b/_pytest/data/websocket/1485976125.95-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976125.95-user_typing.json +++ b/_pytest/data/websocket/1485976125.95-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976129.49-user_typing.json b/_pytest/data/websocket/1485976129.49-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976129.49-user_typing.json +++ b/_pytest/data/websocket/1485976129.49-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976133.0-user_typing.json b/_pytest/data/websocket/1485976133.0-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976133.0-user_typing.json +++ b/_pytest/data/websocket/1485976133.0-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976137.51-user_typing.json b/_pytest/data/websocket/1485976137.51-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976137.51-user_typing.json +++ b/_pytest/data/websocket/1485976137.51-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976137.95-message.json b/_pytest/data/websocket/1485976137.95-message.json index 715e4e4..f4579e5 100644 --- a/_pytest/data/websocket/1485976137.95-message.json +++ b/_pytest/data/websocket/1485976137.95-message.json @@ -1 +1 @@ -{u'text': u'referencing someone else by <@U407ABLLW> name', u'ts': u'1485976137.000015', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file +{"text": "referencing someone else by <@U407ABLLW> name", "ts": "1485976137.000015", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976138.73-user_typing.json b/_pytest/data/websocket/1485976138.73-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976138.73-user_typing.json +++ b/_pytest/data/websocket/1485976138.73-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976141.79-user_typing.json b/_pytest/data/websocket/1485976141.79-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976141.79-user_typing.json +++ b/_pytest/data/websocket/1485976141.79-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976145.8-user_typing.json b/_pytest/data/websocket/1485976145.8-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976145.8-user_typing.json +++ b/_pytest/data/websocket/1485976145.8-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976149.89-user_typing.json b/_pytest/data/websocket/1485976149.89-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976149.89-user_typing.json +++ b/_pytest/data/websocket/1485976149.89-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976151.6-message.json b/_pytest/data/websocket/1485976151.6-message.json index a2f431e..7efa65d 100644 --- a/_pytest/data/websocket/1485976151.6-message.json +++ b/_pytest/data/websocket/1485976151.6-message.json @@ -1 +1 @@ -{u'text': u'referencing a #channel-that-doesnt-exist', u'ts': u'1485976151.000016', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file +{"text": "referencing a #channel-that-doesnt-exist", "ts": "1485976151.000016", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976157.18-message.json b/_pytest/data/websocket/1485976157.18-message.json index 96c82f6..62244b4 100644 --- a/_pytest/data/websocket/1485976157.18-message.json +++ b/_pytest/data/websocket/1485976157.18-message.json @@ -1 +1 @@ -{u'event_ts': u'1485976156.891735', u'ts': u'1485976156.000017', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'subtype': u'message_changed', u'message': {u'text': u'referencing a <#C407ABS94|general>', u'type': u'message', u'user': u'U407ABLLW', u'ts': u'1485976151.000016', u'edited': {u'user': u'U407ABLLW', u'ts': u'1485976157.000000'}}, u'type': u'message', u'hidden': True, u'channel': u'C407ABS94', u'previous_message': {u'text': u'referencing a #channel-that-doesnt-exist', u'type': u'message', u'user': u'U407ABLLW', u'ts': u'1485976151.000016'}} \ No newline at end of file +{"hidden": true, "event_ts": "1485976156.891735", "ts": "1485976156.000017", "subtype": "message_changed", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "message": {"text": "referencing a <#C407ABS94|general>", "edited": {"user": "U407ABLLW", "ts": "1485976157.000000"}, "type": "message", "user": "U407ABLLW", "ts": "1485976151.000016"}, "channel": "C407ABS94", "previous_message": {"text": "referencing a #channel-that-doesnt-exist", "type": "message", "user": "U407ABLLW", "ts": "1485976151.000016"}} diff --git a/_pytest/data/websocket/1485976157.8-user_typing.json b/_pytest/data/websocket/1485976157.8-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976157.8-user_typing.json +++ b/_pytest/data/websocket/1485976157.8-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976161.29-user_typing.json b/_pytest/data/websocket/1485976161.29-user_typing.json index 4e53cd4..df57e72 100644 --- a/_pytest/data/websocket/1485976161.29-user_typing.json +++ b/_pytest/data/websocket/1485976161.29-user_typing.json @@ -1 +1 @@ -{'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'user_typing', u'user': u'U407ABLLW', u'channel': u'C407ABS94'} \ No newline at end of file +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976161.75-message.json b/_pytest/data/websocket/1485976161.75-message.json index 9ed97d2..f0b899b 100644 --- a/_pytest/data/websocket/1485976161.75-message.json +++ b/_pytest/data/websocket/1485976161.75-message.json @@ -1 +1 @@ -{u'text': u'referencing <#C3ZM8JTD3|some-channel2-renamed>', u'ts': u'1485976161.000018', u'user': u'U407ABLLW', u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file +{"text": "referencing <#C3ZM8JTD3|some-channel2-renamed>", "ts": "1485976161.000018", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976182.59-channel_archive.json b/_pytest/data/websocket/1485976182.59-channel_archive.json index 66ce021..4ebdb1f 100644 --- a/_pytest/data/websocket/1485976182.59-channel_archive.json +++ b/_pytest/data/websocket/1485976182.59-channel_archive.json @@ -1 +1 @@ -{u'event_ts': u'1485976182.892242', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_archive', u'user': u'U407ABLLW', u'channel': u'C3ZM2GMGU'} \ No newline at end of file +{"event_ts": "1485976182.892242", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_archive", "user": "U407ABLLW", "channel": "C3ZM2GMGU"} diff --git a/_pytest/data/websocket/1485976186.62-channel_unarchive.json b/_pytest/data/websocket/1485976186.62-channel_unarchive.json index 67f15d7..c95897d 100644 --- a/_pytest/data/websocket/1485976186.62-channel_unarchive.json +++ b/_pytest/data/websocket/1485976186.62-channel_unarchive.json @@ -1 +1 @@ -{u'event_ts': u'1485976186.892309', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'channel_unarchive', u'user': u'U407ABLLW', u'channel': u'C3ZM2GMGU'} \ No newline at end of file +{"event_ts": "1485976186.892309", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_unarchive", "user": "U407ABLLW", "channel": "C3ZM2GMGU"} diff --git a/_pytest/data/websocket/1485976236.58-message.json b/_pytest/data/websocket/1485976236.58-message.json index ea7ecef..f09b294 100644 --- a/_pytest/data/websocket/1485976236.58-message.json +++ b/_pytest/data/websocket/1485976236.58-message.json @@ -1 +1 @@ -{u'comment': {u'comment': u'first comment!', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'text': u'<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment!', u'ts': u'1485976236.000019', u'subtype': u'file_comment', u'is_intro': False, u'file': {u'groups': [], u'filetype': u'png', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'thumb_64': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png', u'size': 68, u'original_h': 1, u'thumb_360_w': 1, u'title': u'1x1.png', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png', u'thumb_360': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png', u'id': u'F3ZJQTA66', u'ims': [], u'thumb_80': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png', u'thumb_360_h': 1, u'external_type': u'', u'username': u'', u'timestamp': 1485975955, u'public_url_shared': False, u'editable': False, u'thumb_160': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png', u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png', u'user': u'U407ABLLW', u'image_exif_rotation': 1, u'is_public': True, u'pretty_type': u'PNG', u'name': u'1x1.png', u'mimetype': u'image/png', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png', u'is_external': False, u'created': 1485975955, u'original_w': 1, u'comments_count': 1, u'mode': u'hosted'}, u'team': u'T3YS5EAL9', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'message', u'channel': u'C407ABS94'} \ No newline at end of file +{"comment": {"comment": "first comment!", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment!", "ts": "1485976236.000019", "subtype": "file_comment", "is_intro": false, "file": {"image_exif_rotation": 1, "filetype": "png", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3ZJQTA66", "size": 68, "original_h": 1, "thumb_360_w": 1, "title": "1x1.png", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png", "thumb_360": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png", "thumb_64": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png", "ims": [], "thumb_80": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png", "thumb_360_h": 1, "external_type": "", "username": "", "timestamp": 1485975955, "public_url_shared": false, "editable": false, "thumb_160": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png", "user": "U407ABLLW", "groups": [], "is_public": true, "pretty_type": "PNG", "is_external": false, "mimetype": "image/png", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png", "name": "1x1.png", "created": 1485975955, "original_w": 1, "comments_count": 1, "mode": "hosted"}, "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976236.67-file_comment_added.json b/_pytest/data/websocket/1485976236.67-file_comment_added.json index 13272a8..82c1f0c 100644 --- a/_pytest/data/websocket/1485976236.67-file_comment_added.json +++ b/_pytest/data/websocket/1485976236.67-file_comment_added.json @@ -1 +1 @@ -{u'comment': {u'comment': u'first comment!', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'user_id': u'U407ABLLW', u'event_ts': u'1485976236.893560', u'file_id': u'F3ZJQTA66', u'file': {u'id': u'F3ZJQTA66'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_comment_added'} \ No newline at end of file +{"comment": {"comment": "first comment!", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "user_id": "U407ABLLW", "event_ts": "1485976236.893560", "file_id": "F3ZJQTA66", "file": {"id": "F3ZJQTA66"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_comment_added"} diff --git a/_pytest/data/websocket/1485976248.57-message.json b/_pytest/data/websocket/1485976248.57-message.json index 797e0dc..d3c8ce6 100644 --- a/_pytest/data/websocket/1485976248.57-message.json +++ b/_pytest/data/websocket/1485976248.57-message.json @@ -1 +1 @@ -{u'event_ts': u'1485976248.893799', u'ts': u'1485976248.000020', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'subtype': u'message_changed', u'message': {u'comment': {u'comment': u'first comment! now i edited it.', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'text': u'<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.', u'ts': u'1485976236.000019', u'subtype': u'file_comment', u'is_intro': False, u'file': {u'groups': [], u'filetype': u'png', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'thumb_64': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png', u'size': 68, u'original_h': 1, u'thumb_360_w': 1, u'title': u'1x1.png', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png', u'thumb_360': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png', u'id': u'F3ZJQTA66', u'ims': [], u'thumb_80': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png', u'thumb_360_h': 1, u'external_type': u'', u'username': u'', u'timestamp': 1485975955, u'public_url_shared': False, u'editable': False, u'thumb_160': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png', u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png', u'user': u'U407ABLLW', u'image_exif_rotation': 1, u'is_public': True, u'pretty_type': u'PNG', u'name': u'1x1.png', u'mimetype': u'image/png', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png', u'is_external': False, u'created': 1485975955, u'original_w': 1, u'comments_count': 1, u'mode': u'hosted'}, u'type': u'message'}, u'type': u'message', u'hidden': True, u'channel': u'C407ABS94', u'previous_message': {u'comment': {u'comment': u'first comment! now i edited it.', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'text': u'<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.', u'ts': u'1485976236.000019', u'subtype': u'file_comment', u'is_intro': False, u'file': {u'groups': [], u'filetype': u'png', u'channels': [u'C407ABS94'], u'display_as_bot': False, u'thumb_64': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png', u'size': 68, u'original_h': 1, u'thumb_360_w': 1, u'title': u'1x1.png', u'url_private': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png', u'thumb_360': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png', u'id': u'F3ZJQTA66', u'ims': [], u'thumb_80': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png', u'thumb_360_h': 1, u'external_type': u'', u'username': u'', u'timestamp': 1485975955, u'public_url_shared': False, u'editable': False, u'thumb_160': u'https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png', u'url_private_download': u'https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png', u'user': u'U407ABLLW', u'image_exif_rotation': 1, u'is_public': True, u'pretty_type': u'PNG', u'name': u'1x1.png', u'mimetype': u'image/png', u'permalink_public': u'https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2', u'permalink': u'https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png', u'is_external': False, u'created': 1485975955, u'original_w': 1, u'comments_count': 1, u'mode': u'hosted'}, u'type': u'message'}} \ No newline at end of file +{"hidden": true, "event_ts": "1485976248.893799", "ts": "1485976248.000020", "subtype": "message_changed", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "message": {"comment": {"comment": "first comment! now i edited it.", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.", "ts": "1485976236.000019", "subtype": "file_comment", "is_intro": false, "file": {"image_exif_rotation": 1, "filetype": "png", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3ZJQTA66", "size": 68, "original_h": 1, "thumb_360_w": 1, "title": "1x1.png", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png", "thumb_360": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png", "thumb_64": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png", "ims": [], "thumb_80": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png", "thumb_360_h": 1, "external_type": "", "username": "", "timestamp": 1485975955, "public_url_shared": false, "editable": false, "thumb_160": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png", "user": "U407ABLLW", "groups": [], "is_public": true, "pretty_type": "PNG", "is_external": false, "mimetype": "image/png", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png", "name": "1x1.png", "created": 1485975955, "original_w": 1, "comments_count": 1, "mode": "hosted"}, "type": "message"}, "channel": "C407ABS94", "previous_message": {"comment": {"comment": "first comment! now i edited it.", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.", "ts": "1485976236.000019", "subtype": "file_comment", "is_intro": false, "file": {"image_exif_rotation": 1, "filetype": "png", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3ZJQTA66", "size": 68, "original_h": 1, "thumb_360_w": 1, "title": "1x1.png", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png", "thumb_360": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png", "thumb_64": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png", "ims": [], "thumb_80": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png", "thumb_360_h": 1, "external_type": "", "username": "", "timestamp": 1485975955, "public_url_shared": false, "editable": false, "thumb_160": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png", "user": "U407ABLLW", "groups": [], "is_public": true, "pretty_type": "PNG", "is_external": false, "mimetype": "image/png", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png", "name": "1x1.png", "created": 1485975955, "original_w": 1, "comments_count": 1, "mode": "hosted"}, "type": "message"}} diff --git a/_pytest/data/websocket/1485976248.65-file_comment_edited.json b/_pytest/data/websocket/1485976248.65-file_comment_edited.json index f944ede..3e7c528 100644 --- a/_pytest/data/websocket/1485976248.65-file_comment_edited.json +++ b/_pytest/data/websocket/1485976248.65-file_comment_edited.json @@ -1 +1 @@ -{u'comment': {u'comment': u'first comment! now i edited it.', u'created': 1485976236, u'timestamp': 1485976236, u'is_intro': False, u'user': u'U407ABLLW', u'id': u'Fc3ZMDRQLV', u'channel': u''}, u'user_id': u'U407ABLLW', u'event_ts': u'1485976248.893801', u'file_id': u'F3ZJQTA66', u'file': {u'id': u'F3ZJQTA66'}, 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'file_comment_edited'} \ No newline at end of file +{"comment": {"comment": "first comment! now i edited it.", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "user_id": "U407ABLLW", "event_ts": "1485976248.893801", "file_id": "F3ZJQTA66", "file": {"id": "F3ZJQTA66"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_comment_edited"} diff --git a/_pytest/data/websocket/1485976267.81-reconnect_url.json b/_pytest/data/websocket/1485976267.81-reconnect_url.json index c373cbc..55533ab 100644 --- a/_pytest/data/websocket/1485976267.81-reconnect_url.json +++ b/_pytest/data/websocket/1485976267.81-reconnect_url.json @@ -1 +1 @@ -{u'url': u'wss://mpmulti-m00z.slack-msgs.com/websocket/fZI1FV2ZuMYXeQLMKi1SrH2j69xL6m_6hC38CVCr0ugPFpMS1IBxlCvHJYbSrH-fMhSuLmbkEd1te_ND5Q-EyVVX-w06mn5NLZM9GaX_mWb9A3w79sThYzEgnnQ8onoeFM2CWgFNxWM_3XS4HJaWXeee-_sNh_booNbby8jm9mg=', 'wee_slack_metadata': {'team': 'd80c2b6c3127dbb1991917394ed219e8212a2606'}, u'type': u'reconnect_url'} \ No newline at end of file +{"url": "wss://mpmulti-m00z.slack-msgs.com/websocket/fZI1FV2ZuMYXeQLMKi1SrH2j69xL6m_6hC38CVCr0ugPFpMS1IBxlCvHJYbSrH-fMhSuLmbkEd1te_ND5Q-EyVVX-w06mn5NLZM9GaX_mWb9A3w79sThYzEgnnQ8onoeFM2CWgFNxWM_3XS4HJaWXeee-_sNh_booNbby8jm9mg=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} -- cgit From 681dbc193bae47429662a9504d75768ab1fdda4c Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 11:58:13 -0800 Subject: add first basic tests --- _pytest/conftest.py | 84 +++++++++++++++++++++++++++++++++++++++++ _pytest/test_eventrouter.py | 76 +++++++++++++++++++++++++++++++++++++ _pytest/test_linkifytext.py | 6 +++ _pytest/test_presencechange.py | 31 +++++++++++++++ _pytest/test_process_message.py | 44 +++++++++++++++++++++ _pytest/test_processreply.py | 33 ++++++++++++++++ _pytest/test_sendmessage.py | 21 +++++++++++ _pytest/test_slackrequest.py | 11 ++++++ _pytest/test_slackteam.py | 10 +++++ _pytest/test_unfurl.py | 53 ++++++++++++++++++++++++++ wee_slack.py | 2 +- 11 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 _pytest/conftest.py create mode 100644 _pytest/test_eventrouter.py create mode 100644 _pytest/test_linkifytext.py create mode 100644 _pytest/test_presencechange.py create mode 100644 _pytest/test_process_message.py create mode 100644 _pytest/test_processreply.py create mode 100644 _pytest/test_sendmessage.py create mode 100644 _pytest/test_slackrequest.py create mode 100644 _pytest/test_slackteam.py create mode 100644 _pytest/test_unfurl.py diff --git a/_pytest/conftest.py b/_pytest/conftest.py new file mode 100644 index 0000000..0212601 --- /dev/null +++ b/_pytest/conftest.py @@ -0,0 +1,84 @@ +import pickle +import json +import pytest +import sys + +sys.path.append(".") + +#New stuff +from wee_slack import EventRouter +from wee_slack import SlackRequest +import wee_slack + +class fakewebsocket(object): + def __init__(self): + self.returndata = [] + pass + def add(self, data): + self.returndata.append(data) + def recv(self): + return json.dumps(self.returndata.pop(0)) + def send(self, data): + print "websocket received: {}".format(data) + return + +@pytest.fixture +def mock_websocket(): + return fakewebsocket() + +@pytest.fixture +def realish_eventrouter(): + e = EventRouter() + pickled_data = pickle.dumps(SlackRequest('xoxoxoxox', "rtm.start", {"meh": "blah"})) + rtmstartdata = open('_pytest/data/http/rtm.start.json', 'r').read() + e.receive_httprequest_callback(pickled_data, 1, 0, rtmstartdata, 4) + e.handle_next() + return e + + +class FakeWeechat(): + """ + this is the thing that acts as "w." everywhere.. + basically mock out all of the weechat calls here i guess + """ + WEECHAT_RC_OK = True + + def __init__(self): + pass + #print "INITIALIZE FAKE WEECHAT" + def prnt(*args): + output = "(" + for arg in args: + if arg != None: + output += "{}, ".format(arg) + print "w.prnt {}".format(output) + def hdata_get(*args): + return "0x000001" + def hdata_pointer(*args): + return "0x000002" + def hdata_time(*args): + return "1355517519" + def hdata_string(*args): + return "testuser" + def buffer_new(*args): + return "0x8a8a8a8b" + + def __getattr__(self, name): + def method(*args): + pass + #print "called {}".format(name) + #if args: + # print "\twith args: {}".format(args) + return method + +@pytest.fixture +def mock_weechat(): + wee_slack.w = FakeWeechat() + wee_slack.config = wee_slack.PluginConfig() + wee_slack.debug_string = None + wee_slack.slack_debug = "debug_buffer_ptr" + wee_slack.STOP_TALKING_TO_SLACK = False + wee_slack.proc = {} + pass + + diff --git a/_pytest/test_eventrouter.py b/_pytest/test_eventrouter.py new file mode 100644 index 0000000..fc6a6a9 --- /dev/null +++ b/_pytest/test_eventrouter.py @@ -0,0 +1,76 @@ +import pytest +import pickle +from wee_slack import EventRouter, ProcessNotImplemented, SlackRequest + +def test_EventRouter(mock_weechat): + # Sending valid json adds to the queue. + e = EventRouter() + e.receive_json('{}') + assert len(e.queue) == 1 + + # Handling an event removes from the queue. + e = EventRouter() + # Create a function to test we are called + e.proc['testfunc'] = lambda x, y: x + e.receive_json('{"type": "testfunc"}') + e.handle_next() + assert len(e.queue) == 0 + + # Handling a local event removes from the queue. + e = EventRouter() + # Create a function to test we are called + e.proc['local_testfunc'] = lambda x, y: x + e.receive_json('{"type": "local_testfunc"}') + e.handle_next() + assert len(e.queue) == 0 + + # Handling an event without an associated processor + # raises an exception. + e = EventRouter() + # Create a function to test we are called + e.receive_json('{"type": "testfunc"}') + with pytest.raises(ProcessNotImplemented): + e.handle_next() + +def test_EventRouterReceivedata(mock_weechat): + + e = EventRouter() + pickled_data = pickle.dumps(SlackRequest('xoxoxoxox', "rtm.startold", {"meh": "blah"})) + e.receive_httprequest_callback(pickled_data, 1, -1, ' {"JSON": "MEH", ', 4) + #print len(e.reply_buffer) + e.receive_httprequest_callback(pickled_data, 1, -1, ' "JSON2": "MEH", ', 4) + #print len(e.reply_buffer) + e.receive_httprequest_callback(pickled_data, 1, 0, ' "JSON3": "MEH"}', 4) + #print len(e.reply_buffer) + try: + e.handle_next() + except: + pass + + pickled_data = pickle.dumps(SlackRequest('xoxoxoxox', "rtm.start", {"meh": "blah"})) + rtmstartdata = open('_pytest/data/http/rtm.start.json', 'r').read() + e.receive_httprequest_callback(pickled_data, 1, 0, rtmstartdata[:5000], 4) + e.receive_httprequest_callback(pickled_data, 1, 0, rtmstartdata[5000:], 4) + e.handle_next() + + #print len(e.reply_buffer) + + #print e.teams + + for t in e.teams: + #print vars(e.teams[t]) + for c in e.teams[t].channels: + pass + #print c + for u in e.teams[t].users: + pass + #print vars(u) + + +# e = EventRouter() +# # Create a function to test we are called +# e.receive_json('{"type": "message"}') +# e.handle_next() +# assert False + + #assert False diff --git a/_pytest/test_linkifytext.py b/_pytest/test_linkifytext.py new file mode 100644 index 0000000..f9da3f9 --- /dev/null +++ b/_pytest/test_linkifytext.py @@ -0,0 +1,6 @@ +from wee_slack import linkify_text + +#def test_linkifytext(): +# linkify_text('@ryan') + +# assert False diff --git a/_pytest/test_presencechange.py b/_pytest/test_presencechange.py new file mode 100644 index 0000000..b4202fa --- /dev/null +++ b/_pytest/test_presencechange.py @@ -0,0 +1,31 @@ + +def test_PresenceChange(monkeypatch, realish_eventrouter, mock_websocket): + + e = realish_eventrouter + + t = e.teams.keys()[0] + u = e.teams[t].users.keys()[0] + + user = e.teams[t].users[u] + + socket = mock_websocket + e.teams[t].ws = socket + + socket.add({ + "type": "presence_change", + "user": user.identifier, + "presence": "active", + }) + socket.add({ + "type": "presence_change", + "user": user.identifier, + "presence": "away", + }) + + e.receive_ws_callback(t) + e.handle_next() + assert e.teams[t].users[u].presence == "active" + + e.receive_ws_callback(t) + e.handle_next() + assert e.teams[t].users[u].presence == "away" diff --git a/_pytest/test_process_message.py b/_pytest/test_process_message.py new file mode 100644 index 0000000..9d238d6 --- /dev/null +++ b/_pytest/test_process_message.py @@ -0,0 +1,44 @@ +import json + +from wee_slack import render + +def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): + + e = realish_eventrouter + + t = e.teams.keys()[0] + u = e.teams[t].users.keys()[0] + + user = e.teams[t].users[u] + #print user + + socket = mock_websocket + e.teams[t].ws = socket + + messages = [] + messages.append(json.loads(open('_pytest/data/websocket/1485975421.33-message.json', 'r').read())) + + # test message and then change + messages.append(json.loads(open('_pytest/data/websocket/1485976157.18-message.json', 'r').read())) + messages.append(json.loads(open('_pytest/data/websocket/1485976151.6-message.json', 'r').read())) + + # test message then deletion + messages.append(json.loads(open('_pytest/data/websocket/1485975698.45-message.json', 'r').read())) + messages.append(json.loads(open('_pytest/data/websocket/1485975723.85-message.json', 'r').read())) + + for m in messages: + m["user"] = user.id + socket.add(m) + + e.receive_ws_callback(t) + e.handle_next() + + e.receive_ws_callback(t) + e.handle_next() + + e.receive_ws_callback(t) + e.handle_next() + + e.receive_ws_callback(t) + e.handle_next() + diff --git a/_pytest/test_processreply.py b/_pytest/test_processreply.py new file mode 100644 index 0000000..a725f23 --- /dev/null +++ b/_pytest/test_processreply.py @@ -0,0 +1,33 @@ +#from wee_slack import process_reply + +def test_process_reply(monkeypatch, realish_eventrouter, mock_websocket): + + e = realish_eventrouter + + t = e.teams.keys()[0] + #u = e.teams[t].users.keys()[0] + + #user = e.teams[t].users[u] + #print user + + socket = mock_websocket + e.teams[t].ws = socket + + c = e.teams[t].channels.keys()[0] + channel = e.teams[t].channels[c] + channel.send_message('asdf') + + socket = mock_websocket + socket.add({"reply_to": 1, "_team": t, "ts": "12341234.111"}) + + print e.teams[t].ws_replies + + e.receive_ws_callback(t) + e.handle_next() + + #reply = {"reply_to": 1, "_team": t, "ts": "12341234.111"} + #print reply + #process_reply(reply, e) + #print e.teams[t].ws_replies + #assert False + pass diff --git a/_pytest/test_sendmessage.py b/_pytest/test_sendmessage.py new file mode 100644 index 0000000..a87942d --- /dev/null +++ b/_pytest/test_sendmessage.py @@ -0,0 +1,21 @@ + +def test_send_message(monkeypatch, realish_eventrouter, mock_websocket): + e = realish_eventrouter + + t = e.teams.keys()[0] + #u = e.teams[t].users.keys()[0] + + #user = e.teams[t].users[u] + #print user + + socket = mock_websocket + e.teams[t].ws = socket + + c = e.teams[t].channels.keys()[0] + + channel = e.teams[t].channels[c] + channel.send_message('asdf') + + print c + + #assert False diff --git a/_pytest/test_slackrequest.py b/_pytest/test_slackrequest.py new file mode 100644 index 0000000..081330e --- /dev/null +++ b/_pytest/test_slackrequest.py @@ -0,0 +1,11 @@ +from wee_slack import SlackRequest, EventRouter + +def test_SlackRequest(): + s = SlackRequest('xoxoxoxox', "blah.get", {"meh": "blah"}) + print s + + e = EventRouter() + e.receive(s) + e.handle_next() + #assert False + diff --git a/_pytest/test_slackteam.py b/_pytest/test_slackteam.py new file mode 100644 index 0000000..fa6ac6a --- /dev/null +++ b/_pytest/test_slackteam.py @@ -0,0 +1,10 @@ +from wee_slack import SlackTeam, EventRouter + +def test_SlackTeam(): + e = EventRouter() + #s = SlackTeam('xoxo') + #e.register_team(s) + #print e.queue + #print e.handle_next() + #assert False + diff --git a/_pytest/test_unfurl.py b/_pytest/test_unfurl.py new file mode 100644 index 0000000..b631888 --- /dev/null +++ b/_pytest/test_unfurl.py @@ -0,0 +1,53 @@ +import wee_slack +import pytest + +slack = wee_slack + + +@pytest.mark.parametrize('case', ( + { + 'input': "foo", + 'output': "foo", + }, + { + 'input': "<@U2147483697|@othernick>: foo", + 'output': "@testuser: foo", + 'ignore_alt_text': True, + }, + { + 'input': "foo <#C2147483705|#otherchannel> foo", + 'output': "foo #otherchannel foo", + }, + { + 'input': "foo <#C2147483705> foo", + 'output': "foo #test-chan foo", + }, + { + 'input': "url: suffix", + 'output': "url: https://example.com (example) suffix", + }, + { + 'input': "url: suffix", + 'output': "url: https://example.com (example with spaces) suffix", + }, + { + 'input': "<@U2147483697|@othernick> multiple unfurl ", + 'output': "@othernick multiple unfurl https://example.com (example with spaces)", + }, + { + 'input': "try the #test-chan channel", + 'output': "try the #test-chan channel", + }, +)) +def test_unfurl_refs(case): + pass + #print myslack + #slack.servers = myslack.server + #slack.channels = myslack.channel + #slack.users = myslack.user + #slack.message_cache = {} + #slack.servers[0].users = myslack.user + #print myslack.channel[0].identifier + + #assert slack.unfurl_refs(case['input'], ignore_alt_text=case.get('ignore_alt_text', False)) == case['output'] + diff --git a/wee_slack.py b/wee_slack.py index 51c12b6..2d059a1 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -591,7 +591,7 @@ class SlackChannel(object): m.change_text(text) if suffix: m.change_suffix(suffix) - text = m.render(force=True) + text = m.render(force=True) timestamp, time_id = ts.split(".", 2) timestamp = int(timestamp) modify_buffer_line(self.channel_buffer, text, timestamp, time_id) -- cgit From aef1083ed314295d50d19ca1db37dc2994fdbc5d Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:17:59 -0800 Subject: run every json file through eventrouter and see which events we don't handle --- _pytest/test_everything.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 _pytest/test_everything.py diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py new file mode 100644 index 0000000..da5b500 --- /dev/null +++ b/_pytest/test_everything.py @@ -0,0 +1,47 @@ +import glob +import json + +#from wee_slack import render +from wee_slack import ProcessNotImplemented + +def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): + + eventrouter = realish_eventrouter + + t = eventrouter.teams.keys()[0] + #u = eventrouter.teams[t].users.keys()[0] + + #user = eventrouter.teams[t].users[u] + #print user + + socket = mock_websocket + eventrouter.teams[t].ws = socket + + datafiles = glob.glob("_pytest/data/websocket/*.json") + + print datafiles + #assert False + + notimplemented = [] + + for fname in datafiles: + try: + data = json.loads(open(fname, 'r').read()) + socket.add(data) + eventrouter.receive_ws_callback(t) + eventrouter.handle_next() + except ProcessNotImplemented as e: + notimplemented += [str(e)] + #this handles some message data not existing - need to fix + except KeyError: + pass + + if len(notimplemented) > 0: + print "####################" + print notimplemented + print "####################" + assert False + + + + -- cgit From a84c7a06aa17c018acaac44fe72270fcbb4c4357 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:18:43 -0800 Subject: move assertion --- _pytest/test_everything.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index da5b500..e65b3d8 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -40,7 +40,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): print "####################" print notimplemented print "####################" - assert False + assert False -- cgit From ad6277ba742e0a9e1a751df2f4a7c395273b1b63 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:21:52 -0800 Subject: use a set --- _pytest/test_everything.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index e65b3d8..0c04213 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -22,7 +22,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): print datafiles #assert False - notimplemented = [] + notimplemented = set() for fname in datafiles: try: @@ -31,7 +31,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): eventrouter.receive_ws_callback(t) eventrouter.handle_next() except ProcessNotImplemented as e: - notimplemented += [str(e)] + notimplemented.add(str(e)) #this handles some message data not existing - need to fix except KeyError: pass -- cgit From 96dca650f88d8fe9c596f77b1cacb9272a44b9b7 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:22:27 -0800 Subject: and make it alphabetical --- _pytest/test_everything.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index 0c04213..9e356be 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -38,7 +38,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): if len(notimplemented) > 0: print "####################" - print notimplemented + print sorted(notimplemented) print "####################" assert False -- cgit From 6ba0f69385784d7695b8daa2e9ac15ccb92e479b Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:27:21 -0800 Subject: travis --- .travis.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yaml diff --git a/.travis.yaml b/.travis.yaml new file mode 100644 index 0000000..145b2e3 --- /dev/null +++ b/.travis.yaml @@ -0,0 +1,3 @@ +language: python +install: 'pip install websocket-client' +script: pytest -- cgit From 6d01f9826b5c25fd450845fea1b1fd5364d79e20 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:28:53 -0800 Subject: 3 letters are better than 4 --- .travis.yaml | 3 --- .travis.yml | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 .travis.yaml create mode 100644 .travis.yml diff --git a/.travis.yaml b/.travis.yaml deleted file mode 100644 index 145b2e3..0000000 --- a/.travis.yaml +++ /dev/null @@ -1,3 +0,0 @@ -language: python -install: 'pip install websocket-client' -script: pytest diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..145b2e3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: python +install: 'pip install websocket-client' +script: pytest -- cgit From 550e6511eb36360f29edeb151eac286475cd5031 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:31:31 -0800 Subject: maybe the docs are wrong --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 145b2e3..ab1eacb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: python -install: 'pip install websocket-client' +install: 'pip install websocket-client pytest' script: pytest -- cgit From 8e1f119ee9e47f8f6e3a4d4e8dfc620b58d7c26e Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:37:44 -0800 Subject: maybe this?? --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab1eacb..99e1a50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ language: python -install: 'pip install websocket-client pytest' -script: pytest +python: + - "2.7" +install: + - 'pip install websocket-client pytest' + +script: python -m pytest -- cgit From 7f93c43d8cc6f7698cc161ed63d8a74db473da0b Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 12:41:49 -0800 Subject: show build status --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..347e7f7 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +[![Build Status](https://travis-ci.org/wee-slack/wee-ng.svg?branch=master)](https://travis-ci.org/wee-slack/wee-ng) -- cgit From 8916744f0335bfbdddcbe87703eb518891d69503 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 16:30:54 -0800 Subject: use sorted, because otherwise we try to modify messages that don't exist yet --- _pytest/test_everything.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index 9e356be..fd29850 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -24,7 +24,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): notimplemented = set() - for fname in datafiles: + for fname in sorted(datafiles): try: data = json.loads(open(fname, 'r').read()) socket.add(data) @@ -32,15 +32,13 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): eventrouter.handle_next() except ProcessNotImplemented as e: notimplemented.add(str(e)) - #this handles some message data not existing - need to fix - except KeyError: - pass + if len(notimplemented) > 0: print "####################" print sorted(notimplemented) print "####################" - assert False + #assert False -- cgit From a97b310f586a498de7a2d5d9a0018dfeaa21d980 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 1 Feb 2017 19:14:52 -0800 Subject: more test cases + recorder outputs json properly --- _pytest/data/websocket/1486004888.32-presence_change.json | 1 + _pytest/data/websocket/1486004888.41-reconnect_url.json | 1 + _pytest/data/websocket/1486004935.63-presence_change.json | 1 + _pytest/data/websocket/1486004950.43-message.json | 1 + _pytest/data/websocket/1486004992.49-user_typing.json | 1 + _pytest/data/websocket/1486004995.69-user_typing.json | 1 + _pytest/data/websocket/1486004999.55-user_typing.json | 1 + _pytest/data/websocket/1486004999.95-message.json | 1 + _pytest/data/websocket/1486005004.03-user_typing.json | 1 + _pytest/data/websocket/1486005007.16-user_typing.json | 1 + _pytest/data/websocket/1486005008.45-reconnect_url.json | 1 + _pytest/data/websocket/1486005009.0-message.json | 1 + _pytest/data/websocket/1486005096.38-accounts_changed.json | 1 + _pytest/data/websocket/1486005099.81-presence_change.json | 1 + _pytest/data/websocket/1486005104.63-channel_marked.json | 1 + _pytest/data/websocket/1486005109.58-im_marked.json | 1 + _pytest/data/websocket/1486005119.69-im_marked.json | 1 + _pytest/data/websocket/1486005124.51-mpim_marked.json | 1 + _pytest/data/websocket/1486005124.59-group_marked.json | 1 + _pytest/data/websocket/1486005124.67-group_marked.json | 1 + _pytest/data/websocket/1486005188.48-reconnect_url.json | 1 + _pytest/test_everything.py | 4 +++- wee_slack.py | 2 +- 23 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 _pytest/data/websocket/1486004888.32-presence_change.json create mode 100644 _pytest/data/websocket/1486004888.41-reconnect_url.json create mode 100644 _pytest/data/websocket/1486004935.63-presence_change.json create mode 100644 _pytest/data/websocket/1486004950.43-message.json create mode 100644 _pytest/data/websocket/1486004992.49-user_typing.json create mode 100644 _pytest/data/websocket/1486004995.69-user_typing.json create mode 100644 _pytest/data/websocket/1486004999.55-user_typing.json create mode 100644 _pytest/data/websocket/1486004999.95-message.json create mode 100644 _pytest/data/websocket/1486005004.03-user_typing.json create mode 100644 _pytest/data/websocket/1486005007.16-user_typing.json create mode 100644 _pytest/data/websocket/1486005008.45-reconnect_url.json create mode 100644 _pytest/data/websocket/1486005009.0-message.json create mode 100644 _pytest/data/websocket/1486005096.38-accounts_changed.json create mode 100644 _pytest/data/websocket/1486005099.81-presence_change.json create mode 100644 _pytest/data/websocket/1486005104.63-channel_marked.json create mode 100644 _pytest/data/websocket/1486005109.58-im_marked.json create mode 100644 _pytest/data/websocket/1486005119.69-im_marked.json create mode 100644 _pytest/data/websocket/1486005124.51-mpim_marked.json create mode 100644 _pytest/data/websocket/1486005124.59-group_marked.json create mode 100644 _pytest/data/websocket/1486005124.67-group_marked.json create mode 100644 _pytest/data/websocket/1486005188.48-reconnect_url.json diff --git a/_pytest/data/websocket/1486004888.32-presence_change.json b/_pytest/data/websocket/1486004888.32-presence_change.json new file mode 100644 index 0000000..c52679d --- /dev/null +++ b/_pytest/data/websocket/1486004888.32-presence_change.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U3ZKBBDL5", "presence": "active"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004888.41-reconnect_url.json b/_pytest/data/websocket/1486004888.41-reconnect_url.json new file mode 100644 index 0000000..ae7d1f1 --- /dev/null +++ b/_pytest/data/websocket/1486004888.41-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-3z94.slack-msgs.com/websocket/v0jzriT0ZxrHn6tVO4Dpi96hr54r8ZOaGklQfQN2G_D1kO6mEMs7KXXfUTax40_5zkAA6XTfZpSsjo3TGVD0hcFrhhSwhTsbFvs6W2m5pHH8hfaLJuKFkfPZySnprXsHCXDNKMWyTKMFTZ_CAUKjyhJWzY9Z708pAGB8oy6YMcE=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004935.63-presence_change.json b/_pytest/data/websocket/1486004935.63-presence_change.json new file mode 100644 index 0000000..eca19bc --- /dev/null +++ b/_pytest/data/websocket/1486004935.63-presence_change.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U407ABLLW", "presence": "active"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004950.43-message.json b/_pytest/data/websocket/1486004950.43-message.json new file mode 100644 index 0000000..ce13b3f --- /dev/null +++ b/_pytest/data/websocket/1486004950.43-message.json @@ -0,0 +1 @@ +{"text": "is here", "ts": "1486004950.000002", "subtype": "me_message", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004992.49-user_typing.json b/_pytest/data/websocket/1486004992.49-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486004992.49-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004995.69-user_typing.json b/_pytest/data/websocket/1486004995.69-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486004995.69-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004999.55-user_typing.json b/_pytest/data/websocket/1486004999.55-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486004999.55-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004999.95-message.json b/_pytest/data/websocket/1486004999.95-message.json new file mode 100644 index 0000000..b906b1b --- /dev/null +++ b/_pytest/data/websocket/1486004999.95-message.json @@ -0,0 +1 @@ +{"text": "this _is_ a *formatted* message", "ts": "1486004999.000003", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005004.03-user_typing.json b/_pytest/data/websocket/1486005004.03-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486005004.03-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005007.16-user_typing.json b/_pytest/data/websocket/1486005007.16-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486005007.16-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005008.45-reconnect_url.json b/_pytest/data/websocket/1486005008.45-reconnect_url.json new file mode 100644 index 0000000..bafddd6 --- /dev/null +++ b/_pytest/data/websocket/1486005008.45-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-14cn.slack-msgs.com/websocket/28Rgy5cmEP5N50Jq5Jk0YSCKRdC_PTNklbuT07jR1-BP0vpyvFzhUFpTqbl7pxbMkSCE1rv8OMQMKsWyHgDnoJ9KANYeHweBmRAsL41zPn-dJnJmTpnm670fgK1Nr-tVsfXpo7ql0cbstLR_KWDHr8zt1B2TbqIUeoikNN6oBiY=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005009.0-message.json b/_pytest/data/websocket/1486005009.0-message.json new file mode 100644 index 0000000..10c96e9 --- /dev/null +++ b/_pytest/data/websocket/1486005009.0-message.json @@ -0,0 +1 @@ +{"text": "this is ~strikethrough~", "ts": "1486005008.000004", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005096.38-accounts_changed.json b/_pytest/data/websocket/1486005096.38-accounts_changed.json new file mode 100644 index 0000000..1d0086c --- /dev/null +++ b/_pytest/data/websocket/1486005096.38-accounts_changed.json @@ -0,0 +1 @@ +{"event_ts": "1486005096.312395", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "accounts_changed"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005099.81-presence_change.json b/_pytest/data/websocket/1486005099.81-presence_change.json new file mode 100644 index 0000000..c52679d --- /dev/null +++ b/_pytest/data/websocket/1486005099.81-presence_change.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U3ZKBBDL5", "presence": "active"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005104.63-channel_marked.json b/_pytest/data/websocket/1486005104.63-channel_marked.json new file mode 100644 index 0000000..956e886 --- /dev/null +++ b/_pytest/data/websocket/1486005104.63-channel_marked.json @@ -0,0 +1 @@ +{"mention_count": 0, "event_ts": "1486005104.313131", "ts": "1486005008.000004", "unread_count": 0, "num_mentions": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_marked", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005109.58-im_marked.json b/_pytest/data/websocket/1486005109.58-im_marked.json new file mode 100644 index 0000000..b74c742 --- /dev/null +++ b/_pytest/data/websocket/1486005109.58-im_marked.json @@ -0,0 +1 @@ +{"event_ts": "1486005109.313169", "ts": "1485975735.000002", "dm_count": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "im_marked", "channel": "D3ZK1D8JY"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005119.69-im_marked.json b/_pytest/data/websocket/1486005119.69-im_marked.json new file mode 100644 index 0000000..802f4c9 --- /dev/null +++ b/_pytest/data/websocket/1486005119.69-im_marked.json @@ -0,0 +1 @@ +{"event_ts": "1486005119.313254", "ts": "1485975421.000002", "dm_count": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "im_marked", "channel": "D3ZEQULHZ"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005124.51-mpim_marked.json b/_pytest/data/websocket/1486005124.51-mpim_marked.json new file mode 100644 index 0000000..3357683 --- /dev/null +++ b/_pytest/data/websocket/1486005124.51-mpim_marked.json @@ -0,0 +1 @@ +{"mention_count": 0, "event_ts": "1486005124.313290", "is_mpim": true, "ts": "1485975715.000005", "unread_count": 0, "num_mentions": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "mpim_marked", "channel": "G3ZGMF4RZ"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005124.59-group_marked.json b/_pytest/data/websocket/1486005124.59-group_marked.json new file mode 100644 index 0000000..8efe97a --- /dev/null +++ b/_pytest/data/websocket/1486005124.59-group_marked.json @@ -0,0 +1 @@ +{"mention_count": 0, "event_ts": "1486005124.313291", "is_mpim": true, "ts": "1485975715.000005", "unread_count": 0, "num_mentions": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_marked", "channel": "G3ZGMF4RZ"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005124.67-group_marked.json b/_pytest/data/websocket/1486005124.67-group_marked.json new file mode 100644 index 0000000..7753500 --- /dev/null +++ b/_pytest/data/websocket/1486005124.67-group_marked.json @@ -0,0 +1 @@ +{"mention_count": 0, "event_ts": "1486005124.313292", "is_mpim": false, "ts": "1485975492.000004", "unread_count": 0, "num_mentions": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_marked", "channel": "G3ZJKP7GA"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005188.48-reconnect_url.json b/_pytest/data/websocket/1486005188.48-reconnect_url.json new file mode 100644 index 0000000..1bdbd43 --- /dev/null +++ b/_pytest/data/websocket/1486005188.48-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-lz08.slack-msgs.com/websocket/dHqqUxSrrgTCJI7af1mYz7ECm0-rxu4GOIPmnYwK_K9uWOwBXvF54joGrhpZcUjrJ913Vx01Wh6Ta4u5-bC4-Zn9H9wXklCc6mDDf8iJJmXKC2nWZYaP8jOTzPRNM97iISoe5LNSXl_JB-9J5DSSwEdBh7eQ0SEFeko7NxXP6_o=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} \ No newline at end of file diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index fd29850..039be54 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -33,13 +33,15 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): except ProcessNotImplemented as e: notimplemented.add(str(e)) - if len(notimplemented) > 0: print "####################" print sorted(notimplemented) print "####################" #assert False + print eventrouter.queue + assert False + diff --git a/wee_slack.py b/wee_slack.py index 2d059a1..98941c7 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -166,7 +166,7 @@ class EventRouter(object): now = time.time() mtype = message_json.get('type', 'unknown') f = open('{}/{}-{}.json'.format(RECORD_DIR, now, mtype), 'w') - f.write("{}".format(message_json)) + f.write("{}".format(json.dumps(message_json))) f.close() self.receive_json(json.dumps(message_json)) except WebSocketConnectionClosedException: -- cgit From 352542c718586dcc0f59dcc1f25803542d8fe7dd Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 05:33:43 -0800 Subject: run them all at the end --- _pytest/test_everything.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index 039be54..4343e04 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -29,7 +29,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): data = json.loads(open(fname, 'r').read()) socket.add(data) eventrouter.receive_ws_callback(t) - eventrouter.handle_next() + #eventrouter.handle_next() except ProcessNotImplemented as e: notimplemented.add(str(e)) @@ -39,6 +39,9 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): print "####################" #assert False + for item in eventrouter.queue: + eventrouter.handle_next() + print eventrouter.queue assert False -- cgit From 3db54806c4889354791e922efd72b2812f5d8c22 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 05:38:48 -0800 Subject: the old ways are better --- _pytest/test_everything.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index 4343e04..cbd6fde 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -24,27 +24,25 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): notimplemented = set() - for fname in sorted(datafiles): + for fname in datafiles: try: data = json.loads(open(fname, 'r').read()) socket.add(data) eventrouter.receive_ws_callback(t) - #eventrouter.handle_next() + eventrouter.handle_next() except ProcessNotImplemented as e: notimplemented.add(str(e)) + #this handles some message data not existing - need to fix + except KeyError: + pass if len(notimplemented) > 0: print "####################" print sorted(notimplemented) print "####################" - #assert False - for item in eventrouter.queue: - eventrouter.handle_next() - - print eventrouter.queue + print len(eventrouter.queue) assert False - -- cgit From d7041c04e8c4d1266f4445a0169bd85889ed69fe Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 05:41:34 -0800 Subject: seperator --- _pytest/test_everything.py | 1 + 1 file changed, 1 insertion(+) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index cbd6fde..fdd4382 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -26,6 +26,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): for fname in datafiles: try: + print "####################" data = json.loads(open(fname, 'r').read()) socket.add(data) eventrouter.receive_ws_callback(t) -- cgit From c4517524fb8020c07bd08c2a51144e8996151d43 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 05:45:59 -0800 Subject: move notifications --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 99e1a50..af91796 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,3 +5,6 @@ install: - 'pip install websocket-client pytest' script: python -m pytest + +notifications: + irc: "chat.freenode.net#wee-slack-dev" -- cgit From 25f7dab5a58fca12e4a0bf4151e5eb8f9a52e1b6 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 05:51:05 -0800 Subject: disable email notification --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index af91796..6739e1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,3 +8,4 @@ script: python -m pytest notifications: irc: "chat.freenode.net#wee-slack-dev" + email: false -- cgit From d4920b661b99f53097343dffbb8774392c1a6b43 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 06:16:06 -0800 Subject: enhance event recorder --- wee_slack.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 98941c7..2ba38bd 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -100,12 +100,27 @@ class EventRouter(object): self.recording_path = "/tmp" def record(self): + """ + Toggles the event recorder and creates a directory for data if enabled. + """ self.recording = not self.recording if self.recording: import os if not os.path.exists(RECORD_DIR): os.makedirs(RECORD_DIR) + def record_event(self, message_json, file_name_field): + """ + Called each time you want to record an event. + message_json is a json in dict form + file_name_field is the json key whose value you want to be part of the file name + """ + now = time.time() + mtype = message_json.get(file_name_field, 'unknown') + f = open('{}/{}-{}.json'.format(RECORD_DIR, now, mtype), 'w') + f.write("{}".format(json.dumps(message_json))) + f.close() + def shutdown(self): self.shutting_down = not self.shutting_down @@ -163,11 +178,7 @@ class EventRouter(object): message_json["wee_slack_metadata"] = metadata #print message_json if self.recording: - now = time.time() - mtype = message_json.get('type', 'unknown') - f = open('{}/{}-{}.json'.format(RECORD_DIR, now, mtype), 'w') - f.write("{}".format(json.dumps(message_json))) - f.close() + self.record_event(message_json, 'type') self.receive_json(json.dumps(message_json)) except WebSocketConnectionClosedException: #TODO: handle reconnect here @@ -194,6 +205,8 @@ class EventRouter(object): j["wee_slack_request_metadata"] = pickle.dumps(request_metadata) #print self.reply_buffer[request_metadata.response_id] self.reply_buffer.pop(request_metadata.response_id) + if self.recording: + self.record_event(j, 'wee_slack_process_method') self.receive_json(json.dumps(j)) except: dbg("FAILED") -- cgit From be168a3caae063f1e8e834bda66f964bd6f683d4 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 06:35:41 -0800 Subject: make event recorder plugin configurable and load at startup --- wee_slack.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 2ba38bd..3017847 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1388,6 +1388,7 @@ class PluginConfig(object): 'trigger_value': 'false', 'unfurl_ignore_alt_text': 'false', 'cache_messages': 'true', + 'record_events': 'false', } # Set missing settings to their defaults. Load non-missing settings from @@ -1531,6 +1532,8 @@ if __name__ == "__main__": s = SlackRequest(tok, 'rtm.start', {}) global EVENTROUTER EVENTROUTER = EventRouter() + if config.record_events: + EVENTROUTER.record() EVENTROUTER.receive(s) EVENTROUTER.handle_next() w.hook_timer(10, 0, 0, "handle_next", "") -- cgit From 1c6928d2accb6a383d05ae61669727b8fe7438f7 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 10:17:05 -0800 Subject: add lots of docstrings. fix buffer names/localvars --- _pytest/test_everything.py | 2 +- wee_slack.py | 156 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 131 insertions(+), 27 deletions(-) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index fdd4382..b1a2f23 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -43,7 +43,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): print "####################" print len(eventrouter.queue) - assert False +# assert False diff --git a/wee_slack.py b/wee_slack.py index 3017847..7077f24 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -86,6 +86,15 @@ IGNORED_EVENTS = [ class EventRouter(object): def __init__(self): + """ + complete + Eventrouter is the central hub we use to route: + 1) incoming websocket data + 2) outgoing http requests and incoming replies + 3) local requests + It has a recorder that, when enabled, logs most events + to the location specified in RECORD_DIR. + """ self.queue = [] self.teams = {} self.weechat_buffers = {} @@ -101,6 +110,7 @@ class EventRouter(object): def record(self): """ + complete Toggles the event recorder and creates a directory for data if enabled. """ self.recording = not self.recording @@ -111,6 +121,7 @@ class EventRouter(object): def record_event(self, message_json, file_name_field): """ + complete Called each time you want to record an event. message_json is a json in dict form file_name_field is the json key whose value you want to be part of the file name @@ -122,11 +133,19 @@ class EventRouter(object): f.close() def shutdown(self): + """ + complete + This toggles shutdown mode. Shutdown mode tells us not to + talk to Slack anymore. Without this, typing /quit will trigger + a race with the buffer close callback and may result in you + leaving every slack channel. + """ self.shutting_down = not self.shutting_down def register_team(self, team): """ - Adds a team to the list of known teams for this EventRouter + complete + Adds a team to the list of known teams for this EventRouter. """ if isinstance(team, SlackTeam): self.teams[team.get_team_hash()] = team @@ -135,6 +154,7 @@ class EventRouter(object): def register_weechat_buffer(self, buffer_ptr, channel): """ + complete Adds a weechat buffer to the list of handled buffers for this EventRouter """ if isinstance(buffer_ptr, str): @@ -144,6 +164,7 @@ class EventRouter(object): def unregister_weechat_buffer(self, buffer_ptr, update_remote=False, close_buffer=False): """ + complete Adds a weechat buffer to the list of handled buffers for this EventRouter """ if isinstance(buffer_ptr, str): @@ -159,6 +180,7 @@ class EventRouter(object): def receive_ws_callback(self, team_hash): """ + incomplete (reconnect) This is called by the global method of the same name. It is triggered when we have incoming data on a websocket, which needs to be read. Once it is read, we will ensure @@ -171,12 +193,8 @@ class EventRouter(object): message_json = json.loads(data) metadata = WeeSlackMetadata({ "team": team_hash, - #"channels": self.teams[team_hash].channels, - #"users": self.teams[team_hash].users, }).jsonify() - #print self.teams[team_hash].domain message_json["wee_slack_metadata"] = metadata - #print message_json if self.recording: self.record_event(message_json, 'type') self.receive_json(json.dumps(message_json)) @@ -189,13 +207,19 @@ class EventRouter(object): return w.WEECHAT_RC_OK def receive_httprequest_callback(self, data, command, return_code, out, err): - #def url_processor_cb(data, command, return_code, out, err): + """ + complete + Receives the result of an http request we previously handed + off to weechat (weechat bundles libcurl). Weechat can fragment + replies, so it buffers them until the reply is complete. + It is then populated with metadata here so we can identify + where the request originated and route properly. + """ request_metadata = pickle.loads(data) dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out)), main_buffer=True) if return_code == 0: if request_metadata.response_id in self.reply_buffer: self.reply_buffer[request_metadata.response_id] += out - #print self.reply_buffer[request_metadata.response_id] else: self.reply_buffer[request_metadata.response_id] = "" self.reply_buffer[request_metadata.response_id] += out @@ -203,13 +227,12 @@ class EventRouter(object): j = json.loads(self.reply_buffer[request_metadata.response_id]) j["wee_slack_process_method"] = request_metadata.request_normalized j["wee_slack_request_metadata"] = pickle.dumps(request_metadata) - #print self.reply_buffer[request_metadata.response_id] self.reply_buffer.pop(request_metadata.response_id) if self.recording: self.record_event(j, 'wee_slack_process_method') self.receive_json(json.dumps(j)) except: - dbg("FAILED") + dbg("HTTP REQUEST CALLBACK FAILED") pass elif return_code != -1: self.reply_buffer.pop(request_metadata.response_id, None) @@ -219,15 +242,30 @@ class EventRouter(object): self.reply_buffer[request_metadata.response_id] += out def receive_json(self, data): + """ + complete + Receives a raw JSON string from and unmarshals it + as dict, then places it back on the queue for processing. + """ dbg("RECEIVED JSON of len {}".format(len(data))) message_json = json.loads(data) - #print message_json.keys() self.queue.append(message_json) def receive(self, dataobj): - dbg("RECEIVED QUEUE", main_buffer=True) - #dbg(str(len(dataobj)), main_buffer=True) + """ + complete + Receives a raw object and places it on the queue for + processing. Object must be known to handle_next or + be JSON. + """ + dbg("RECEIVED FROM QUEUE", main_buffer=True) self.queue.append(dataobj) def handle_next(self): + """ + complete + Main handler of the EventRouter. This is called repeatedly + via callback to drain events from the queue. It also attaches + useful metadata and context to events as they are processed. + """ if len(self.queue) > 0: j = self.queue.pop(0) # Reply is a special case of a json reply from websocket. @@ -278,6 +316,11 @@ class EventRouter(object): raise ProcessNotImplemented(function_name) def handle_next(*args): + """ + complete + This is just a place to call the event router globally. + This is a dirty hack. There must be a better way. + """ EVENTROUTER.handle_next() return w.WEECHAT_RC_OK @@ -285,6 +328,7 @@ def handle_next(*args): def local_process_async_slack_api_request(request, event_router): """ + complete Sends an API request to Slack. You'll need to give this a well formed SlackRequest object. """ if not event_router.shutting_down: @@ -299,6 +343,7 @@ def local_process_async_slack_api_request(request, event_router): def receive_httprequest_callback(data, command, return_code, out, err): """ + complete This is a dirty hack. There must be a better way. """ #def url_processor_cb(data, command, return_code, out, err): @@ -307,6 +352,7 @@ def receive_httprequest_callback(data, command, return_code, out, err): def receive_ws_callback(*args): """ + complete The first arg is all we want here. It contains the team hash which is set when we _hook the descriptor. This is a dirty hack. There must be a better way. @@ -315,10 +361,23 @@ def receive_ws_callback(*args): return w.WEECHAT_RC_OK def buffer_closing_callback(signal, sig_type, data): + """ + complete + Receives a callback from weechat when a buffer is being closed. + We pass the eventrouter variable name in as a string, as + that is the only way we can do dependency injection via weechat + callback, hence the eval. + """ eval(signal).unregister_weechat_buffer(data, True, False) return w.WEECHAT_RC_OK def buffer_input_callback(signal, buffer_ptr, data): + """ + incomplete + Handles everything a user types in the input bar. In our case + this includes add/remove reactions, modifying messages, and + sending messages. + """ eventrouter = eval(signal) channel = eventrouter.weechat_buffers[buffer_ptr] if not channel: @@ -348,6 +407,12 @@ def buffer_input_callback(signal, buffer_ptr, data): return w.WEECHAT_RC_ERROR def buffer_switch_callback(signal, sig_type, data): + """ + incomplete + Every time we change channels in weechat, we call this to: + 1) set read marker 2) determine if we have already populated + channel history data + """ eventrouter = eval(signal) # this is to see if we need to gray out things in the buffer list @@ -365,6 +430,14 @@ def buffer_switch_callback(signal, sig_type, data): return w.WEECHAT_RC_OK def buffer_list_update_callback(data, somecount): + """ + incomplete + A simple timer-based callback that will update the buffer list + if needed. We only do this max 1x per second, as otherwise it + uses a lot of cpu for minimal changes. We use buffer short names + to indicate typing via "#channel" <-> ">channel" and + user presence via " name" <-> "+name". + """ eventrouter = eval(data) global buffer_list_update @@ -389,6 +462,7 @@ def script_unloaded(): def stop_talking_to_slack(): """ + complete Prevents a race condition where quitting closes buffers which triggers leaving the channel because of how close buffer is handled @@ -401,7 +475,9 @@ def stop_talking_to_slack(): class SlackRequest(object): """ + complete Encapsulates a Slack api request. Valuable as an object that we can add to the queue and/or retry. + makes a SHA of the requst url and current time so we can re-tag this on the way back through. """ def __init__(self, token, request, post_data={}, **kwargs): print '=================' @@ -429,6 +505,10 @@ class SlackRequest(object): return self.tries < 3 class SlackTeam(object): + """ + incomplete + Team object under which users and channels live.. Does lots. + """ def __init__(self, eventrouter, token, team, nick, myidentifier, users, bots, channels): self.connected = False self.ws = None @@ -468,9 +548,9 @@ class SlackTeam(object): if not self.server_buffer: self.server_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.register_weechat_buffer(self.server_buffer, self) - w.buffer_set(self.server_buffer, "localvar_set_type", 'channel') - w.buffer_set(self.server_buffer, "localvar_set_channel", self.name) - w.buffer_set(self.server_buffer, "short_name", self.name) + if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': + w.buffer_merge(self.server_buffer, w.buffer_search_main()) + w.buffer_set(self.server_buffer, "nicklist", "1") def get_channel_map(self): return {v.slack_name: k for k, v in self.channels.iteritems()} def get_username_map(self): @@ -547,21 +627,26 @@ class SlackChannel(object): def set_related_server(self, team): self.team = team def create_buffer(self): + """ + incomplete + Creates the weechat buffer where the channel magic happens. + """ if not self.channel_buffer: self.channel_buffer = w.buffer_new("{}.{}".format(self.team.domain, self.name), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.register_weechat_buffer(self.channel_buffer, self) - - #dbg(self.channel_buffer, True) - w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') + if self.type == "im": + w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') + else: + w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') w.buffer_set(self.channel_buffer, "localvar_set_channel", self.name) w.buffer_set(self.channel_buffer, "short_name", self.formatted_name()) -# if self.server.alias: -# w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) -# else: -# w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) -# buffer_list_update_next() -# if self.unread_count != 0 and not self.muted: -# w.buffer_set(self.channel_buffer, "hotlist", "1") + if self.server.alias: + w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) + else: + w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) + buffer_list_update_next() + if self.unread_count != 0 and not self.muted: + w.buffer_set(self.channel_buffer, "hotlist", "1") def destroy_buffer(self, update_remote): if self.channel_buffer is not None: self.channel_buffer = None @@ -632,6 +717,10 @@ class SlackChannel(object): class SlackDMChannel(SlackChannel): + """ + Subclass of a normal channel for person-to-person communication, which + has some important differences. + """ def __init__(self, eventrouter, users, **kwargs): dmuser = kwargs["user"] kwargs["name"] = users[dmuser].name @@ -662,6 +751,9 @@ class SlackDMChannel(SlackChannel): class SlackGroupChannel(SlackChannel): + """ + A group channel is a private discussion group. + """ def __init__(self, eventrouter, **kwargs): super(SlackGroupChannel, self).__init__(eventrouter, **kwargs) self.name = "#" + kwargs['name'] @@ -685,7 +777,7 @@ class SlackMPDMChannel(SlackChannel): class SlackUser(object): """ - Represends an individual slack user. + Represends an individual slack user. Also where you set their name formatting. """ def __init__(self, **kwargs): # We require these two things for a vaid object, @@ -715,10 +807,19 @@ class SlackUser(object): return print_color + prepend + self.name 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) class SlackMessage(object): + """ + Represents a single slack message and associated context/metadata. + These are modifiable and can be rerendered to change a message, + delete a message, add a reaction, add a thread. + """ def __init__(self, message_json, team, channel): self.team = team self.channel = channel @@ -780,6 +881,9 @@ class SlackMessage(object): class WeeSlackMetadata(object): + """ + A simple container that we pickle/unpickle to hold data. + """ def __init__(self, meta): self.meta = meta def jsonify(self): -- cgit From c0a5b543de425698c8cf6e997de28bbbe44aff26 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 12:02:01 -0800 Subject: split out weechat interaction --- wee_slack.py | 98 ++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 7077f24..99e42da 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -97,7 +97,7 @@ class EventRouter(object): """ self.queue = [] self.teams = {} - self.weechat_buffers = {} + self.weechat_controller = WeechatController(self) self.previous_buffer = "" self.reply_buffer = {} self.cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} @@ -152,32 +152,6 @@ class EventRouter(object): else: raise InvalidType(type(team)) - def register_weechat_buffer(self, buffer_ptr, channel): - """ - complete - Adds a weechat buffer to the list of handled buffers for this EventRouter - """ - if isinstance(buffer_ptr, str): - self.weechat_buffers[buffer_ptr] = channel - else: - raise InvalidType(type(buffer_ptr)) - - def unregister_weechat_buffer(self, buffer_ptr, update_remote=False, close_buffer=False): - """ - complete - Adds a weechat buffer to the list of handled buffers for this EventRouter - """ - if isinstance(buffer_ptr, str): - try: - self.weechat_buffers[buffer_ptr].destroy_buffer(update_remote) - if close_buffer: - w.buffer_close(buffer_ptr) - del self.weechat_buffers[buffer_ptr] - except: - dbg("Tried to close unknown buffer") - else: - raise InvalidType(type(buffer_ptr)) - def receive_ws_callback(self, team_hash): """ incomplete (reconnect) @@ -324,6 +298,51 @@ def handle_next(*args): EVENTROUTER.handle_next() return w.WEECHAT_RC_OK +class WeechatController(object): + """ + Encapsulates our interaction with weechat + """ + def __init__(self, eventrouter): + self.eventrouter = eventrouter + self.buffers = {} + self.previous_buffer = "" + def iter_buffers(self): + for b in self.buffers: + yield (b, self.buffers[b]) + def register_buffer(self, buffer_ptr, channel): + """ + complete + Adds a weechat buffer to the list of handled buffers for this EventRouter + """ + if isinstance(buffer_ptr, str): + self.buffers[buffer_ptr] = channel + else: + raise InvalidType(type(buffer_ptr)) + def unregister_buffer(self, buffer_ptr, update_remote=False, close_buffer=False): + """ + complete + Adds a weechat buffer to the list of handled buffers for this EventRouter + """ + if isinstance(buffer_ptr, str): + try: + self.buffers[buffer_ptr].destroy_buffer(update_remote) + if close_buffer: + w.buffer_close(buffer_ptr) + del self.buffers[buffer_ptr] + except: + dbg("Tried to close unknown buffer") + else: + raise InvalidType(type(buffer_ptr)) + def get_channel_from_buffer_ptr(self, buffer_ptr): + return self.buffers.get(buffer_ptr, None) + def get_all(self, buffer_ptr): + return self.buffers + def get_previous_buffer(self): + return self.previous_buffer + def set_previous_buffer(self, data): + self.previous_buffer = data + + ###### New Local Processors def local_process_async_slack_api_request(request, event_router): @@ -368,7 +387,7 @@ def buffer_closing_callback(signal, sig_type, data): that is the only way we can do dependency injection via weechat callback, hence the eval. """ - eval(signal).unregister_weechat_buffer(data, True, False) + eval(signal).weechat_controller.unregister_buffer(data, True, False) return w.WEECHAT_RC_OK def buffer_input_callback(signal, buffer_ptr, data): @@ -379,7 +398,7 @@ def buffer_input_callback(signal, buffer_ptr, data): sending messages. """ eventrouter = eval(signal) - channel = eventrouter.weechat_buffers[buffer_ptr] + channel = eventrouter.weechat_controller.get_channel_from_buffer_ptr(buffer_ptr) if not channel: return w.WEECHAT_RC_OK_EAT @@ -415,18 +434,18 @@ def buffer_switch_callback(signal, sig_type, data): """ eventrouter = eval(signal) + prev_buffer = eventrouter.weechat_controller.get_previous_buffer() # this is to see if we need to gray out things in the buffer list - if eventrouter.previous_buffer in eventrouter.weechat_buffers: + if eventrouter.weechat_controller.get_channel_from_buffer_ptr(prev_buffer): pass #channels.find(previous_buffer).mark_read() - if data in eventrouter.weechat_buffers: - new_channel = eventrouter.weechat_buffers[data] - #if new_channel: + new_channel = eventrouter.weechat_controller.get_channel_from_buffer_ptr(data) + if new_channel: if not new_channel.got_history: new_channel.get_history() - eventrouter.previous_buffer = data + eventrouter.weechat_controller.set_previous_buffer(data) return w.WEECHAT_RC_OK def buffer_list_update_callback(data, somecount): @@ -447,9 +466,10 @@ def buffer_list_update_callback(data, somecount): # gray_check = False # if len(servers) > 1: # gray_check = True - for b in eventrouter.weechat_buffers: + for b in eventrouter.weechat_controller.iter_buffers(): + b[1].rename() #print b - eventrouter.weechat_buffers[b].rename() + #eventrouter.weechat_buffers[b].rename() buffer_list_update = False return w.WEECHAT_RC_OK @@ -547,7 +567,7 @@ class SlackTeam(object): def create_buffer(self): if not self.server_buffer: self.server_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") - self.eventrouter.register_weechat_buffer(self.server_buffer, self) + self.eventrouter.weechat_controller.register_buffer(self.server_buffer, self) if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': w.buffer_merge(self.server_buffer, w.buffer_search_main()) w.buffer_set(self.server_buffer, "nicklist", "1") @@ -633,7 +653,7 @@ class SlackChannel(object): """ if not self.channel_buffer: self.channel_buffer = w.buffer_new("{}.{}".format(self.team.domain, self.name), "buffer_input_callback", "EVENTROUTER", "", "") - self.eventrouter.register_weechat_buffer(self.channel_buffer, self) + self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) if self.type == "im": w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') else: @@ -1127,7 +1147,7 @@ def process_im_close(message_json, eventrouter, **kwargs): item = message_json cbuf = kwargs['team'].channels[item["channel"]].channel_buffer print cbuf - eventrouter.unregister_weechat_buffer(cbuf, False, True) + eventrouter.weechat_controller.unregister_buffer(cbuf, False, True) def process_reaction_added(message_json, eventrouter, **kwargs): channel = kwargs['team'].channels[message_json["item"]["channel"]] -- cgit From 548c4b3df3df8518ccf83e3c404ece32d1394d38 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 13:08:08 -0800 Subject: colorization works --- wee_slack.py | 126 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 104 insertions(+), 22 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 99e42da..4a009b2 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -306,6 +306,7 @@ class WeechatController(object): self.eventrouter = eventrouter self.buffers = {} self.previous_buffer = "" + self.buffer_list_stale = False def iter_buffers(self): for b in self.buffers: yield (b, self.buffers[b]) @@ -341,6 +342,10 @@ class WeechatController(object): return self.previous_buffer def set_previous_buffer(self, data): self.previous_buffer = data + def check_refresh_buffer_list(self): + return self.buffer_list_stale and self.last_buffer_list_update + 1 < time.time() + def set_refresh_buffer_list(self, setting): + self.buffer_list_stale = setting ###### New Local Processors @@ -458,19 +463,16 @@ def buffer_list_update_callback(data, somecount): user presence via " name" <-> "+name". """ eventrouter = eval(data) - global buffer_list_update - - buffer_list_update = True - now = time.time() - if buffer_list_update and previous_buffer_list_update + 1 < now: - # gray_check = False - # if len(servers) > 1: - # gray_check = True - for b in eventrouter.weechat_controller.iter_buffers(): - b[1].rename() - #print b - #eventrouter.weechat_buffers[b].rename() - buffer_list_update = False + #global buffer_list_update + + for b in eventrouter.weechat_controller.iter_buffers(): + b[1].refresh() +# buffer_list_update = True +# if eventrouter.weechat_controller.check_refresh_buffer_list(): +# # gray_check = False +# # if len(servers) > 1: +# # gray_check = True +# eventrouter.weechat_controller.set_refresh_buffer_list(False) return w.WEECHAT_RC_OK def quit_notification_callback(signal, sig_type, data): @@ -577,10 +579,18 @@ class SlackTeam(object): return {v.name: k for k, v in self.users.iteritems()} def get_team_hash(self): return self.team_hash + def refresh(self): + self.rename() def rename(self): pass def attach_websocket(self, ws): self.ws = ws + def is_user_present(self, user_id): + user = self.users.get(user_id) + if user.presence == 'active': + return True + else: + return False def set_connected(self): self.connected = True def set_disconnected(self): @@ -617,18 +627,30 @@ class SlackChannel(object): self.team = None self.got_history = False self.messages = {} + self.typing = {} self.type = 'channel' for key, value in kwargs.items(): setattr(self, key, value) self.set_name(self.slack_name) + #short name relates to the localvar we change for typing indication + self.current_short_name = self.name def __repr__(self): return "Name:{} Identifier:{}".format(self.name, self.identifier) def set_name(self, slack_name): self.name = "#" + slack_name + def refresh(self): + return self.rename() def rename(self): - pass - #self.name = "?" + self.slack_name - #w.buffer_set(self.channel_buffer, "short_name", self.name) + if self.channel_buffer: + if self.is_someone_typing(): + new_name = ">{}".format(self.formatted_name()[1:]) + else: + new_name = self.formatted_name() + if self.current_short_name != new_name: + self.current_short_name = new_name + w.buffer_set(self.channel_buffer, "short_name", new_name) + return True + return False def formatted_name(self): return self.name def update_from_message_json(self, message_json): @@ -664,7 +686,7 @@ class SlackChannel(object): w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) else: w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) - buffer_list_update_next() + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) if self.unread_count != 0 and not self.muted: w.buffer_set(self.channel_buffer, "hotlist", "1") def destroy_buffer(self, update_remote): @@ -714,6 +736,8 @@ class SlackChannel(object): timestamp = int(timestamp) modify_buffer_line(self.channel_buffer, text, timestamp, time_id) return True + def is_visible(self): + return w.buffer_get_integer(self.channel_buffer, "hidden") == 0 def get_history(self): #if config.cache_messages: # for message in message_cache[self.identifier]: @@ -734,7 +758,48 @@ class SlackChannel(object): self.eventrouter.receive(s) def sorted_message_keys(self): return sorted(self.messages) + # Typing related + def set_typing(self, user): + if self.channel_buffer and self.is_visible(): + self.typing[user] = time.time() + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + def unset_typing(self, user): + if self.channel_buffer and self.is_visible(): + u = self.typing.get(user, None) + if u: + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + def is_someone_typing(self): + """ + Walks through dict of typing folks in a channel and fast + returns if any of them is actively typing. If none are, + nulls the dict and returns false. + """ + for user, timestamp in self.typing.iteritems(): + if timestamp + 4 > time.time(): + return True + if len(self.typing) > 0: + self.typing = {} + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + return False + def get_typing_list(self): + """ + Returns the names of everyone in the channel who is currently typing. + """ + typing = [] + for user, timestamp in self.typing.iteritems(): + if timestamp + 4 > time.time(): + typing.append(user) + return typing + + def mark_read(self, update_remote=True): + if self.channel_buffer: + w.buffer_set(self.channel_buffer, "unread", "") + if update_remote: + self.last_read = time.time() + self.update_read_marker(self.last_read) + def update_read_marker(self, time): + async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": time}) class SlackDMChannel(SlackChannel): """ @@ -756,19 +821,34 @@ class SlackDMChannel(SlackChannel): super(SlackDMChannel, self).create_buffer() w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') def update_color(self): - if config.colorize_nicks: + if config.colorize_private_chats: self.color_name = w.info_get('irc_nick_color_name', self.name.encode('utf-8')) self.color = w.color(self.color_name) else: self.color = "" self.color_name = "" def formatted_name(self, prepend="", enable_color=True): - if config.colorize_nicks and enable_color: + if config.colorize_private_chats and enable_color: print_color = self.color else: print_color = "" return print_color + prepend + self.name + def refresh(self): + return self.rename() + + def rename(self): + if self.channel_buffer: + if self.team.is_user_present(self.user): + new_name = "+{}".format(self.formatted_name()) + else: + new_name = " {}".format(self.formatted_name()) + if self.current_short_name != new_name: + self.current_short_name = new_name + w.buffer_set(self.channel_buffer, "short_name", new_name) + return True + return False + class SlackGroupChannel(SlackChannel): """ @@ -993,6 +1073,11 @@ def process_manual_presence_change(message_json, eventrouter, **kwargs): def process_presence_change(message_json, eventrouter, **kwargs): kwargs["user"].presence = message_json["presence"] +def process_user_typing(message_json, eventrouter, **kwargs): + channel = kwargs["channel"] + team = kwargs["team"] + if channel: + channel.set_typing(team.users.get(message_json["user"]).name) def process_pong(message_json, eventrouter, **kwargs): pass @@ -1588,9 +1673,6 @@ if __name__ == "__main__": previous_buffer = None slack_buffer = None - buffer_list_update = False - previous_buffer_list_update = 0 - never_away = False hide_distractions = False hotlist = w.infolist_get("hotlist", "", "") -- cgit From 41ccc05e014e4f3cea3fbab6b291aa44aceeb619 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 13:36:33 -0800 Subject: avoid useless updates of read marker --- wee_slack.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 4a009b2..692ccf1 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -338,7 +338,7 @@ class WeechatController(object): return self.buffers.get(buffer_ptr, None) def get_all(self, buffer_ptr): return self.buffers - def get_previous_buffer(self): + def get_previous_buffer_ptr(self): return self.previous_buffer def set_previous_buffer(self, data): self.previous_buffer = data @@ -439,18 +439,18 @@ def buffer_switch_callback(signal, sig_type, data): """ eventrouter = eval(signal) - prev_buffer = eventrouter.weechat_controller.get_previous_buffer() + prev_buffer_ptr = eventrouter.weechat_controller.get_previous_buffer_ptr() # this is to see if we need to gray out things in the buffer list - if eventrouter.weechat_controller.get_channel_from_buffer_ptr(prev_buffer): - pass - #channels.find(previous_buffer).mark_read() + prev = eventrouter.weechat_controller.get_channel_from_buffer_ptr(prev_buffer_ptr) + if prev: + prev.mark_read() new_channel = eventrouter.weechat_controller.get_channel_from_buffer_ptr(data) if new_channel: if not new_channel.got_history: new_channel.get_history() - eventrouter.weechat_controller.set_previous_buffer(data) + eventrouter.weechat_controller.set_previous_buffer(data) return w.WEECHAT_RC_OK def buffer_list_update_callback(data, somecount): @@ -591,6 +591,8 @@ class SlackTeam(object): return True else: return False + def mark_read(self): + pass def set_connected(self): self.connected = True def set_disconnected(self): @@ -627,6 +629,7 @@ class SlackChannel(object): self.team = None self.got_history = False self.messages = {} + self.new_messages = False self.typing = {} self.type = 'channel' for key, value in kwargs.items(): @@ -701,6 +704,7 @@ class SlackChannel(object): t, time_id = timestamp.split('.', 1) time_int = int(t) if self.channel_buffer: + self.new_messages = True #w.prnt(self.channel_buffer, "{}\t{}".format(nick, text)) tags = '' data = "{}\t{}".format(nick, text) @@ -715,6 +719,7 @@ class SlackChannel(object): request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} dbg(request, True) self.team.send_to_websocket(request) + self.mark_read() def store_message(self, message, team, from_me=False): if from_me: message.message_json["user"] = team.myidentifier @@ -790,16 +795,16 @@ class SlackChannel(object): if timestamp + 4 > time.time(): typing.append(user) return typing - def mark_read(self, update_remote=True): - if self.channel_buffer: - w.buffer_set(self.channel_buffer, "unread", "") - if update_remote: - self.last_read = time.time() - self.update_read_marker(self.last_read) - - def update_read_marker(self, time): - async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": time}) + if self.new_messages: + if self.channel_buffer: + w.buffer_set(self.channel_buffer, "unread", "") + w.buffer_set(self.channel_buffer, "hotlist", "-1") + if update_remote: + last_read = time.time() + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": last_read}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + self.new_messages = False class SlackDMChannel(SlackChannel): """ @@ -1211,8 +1216,7 @@ def process_reply(message_json, eventrouter, **kwargs): def process_channel_marked(message_json, eventrouter, **kwargs): channel = kwargs["channel"] dbg(channel, True) - #channel.mark_read(False) - #w.buffer_set(channel.channel_buffer, "hotlist", "-1") + channel.mark_read(False) def process_channel_joined(message_json, eventrouter, **kwargs): item = message_json["channel"] -- cgit From bcc417562c2067095b03df383b0579da3e65d780 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 13:42:13 -0800 Subject: markers fully work --- wee_slack.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 692ccf1..be94d4d 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1217,6 +1217,12 @@ def process_channel_marked(message_json, eventrouter, **kwargs): channel = kwargs["channel"] dbg(channel, True) channel.mark_read(False) +def process_group_marked(message_json, eventrouter, **kwargs): + process_channel_marked(message_json, eventrouter, **kwargs) +def process_im_marked(message_json, eventrouter, **kwargs): + process_channel_marked(message_json, eventrouter, **kwargs) +def process_mpim_marked(message_json, eventrouter, **kwargs): + process_channel_marked(message_json, eventrouter, **kwargs) def process_channel_joined(message_json, eventrouter, **kwargs): item = message_json["channel"] -- cgit From dde53037734027abcc44d01175fecefda13dea14 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 13:42:35 -0800 Subject: mark complete --- wee_slack.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index be94d4d..1859841 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1214,6 +1214,9 @@ def process_reply(message_json, eventrouter, **kwargs): dbg("Unexpected reply") def process_channel_marked(message_json, eventrouter, **kwargs): + """ + complete + """ channel = kwargs["channel"] dbg(channel, True) channel.mark_read(False) -- cgit From 4b6fe56a99b114cdacddd2429c83a9cee160c89e Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 17:43:41 -0800 Subject: that was super not fun to debug --- _pytest/conftest.py | 5 ++- _pytest/test_eventrouter.py | 26 ++++++++++----- wee_slack.py | 78 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/_pytest/conftest.py b/_pytest/conftest.py index 0212601..07dfc5e 100644 --- a/_pytest/conftest.py +++ b/_pytest/conftest.py @@ -1,4 +1,3 @@ -import pickle import json import pytest import sys @@ -29,9 +28,9 @@ def mock_websocket(): @pytest.fixture def realish_eventrouter(): e = EventRouter() - pickled_data = pickle.dumps(SlackRequest('xoxoxoxox', "rtm.start", {"meh": "blah"})) + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.start", {"meh": "blah"})) rtmstartdata = open('_pytest/data/http/rtm.start.json', 'r').read() - e.receive_httprequest_callback(pickled_data, 1, 0, rtmstartdata, 4) + e.receive_httprequest_callback(context, 1, 0, rtmstartdata, 4) e.handle_next() return e diff --git a/_pytest/test_eventrouter.py b/_pytest/test_eventrouter.py index fc6a6a9..b235512 100644 --- a/_pytest/test_eventrouter.py +++ b/_pytest/test_eventrouter.py @@ -1,5 +1,4 @@ import pytest -import pickle from wee_slack import EventRouter, ProcessNotImplemented, SlackRequest def test_EventRouter(mock_weechat): @@ -35,22 +34,33 @@ def test_EventRouter(mock_weechat): def test_EventRouterReceivedata(mock_weechat): e = EventRouter() - pickled_data = pickle.dumps(SlackRequest('xoxoxoxox', "rtm.startold", {"meh": "blah"})) - e.receive_httprequest_callback(pickled_data, 1, -1, ' {"JSON": "MEH", ', 4) + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.startold", {"meh": "blah"})) + print context + e.receive_httprequest_callback(context, 1, -1, ' {"JSON": "MEH", ', 4) #print len(e.reply_buffer) - e.receive_httprequest_callback(pickled_data, 1, -1, ' "JSON2": "MEH", ', 4) + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.startold", {"meh": "blah"})) + print context + e.receive_httprequest_callback(context, 1, -1, ' "JSON2": "MEH", ', 4) #print len(e.reply_buffer) - e.receive_httprequest_callback(pickled_data, 1, 0, ' "JSON3": "MEH"}', 4) + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.startold", {"meh": "blah"})) + print context + e.receive_httprequest_callback(context, 1, 0, ' "JSON3": "MEH"}', 4) #print len(e.reply_buffer) try: e.handle_next() + e.handle_next() + e.handle_next() + e.handle_next() except: pass - pickled_data = pickle.dumps(SlackRequest('xoxoxoxox', "rtm.start", {"meh": "blah"})) + print e.context + #assert False + + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.start", {"meh": "blah"})) rtmstartdata = open('_pytest/data/http/rtm.start.json', 'r').read() - e.receive_httprequest_callback(pickled_data, 1, 0, rtmstartdata[:5000], 4) - e.receive_httprequest_callback(pickled_data, 1, 0, rtmstartdata[5000:], 4) + e.receive_httprequest_callback(context, 1, -1, rtmstartdata[:5000], 4) + e.receive_httprequest_callback(context, 1, 0, rtmstartdata[5000:], 4) e.handle_next() #print len(e.reply_buffer) diff --git a/wee_slack.py b/wee_slack.py index 1859841..1aea0c5 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -11,7 +11,8 @@ import sys import traceback import collections import ssl -#import random +import random +import string from websocket import create_connection, WebSocketConnectionClosedException @@ -97,6 +98,7 @@ class EventRouter(object): """ self.queue = [] self.teams = {} + self.context = {} self.weechat_controller = WeechatController(self) self.previous_buffer = "" self.reply_buffer = {} @@ -132,6 +134,33 @@ class EventRouter(object): f.write("{}".format(json.dumps(message_json))) f.close() + def store_context(self, data): + """ + A place to store data and vars needed by callback returns. We need this because + weechat's "callback_data" has a limited size and weechat will crash if you exceed + this size. + """ + identifier = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(40)) + self.context[identifier] = data + return identifier + + def retrieve_context(self, identifier): + """ + A place to retrieve data and vars needed by callback returns. We need this because + weechat's "callback_data" has a limited size and weechat will crash if you exceed + this size. + """ + data = self.context.get(identifier, None) + if data: + return data + + def delete_context(self, identifier): + """ + Requests can span multiple requests, so we may need to delete this as a last step + """ + if identifier in self.context: + del self.context[identifier] + def shutdown(self): """ complete @@ -189,7 +218,7 @@ class EventRouter(object): It is then populated with metadata here so we can identify where the request originated and route properly. """ - request_metadata = pickle.loads(data) + request_metadata = self.retrieve_context(data) dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out)), main_buffer=True) if return_code == 0: if request_metadata.response_id in self.reply_buffer: @@ -205,8 +234,10 @@ class EventRouter(object): if self.recording: self.record_event(j, 'wee_slack_process_method') self.receive_json(json.dumps(j)) + self.delete_context(data) except: - dbg("HTTP REQUEST CALLBACK FAILED") + dbg("HTTP REQUEST CALLBACK FAILED", True) + dbg(data, True) pass elif return_code != -1: self.reply_buffer.pop(request_metadata.response_id, None) @@ -305,7 +336,7 @@ class WeechatController(object): def __init__(self, eventrouter): self.eventrouter = eventrouter self.buffers = {} - self.previous_buffer = "" + self.previous_buffer = None self.buffer_list_stale = False def iter_buffers(self): for b in self.buffers: @@ -354,13 +385,13 @@ def local_process_async_slack_api_request(request, event_router): """ complete Sends an API request to Slack. You'll need to give this a well formed SlackRequest object. + DEBUGGING!!! The context here cannot be very large. Weechat will crash. """ if not event_router.shutting_down: weechat_request = 'url:{}'.format(request.request_string()) - print weechat_request params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} request.tried() - context = pickle.dumps(request) + context = event_router.store_context(request) w.hook_process_hashtable(weechat_request, params, config.slack_timeout, "receive_httprequest_callback", context) ###### New Callbacks @@ -660,6 +691,11 @@ class SlackChannel(object): for key, value in message_json.items(): setattr(self, key, value) def open_if_we_should(self, force=False): + try: + if self.is_archived: + return + except: + pass if force: self.create_buffer() else: @@ -1661,6 +1697,34 @@ class PluginConfig(object): return int(w.config_get_plugin(key)) +# to Trace execution, add `setup_trace()` to startup +# and to a function and sys.settrace(trace_calls) to a function +def setup_trace(): + global f + now = time.time() + f = open('{}/{}-trace.json'.format(RECORD_DIR, now), 'w') + +def trace_calls(frame, event, arg): + global f + if event != 'call': + return + co = frame.f_code + func_name = co.co_name + if func_name == 'write': + # Ignore write() calls from print statements + return + func_line_no = frame.f_lineno + func_filename = co.co_filename + caller = frame.f_back + caller_line_no = caller.f_lineno + caller_filename = caller.f_code.co_filename + print >> f, 'Call to %s on line %s of %s from line %s of %s' % \ + (func_name, func_line_no, func_filename, + caller_line_no, caller_filename) + f.flush() + return + + # Main if __name__ == "__main__": @@ -1672,6 +1736,8 @@ if __name__ == "__main__": w.prnt("", "\nERROR: Weechat version 1.3+ is required to use {}.\n\n".format(SCRIPT_NAME)) else: + #setup_trace() + WEECHAT_HOME = w.info_get("weechat_dir", "") CACHE_NAME = "slack.cache" STOP_TALKING_TO_SLACK = False -- cgit From 77a8d219deec4c5be886297ce3eb095169acc87f Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 17:50:55 -0800 Subject: make main buffer less noisy --- wee_slack.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 1aea0c5..eddbd01 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -219,7 +219,7 @@ class EventRouter(object): where the request originated and route properly. """ request_metadata = self.retrieve_context(data) - dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out)), main_buffer=True) + dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out))) if return_code == 0: if request_metadata.response_id in self.reply_buffer: self.reply_buffer[request_metadata.response_id] += out @@ -237,7 +237,6 @@ class EventRouter(object): self.delete_context(data) except: dbg("HTTP REQUEST CALLBACK FAILED", True) - dbg(data, True) pass elif return_code != -1: self.reply_buffer.pop(request_metadata.response_id, None) @@ -262,7 +261,7 @@ class EventRouter(object): processing. Object must be known to handle_next or be JSON. """ - dbg("RECEIVED FROM QUEUE", main_buffer=True) + dbg("RECEIVED FROM QUEUE") self.queue.append(dataobj) def handle_next(self): """ @@ -326,7 +325,10 @@ def handle_next(*args): This is just a place to call the event router globally. This is a dirty hack. There must be a better way. """ - EVENTROUTER.handle_next() + try: + EVENTROUTER.handle_next() + except: + pass return w.WEECHAT_RC_OK class WeechatController(object): @@ -753,7 +755,6 @@ class SlackChannel(object): dbg(message) print self.team request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} - dbg(request, True) self.team.send_to_websocket(request) self.mark_read() def store_message(self, message, team, from_me=False): @@ -972,10 +973,10 @@ class SlackMessage(object): return render(self.message_json, self.team, self.channel, force) + self.suffix def change_text(self, new_text): self.message_json["text"] = new_text - dbg(self.message_json, True) + dbg(self.message_json) def change_suffix(self, new_suffix): self.suffix = new_suffix - dbg(self.message_json, True) + dbg(self.message_json) def get_sender(self, utf8=True): name = u"" if 'bot_id' in self.message_json and self.message_json['bot_id'] is not None: @@ -1236,7 +1237,7 @@ def process_reply(message_json, eventrouter, **kwargs): channel = team.channels[c] m = SlackMessage(original_message_json, team, channel) # m = Message(message_json, server=server) - dbg(m, True) + #dbg(m, True) #if "type" in message_json: # if message_json["type"] == "message" and "channel" in message_json.keys(): @@ -1254,7 +1255,6 @@ def process_channel_marked(message_json, eventrouter, **kwargs): complete """ channel = kwargs["channel"] - dbg(channel, True) channel.mark_read(False) def process_group_marked(message_json, eventrouter, **kwargs): process_channel_marked(message_json, eventrouter, **kwargs) -- cgit From 92f74774c9b810946fb850dc0b70088b64e59558 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 20:08:51 -0800 Subject: add slack ts type --- _pytest/test_slackts.py | 19 ++++++++ wee_slack.py | 120 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 115 insertions(+), 24 deletions(-) create mode 100644 _pytest/test_slackts.py diff --git a/_pytest/test_slackts.py b/_pytest/test_slackts.py new file mode 100644 index 0000000..afe3719 --- /dev/null +++ b/_pytest/test_slackts.py @@ -0,0 +1,19 @@ +from wee_slack import SlackTS + + +def test_slackts(): + a = SlackTS("1234.0") + print a + b = SlackTS("1234.002") + print b + print type(a.major) + print type(a.minor) + print type(b.major) + print type(b.minor) + print a.minor + assert a < b + c = SlackTS() + assert c > b + + print str(SlackTS()) + #assert False diff --git a/wee_slack.py b/wee_slack.py index eddbd01..c21aabf 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -535,11 +535,8 @@ class SlackRequest(object): makes a SHA of the requst url and current time so we can re-tag this on the way back through. """ def __init__(self, token, request, post_data={}, **kwargs): - print '=================' for key, value in kwargs.items(): setattr(self, key, value) - print "{} {}".format(key, value) - print '=================' self.tries = 0 self.domain = 'api.slack.com' self.request = request @@ -606,6 +603,8 @@ class SlackTeam(object): if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': w.buffer_merge(self.server_buffer, w.buffer_search_main()) w.buffer_set(self.server_buffer, "nicklist", "1") + def buffer_prnt(self, data): + w.prnt_date_tags(self.server_buffer, int(time.time()), tag("backlog"), data) def get_channel_map(self): return {v.slack_name: k for k, v in self.channels.iteritems()} def get_username_map(self): @@ -647,7 +646,6 @@ class SlackTeam(object): dbg("Unexpected error: {}\nSent: {}".format(sys.exc_info()[0], data)) self.connected = False - class SlackChannel(object): """ Represents an individual slack channel. @@ -655,9 +653,13 @@ class SlackChannel(object): def __init__(self, eventrouter, **kwargs): # We require these two things for a vaid object, # the rest we can just learn from slack + for key, value in kwargs.items(): + setattr(self, key, value) self.eventrouter = eventrouter self.slack_name = kwargs["name"] self.identifier = kwargs["id"] + self.last_read = SlackTS(kwargs.get("last_read", SlackTS())) + #print self.last_read self.channel_buffer = None self.team = None self.got_history = False @@ -665,8 +667,6 @@ class SlackChannel(object): self.new_messages = False self.typing = {} self.type = 'channel' - for key, value in kwargs.items(): - setattr(self, key, value) self.set_name(self.slack_name) #short name relates to the localvar we change for typing indication self.current_short_name = self.name @@ -738,22 +738,34 @@ class SlackChannel(object): if update_remote and not self.eventrouter.shutting_down: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) EVENTROUTER.receive(s) - def buffer_prnt(self, nick, text, timestamp, *args): - t, time_id = timestamp.split('.', 1) - time_int = int(t) + def buffer_prnt(self, nick, text, timestamp, **kwargs): + ts = SlackTS(timestamp) if self.channel_buffer: - self.new_messages = True + print type(ts) + print ts + print type(self.last_read) + print self.last_read + if ts < SlackTS(self.last_read): + dbg("{} {}".format(ts, self.last_read), True) + tags = tag("backlog") + elif self.type in ["im", "mpdm"]: + tags = tag("dm") + else: + tags = tag("default") + #otype = kwargs.get('output_type', 'default') + #tags = tag(otype) + + #if otype != 'backlog': + # self.new_messages = True #w.prnt(self.channel_buffer, "{}\t{}".format(nick, text)) - tags = '' data = "{}\t{}".format(nick, text) - w.prnt_date_tags(self.channel_buffer, time_int, tags, data) - modify_print_time(self.channel_buffer, time_id, time_int) - #dbg("should buffer print {} {}".format(nick, text), True) + + w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) + modify_print_time(self.channel_buffer, ts.minor, ts.major) def send_message(self, message): #team = self.eventrouter.teams[self.team] message = linkify_text(message, self.team, self) dbg(message) - print self.team request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} self.team.send_to_websocket(request) self.mark_read() @@ -832,13 +844,16 @@ class SlackChannel(object): if timestamp + 4 > time.time(): typing.append(user) return typing - def mark_read(self, update_remote=True): + def mark_read(self, ts=None, update_remote=True): + if not ts: + ts = SlackTS() if self.new_messages: + self.last_read = ts if self.channel_buffer: w.buffer_set(self.channel_buffer, "unread", "") w.buffer_set(self.channel_buffer, "hotlist", "-1") if update_remote: - last_read = time.time() + last_read = ts s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": last_read}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) self.new_messages = False @@ -1031,6 +1046,34 @@ class WeeSlackMetadata(object): def jsonify(self): return self.meta +class SlackTS(object): + def __init__(self, ts=None): + try: + if ts: + self.major, self.minor = [int(x) for x in ts.split('.', 1)] + else: + self.major = int(time.time()) + self.minor = 0 + except: + print ts + raise + def __cmp__(self, other): + if self.major < other.major: + return -1 + elif self.major > other.major: + return 1 + elif self.major == other.major: + if self.minor < other.minor: + return -1 + elif self.minor > other.minor: + return 1 + else: + return 0 + def __repr__(self): + return "{}.{}".format(self.major, self.minor) + def split(self, *args, **kwargs): + return [self.major, self.minor] + ###### New handlers def handle_rtmstart(login_data, eventrouter): @@ -1086,6 +1129,14 @@ def handle_rtmstart(login_data, eventrouter): dbg("websocket connection error: {}".format(e)) return False + t.buffer_prnt('Connected to Slack') + t.buffer_prnt('{:<20} {}'.format(u"Websocket URL", login_data["url"])) + t.buffer_prnt('{:<20} {}'.format(u"User name", login_data["self"]["name"])) + t.buffer_prnt('{:<20} {}'.format(u"User ID", login_data["self"]["id"])) + t.buffer_prnt('{:<20} {}'.format(u"Team name", login_data["team"]["name"])) + t.buffer_prnt('{:<20} {}'.format(u"Team domain", login_data["team"]["domain"])) + t.buffer_prnt('{:<20} {}'.format(u"Team id", login_data["team"]["id"])) + dbg("connected to {}".format(t.domain)) #self.identifier = self.domain @@ -1103,6 +1154,7 @@ 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] kwargs['channel'] = kwargs['team'].channels[request_metadata.channel_identifier] + kwargs['output_type'] = "backlog" for message in reversed(message_json["messages"]): process_message(message, eventrouter, **kwargs) @@ -1158,13 +1210,13 @@ def process_message(message_json, eventrouter, store=True, **kwargs): text = text[1:-1] if message.sender != channel.server.nick: text = message.sender + " " + text - channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts) + channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, **kwargs) else: suffix = '' if 'edited' in message_json: suffix = ' (edited)' - channel.buffer_prnt(message.sender, text + suffix, message.ts) + channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) if store: channel.store_message(message, team) @@ -1229,7 +1281,7 @@ def process_reply(message_json, eventrouter, **kwargs): original_message_json = team.ws_replies[identifier] del team.ws_replies[identifier] if "ts" in message_json: - original_message_json["ts"] = message_json["ts"] + original_message_json["ts"] = SlackTS(message_json["ts"]) else: dbg("no reply ts {}".format(message_json)) @@ -1255,7 +1307,8 @@ def process_channel_marked(message_json, eventrouter, **kwargs): complete """ channel = kwargs["channel"] - channel.mark_read(False) + ts = kwargs["ts"] + channel.mark_read(False, ts) def process_group_marked(message_json, eventrouter, **kwargs): process_channel_marked(message_json, eventrouter, **kwargs) def process_im_marked(message_json, eventrouter, **kwargs): @@ -1280,7 +1333,6 @@ def process_im_open(message_json, eventrouter, **kwargs): def process_im_close(message_json, eventrouter, **kwargs): item = message_json cbuf = kwargs['team'].channels[item["channel"]].channel_buffer - print cbuf eventrouter.weechat_controller.unregister_buffer(cbuf, False, True) def process_reaction_added(message_json, eventrouter, **kwargs): @@ -1351,9 +1403,9 @@ def linkify_text(message, team, channel): message = message.split(' ') for item in enumerate(message): targets = re.match('.*([@#])([\w.-]+[\w. -])(\W*)', item[1]) - print targets + #print targets if targets and targets.groups()[0] == '@': - print targets.groups() + #print targets.groups() named = targets.groups() if named[1] in ["group", "channel", "here"]: message[item[0]] = "".format(named[1]) @@ -1547,6 +1599,26 @@ def modify_print_time(buffer, new_id, time): return w.WEECHAT_RC_OK +def tag(tagset, user="unknown user"): + default_tag = "nick_" + user + tagsets = { + #when replaying something old + "backlog": "no_highlight,notify_none,logger_backlog_end", + #when posting messages to a muted channel + "muted": "no_highlight,notify_none,logger_backlog_end", + #when my nick is in the message + "highlightme": "notify_highlight,log1", + #when receiving a direct message + "dm": "notify_private,notify_message,log1,irc_privmsg", + #when this is a join/leave, attach for smart filter ala: + #if user in [x.strip() for x in w.prefix("join"), w.prefix("quit")] + "joinleave": "irc_smart_filter", + #catchall ? + "default": "notify_message,log1,irc_privmsg", + } + return default_tag + "," + tagsets[tagset] + + ###### New/converted command_ commands def slack_command_cb(data, current_buffer, args): -- cgit From 01b9f3c2525ecaf331175f2e447423e915c2d476 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 21:40:02 -0800 Subject: fixed crap --- _pytest/test_process_message.py | 4 ++ _pytest/test_slackts.py | 31 ++++++++------- wee_slack.py | 84 ++++++++++++++++++++--------------------- 3 files changed, 64 insertions(+), 55 deletions(-) diff --git a/_pytest/test_process_message.py b/_pytest/test_process_message.py index 9d238d6..e2447f7 100644 --- a/_pytest/test_process_message.py +++ b/_pytest/test_process_message.py @@ -42,3 +42,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): e.receive_ws_callback(t) e.handle_next() + + #assert e.teams[t].channels['C407ABS94'].messages.keys()[0] == '1485976151.00016' + #assert False + diff --git a/_pytest/test_slackts.py b/_pytest/test_slackts.py index afe3719..87f43a7 100644 --- a/_pytest/test_slackts.py +++ b/_pytest/test_slackts.py @@ -2,18 +2,23 @@ from wee_slack import SlackTS def test_slackts(): - a = SlackTS("1234.0") - print a - b = SlackTS("1234.002") - print b - print type(a.major) - print type(a.minor) - print type(b.major) - print type(b.minor) - print a.minor - assert a < b + base = SlackTS("1485976156.000017") + + b = SlackTS("1485976156.000016") + c = SlackTS("1485976156.000018") + + d = SlackTS("1485976155.000017") + e = SlackTS("1485976157.000017") + + assert base > b + assert base < c + + assert base > d + assert base < e + c = SlackTS() - assert c > b + assert c > base - print str(SlackTS()) - #assert False + assert base == "1485976156.000017" + assert base > "1485976156.000016" + assert base < "1485976156.000018" diff --git a/wee_slack.py b/wee_slack.py index c21aabf..79e5d4c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -604,7 +604,7 @@ class SlackTeam(object): w.buffer_merge(self.server_buffer, w.buffer_search_main()) w.buffer_set(self.server_buffer, "nicklist", "1") def buffer_prnt(self, data): - w.prnt_date_tags(self.server_buffer, int(time.time()), tag("backlog"), data) + w.prnt_date_tags(self.server_buffer, SlackTS().major, tag("backlog"), data) def get_channel_map(self): return {v.slack_name: k for k, v in self.channels.iteritems()} def get_username_map(self): @@ -739,29 +739,20 @@ class SlackChannel(object): s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) EVENTROUTER.receive(s) def buffer_prnt(self, nick, text, timestamp, **kwargs): + data = "{}\t{}".format(nick, text) ts = SlackTS(timestamp) if self.channel_buffer: - print type(ts) - print ts - print type(self.last_read) - print self.last_read if ts < SlackTS(self.last_read): - dbg("{} {}".format(ts, self.last_read), True) tags = tag("backlog") elif self.type in ["im", "mpdm"]: tags = tag("dm") + self.new_messages = True else: tags = tag("default") - #otype = kwargs.get('output_type', 'default') - #tags = tag(otype) - - #if otype != 'backlog': - # self.new_messages = True - #w.prnt(self.channel_buffer, "{}\t{}".format(nick, text)) - data = "{}\t{}".format(nick, text) + self.new_messages = True w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) - modify_print_time(self.channel_buffer, ts.minor, ts.major) + modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) def send_message(self, message): #team = self.eventrouter.teams[self.team] message = linkify_text(message, self.team, self) @@ -772,13 +763,14 @@ class SlackChannel(object): def store_message(self, message, team, from_me=False): if from_me: message.message_json["user"] = team.myidentifier - self.messages[message.ts] = message + self.messages[SlackTS(message.ts)] = message if len(self.messages.keys()) > SCROLLBACK_SIZE: mk = self.messages.keys() mk.sort() for k in mk[:SCROLLBACK_SIZE]: del self.messages[k] def change_message(self, ts, text=None, suffix=None): + ts = SlackTS(ts) if ts in self.messages: m = self.messages[ts] if text: @@ -786,9 +778,7 @@ class SlackChannel(object): if suffix: m.change_suffix(suffix) text = m.render(force=True) - timestamp, time_id = ts.split(".", 2) - timestamp = int(timestamp) - modify_buffer_line(self.channel_buffer, text, timestamp, time_id) + modify_buffer_line(self.channel_buffer, text, ts.major, ts.minor) return True def is_visible(self): return w.buffer_get_integer(self.channel_buffer, "hidden") == 0 @@ -853,8 +843,8 @@ class SlackChannel(object): w.buffer_set(self.channel_buffer, "unread", "") w.buffer_set(self.channel_buffer, "hotlist", "-1") if update_remote: - last_read = ts - s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": last_read}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": self.last_read}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + print s.request self.eventrouter.receive(s) self.new_messages = False @@ -931,6 +921,8 @@ class SlackMPDMChannel(SlackChannel): def set_name(self, n): self.name = "|".join("-".join(n.split("-")[1:-1]).split("--")) self.type = "group" + def rename(self): + pass class SlackUser(object): """ @@ -983,7 +975,7 @@ class SlackMessage(object): self.message_json = message_json self.sender = self.get_sender() self.suffix = '' - self.ts = message_json['ts'] + self.ts = SlackTS(message_json['ts']) def render(self, force=False): return render(self.message_json, self.team, self.channel, force) + self.suffix def change_text(self, new_text): @@ -1048,31 +1040,40 @@ class WeeSlackMetadata(object): class SlackTS(object): def __init__(self, ts=None): - try: - if ts: - self.major, self.minor = [int(x) for x in ts.split('.', 1)] - else: - self.major = int(time.time()) - self.minor = 0 - except: - print ts - raise + if ts: + self.major, self.minor = [int(x) for x in ts.split('.', 1)] + else: + self.major = int(time.time()) + self.minor = 0 def __cmp__(self, other): - if self.major < other.major: - return -1 - elif self.major > other.major: - return 1 - elif self.major == other.major: - if self.minor < other.minor: + if isinstance(other, SlackTS): + if self.major < other.major: return -1 - elif self.minor > other.minor: + elif self.major > other.major: return 1 - else: + elif self.major == other.major: + if self.minor < other.minor: + return -1 + elif self.minor > other.minor: + return 1 + else: + return 0 + else: + s = self.__str__() + if s < other: + return -1 + elif s > other: + return 1 + elif s == other: return 0 def __repr__(self): - return "{}.{}".format(self.major, self.minor) + return str("{0}.{1:06d}".format(self.major, self.minor)) def split(self, *args, **kwargs): return [self.major, self.minor] + def majorstr(self): + return str(self.major) + def minorstr(self): + return str(self.minor) ###### New handlers @@ -1201,7 +1202,6 @@ def process_message(message_json, eventrouter, store=True, **kwargs): else: message = SlackMessage(message_json, team, channel) - #message = Message(message_json, server=team, channel=channel) text = message.render() #print text @@ -1281,7 +1281,7 @@ def process_reply(message_json, eventrouter, **kwargs): original_message_json = team.ws_replies[identifier] del team.ws_replies[identifier] if "ts" in message_json: - original_message_json["ts"] = SlackTS(message_json["ts"]) + original_message_json["ts"] = message_json["ts"] else: dbg("no reply ts {}".format(message_json)) @@ -1300,7 +1300,7 @@ def process_reply(message_json, eventrouter, **kwargs): process_message(m.message_json, eventrouter, channel=channel, team=team) dbg("REPLY {}".format(message_json)) except KeyError: - dbg("Unexpected reply") + dbg("Unexpected reply {}".format(message_json)) def process_channel_marked(message_json, eventrouter, **kwargs): """ -- cgit From 4c8cf73778414336f979ef9efc3712ca6891c5f2 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 22:00:16 -0800 Subject: backlog all works now --- wee_slack.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 79e5d4c..d7dae0c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -742,8 +742,11 @@ class SlackChannel(object): data = "{}\t{}".format(nick, text) ts = SlackTS(timestamp) if self.channel_buffer: - if ts < SlackTS(self.last_read): + #backlog messages - we will update the read marker as we print these + backlog = False + if ts <= SlackTS(self.last_read): tags = tag("backlog") + backlog = True elif self.type in ["im", "mpdm"]: tags = tag("dm") self.new_messages = True @@ -753,13 +756,15 @@ class SlackChannel(object): w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) + if backlog: + self.mark_read(ts, update_remote=False, force=True) def send_message(self, message): #team = self.eventrouter.teams[self.team] message = linkify_text(message, self.team, self) dbg(message) request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} self.team.send_to_websocket(request) - self.mark_read() + self.mark_read(force=True) def store_message(self, message, team, from_me=False): if from_me: message.message_json["user"] = team.myidentifier @@ -834,17 +839,15 @@ class SlackChannel(object): if timestamp + 4 > time.time(): typing.append(user) return typing - def mark_read(self, ts=None, update_remote=True): + def mark_read(self, ts=None, update_remote=True, force=False): if not ts: ts = SlackTS() - if self.new_messages: - self.last_read = ts + if self.new_messages or force: if self.channel_buffer: w.buffer_set(self.channel_buffer, "unread", "") w.buffer_set(self.channel_buffer, "hotlist", "-1") if update_remote: - s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": self.last_read}, team_hash=self.team.team_hash, channel_identifier=self.identifier) - print s.request + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": ts}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) self.new_messages = False @@ -1308,7 +1311,7 @@ def process_channel_marked(message_json, eventrouter, **kwargs): """ channel = kwargs["channel"] ts = kwargs["ts"] - channel.mark_read(False, ts) + channel.mark_read(ts=ts, update_remote=False) def process_group_marked(message_json, eventrouter, **kwargs): process_channel_marked(message_json, eventrouter, **kwargs) def process_im_marked(message_json, eventrouter, **kwargs): -- cgit From 6c48a07fc9a9a858fb5e0e6042053b1323ba2078 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 23:26:09 -0800 Subject: thats all folks --- wee_slack.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index d7dae0c..1fb05e4 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -459,8 +459,7 @@ def buffer_input_callback(signal, buffer_ptr, data): # channel.change_previous_message(old.decode("utf-8"), new.decode("utf-8"), flags) else: channel.send_message(data) - # channel.buffer_prnt(channel.server.nick, data) -# channel.mark_read(True) + channel.mark_read(update_remote=True, force=True) return w.WEECHAT_RC_ERROR def buffer_switch_callback(signal, sig_type, data): @@ -687,8 +686,8 @@ class SlackChannel(object): w.buffer_set(self.channel_buffer, "short_name", new_name) return True return False - def formatted_name(self): - return self.name + def formatted_name(self, prepend="#", **kwargs): + return prepend + self.slack_name def update_from_message_json(self, message_json): for key, value in message_json.items(): setattr(self, key, value) @@ -711,25 +710,28 @@ class SlackChannel(object): self.team = team def create_buffer(self): """ - incomplete + incomplete (muted doesn't work) Creates the weechat buffer where the channel magic happens. """ if not self.channel_buffer: - self.channel_buffer = w.buffer_new("{}.{}".format(self.team.domain, self.name), "buffer_input_callback", "EVENTROUTER", "", "") + name = self.formatted_name(basic=True) + self.channel_buffer = w.buffer_new("{}.{}".format(self.team.domain, name), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) if self.type == "im": w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') else: w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') - w.buffer_set(self.channel_buffer, "localvar_set_channel", self.name) + w.buffer_set(self.channel_buffer, "localvar_set_channel", name) w.buffer_set(self.channel_buffer, "short_name", self.formatted_name()) if self.server.alias: w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) else: w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) self.eventrouter.weechat_controller.set_refresh_buffer_list(True) - if self.unread_count != 0 and not self.muted: + if self.unread_count != 0: w.buffer_set(self.channel_buffer, "hotlist", "1") + #if self.unread_count != 0 and not self.muted: + # w.buffer_set(self.channel_buffer, "hotlist", "1") def destroy_buffer(self, update_remote): if self.channel_buffer is not None: self.channel_buffer = None @@ -863,7 +865,6 @@ class SlackDMChannel(SlackChannel): self.type = 'im' self.update_color() self.set_name(self.slack_name) - #self.name = self.formatted_name(" ") def set_name(self, slack_name): self.name = slack_name def create_buffer(self): @@ -877,8 +878,8 @@ class SlackDMChannel(SlackChannel): else: self.color = "" self.color_name = "" - def formatted_name(self, prepend="", enable_color=True): - if config.colorize_private_chats and enable_color: + def formatted_name(self, prepend="", enable_color=True, basic=False): + if config.colorize_private_chats and enable_color and not basic: print_color = self.color else: print_color = "" @@ -911,6 +912,8 @@ class SlackGroupChannel(SlackChannel): self.set_name(self.slack_name) def set_name(self, slack_name): self.name = "#" + slack_name + def formatted_name(self, prepend="#", enable_color=True, basic=False): + return prepend + self.slack_name class SlackMPDMChannel(SlackChannel): """ @@ -921,9 +924,11 @@ class SlackMPDMChannel(SlackChannel): super(SlackMPDMChannel, self).__init__(eventrouter, **kwargs) n = kwargs.get('name') self.set_name(n) + self.type = "group" def set_name(self, n): self.name = "|".join("-".join(n.split("-")[1:-1]).split("--")) - self.type = "group" + def formatted_name(self, **kwargs): + return self.name def rename(self): pass @@ -1301,6 +1306,7 @@ def process_reply(message_json, eventrouter, **kwargs): # channels.find(message_json["channel"]).buffer_prnt(server.nick, m.render(), m.ts) process_message(m.message_json, eventrouter, channel=channel, team=team) + channel.mark_read(update_remote=True, force=True) dbg("REPLY {}".format(message_json)) except KeyError: dbg("Unexpected reply {}".format(message_json)) -- cgit From bc4777310a0e695a8993964bec89f7b5bafd5ad4 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 2 Feb 2017 23:54:09 -0800 Subject: more tests --- _pytest/conftest.py | 8 ++++++++ _pytest/test_slackchannel.py | 20 ++++++++++++++++++++ _pytest/test_slackdmchannel.py | 20 ++++++++++++++++++++ _pytest/test_slackgroupchannel.py | 20 ++++++++++++++++++++ _pytest/test_slackmpdmchannel.py | 20 ++++++++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 _pytest/test_slackchannel.py create mode 100644 _pytest/test_slackdmchannel.py create mode 100644 _pytest/test_slackgroupchannel.py create mode 100644 _pytest/test_slackmpdmchannel.py diff --git a/_pytest/conftest.py b/_pytest/conftest.py index 07dfc5e..8fcaf77 100644 --- a/_pytest/conftest.py +++ b/_pytest/conftest.py @@ -32,6 +32,14 @@ def realish_eventrouter(): rtmstartdata = open('_pytest/data/http/rtm.start.json', 'r').read() e.receive_httprequest_callback(context, 1, 0, rtmstartdata, 4) e.handle_next() + #e.sc is just shortcuts to these items + e.sc = {} + e.sc["team_id"] = e.teams.keys()[0] + e.sc["team"] = e.teams[e.sc["team_id"]] + e.sc["user"] = e.teams[e.sc["team_id"]].users[e.teams[e.sc["team_id"]].users.keys()[0]] + socket = mock_websocket + e.teams[e.sc["team_id"]].ws = socket + return e diff --git a/_pytest/test_slackchannel.py b/_pytest/test_slackchannel.py new file mode 100644 index 0000000..5f04d69 --- /dev/null +++ b/_pytest/test_slackchannel.py @@ -0,0 +1,20 @@ +from mock import Mock +#from wee_slack import SlackChannel + +def test_SlackChannel(realish_eventrouter): + e = realish_eventrouter + + print e.sc["team"].channels + #c = SlackChannel(e, **json.loads(chan)) + c = e.sc["team"].channels['C3ZEQAYN7'] + + print c.formatted_name() + c.is_someone_typing = Mock(return_value=True) + c.channel_buffer = Mock(return_value=True) + print c.create_buffer() + print c.rename() + print c.current_short_name + print c.formatted_name() + print c.rename() + print c.formatted_name() +# assert False diff --git a/_pytest/test_slackdmchannel.py b/_pytest/test_slackdmchannel.py new file mode 100644 index 0000000..203c4ae --- /dev/null +++ b/_pytest/test_slackdmchannel.py @@ -0,0 +1,20 @@ +from mock import Mock +#from wee_slack import SlackChannel + +def test_SlackDMChannel(realish_eventrouter): + e = realish_eventrouter + + print e.sc["team"].channels + #c = SlackChannel(e, **json.loads(chan)) + c = e.sc["team"].channels['D3ZEQULHZ'] + + print c.formatted_name() + c.is_someone_typing = Mock(return_value=True) + c.channel_buffer = Mock(return_value=True) + print c.create_buffer() + print c.rename() + print c.current_short_name + print c.formatted_name() + print c.rename() + print c.formatted_name() +# assert False diff --git a/_pytest/test_slackgroupchannel.py b/_pytest/test_slackgroupchannel.py new file mode 100644 index 0000000..59793f2 --- /dev/null +++ b/_pytest/test_slackgroupchannel.py @@ -0,0 +1,20 @@ +from mock import Mock +#from wee_slack import SlackChannel + +def test_SlackGroupChannel(realish_eventrouter): + e = realish_eventrouter + + print e.sc["team"].channels + #c = SlackChannel(e, **json.loads(chan)) + c = e.sc["team"].channels['G3ZJKP7GA'] + + print c.formatted_name() + c.is_someone_typing = Mock(return_value=True) + c.channel_buffer = Mock(return_value=True) + print c.create_buffer() + print c.rename() + print c.current_short_name + print c.formatted_name() + print c.rename() + print c.formatted_name() +# assert False diff --git a/_pytest/test_slackmpdmchannel.py b/_pytest/test_slackmpdmchannel.py new file mode 100644 index 0000000..a2c6c51 --- /dev/null +++ b/_pytest/test_slackmpdmchannel.py @@ -0,0 +1,20 @@ +from mock import Mock +#from wee_slack import SlackChannel + +def test_SlackMPDMChannel(realish_eventrouter): + e = realish_eventrouter + + print e.sc["team"].channels + #c = SlackChannel(e, **json.loads(chan)) + c = e.sc["team"].channels['G3ZGMF4RZ'] + + print c.formatted_name() + c.is_someone_typing = Mock(return_value=True) + c.channel_buffer = Mock(return_value=True) + print c.create_buffer() + print c.rename() + print c.current_short_name + print c.formatted_name() + print c.rename() + print c.formatted_name() +# assert False -- cgit From 3a0ea46cced1d7c0c9bccbefdf59886e0b0714dc Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 08:05:58 -0800 Subject: more tests --- _pytest/test_slackchannel.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/_pytest/test_slackchannel.py b/_pytest/test_slackchannel.py index 5f04d69..b93c1dd 100644 --- a/_pytest/test_slackchannel.py +++ b/_pytest/test_slackchannel.py @@ -17,4 +17,17 @@ def test_SlackChannel(realish_eventrouter): print c.formatted_name() print c.rename() print c.formatted_name() -# assert False + + print "-------" + print c == "random" + print "-------" + print c == "#random" + print "-------" + print c == "weeslacktest.slack.com.#random" + print "-------" + print c == "weeslacktest.slack.com.random" + print "-------" + print c == "dandom" + + print e.weechat_controller.buffers + assert False -- cgit From 60541c7be2af2e9a7af5fe4989301faebbb17229 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 08:14:51 -0800 Subject: open channels --- wee_slack.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 1fb05e4..1dc44d8 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -586,7 +586,7 @@ class SlackTeam(object): # Last step is to make sure my nickname is the set color self.users[self.myidentifier].force_color(w.config_string(w.config_get('weechat.color.chat_nick_self'))) def __eq__(self, compare_str): - if compare_str == self.token: + if compare_str == self.token or compare_str == self.domain or compare_str == self.team: return True else: return False @@ -669,6 +669,11 @@ class SlackChannel(object): self.set_name(self.slack_name) #short name relates to the localvar we change for typing indication self.current_short_name = self.name + def __eq__(self, compare_str): + if compare_str == self.slack_name or compare_str == self.name or compare_str == self.full_name(): + return True + else: + return False def __repr__(self): return "Name:{} Identifier:{}".format(self.name, self.identifier) def set_name(self, slack_name): @@ -688,9 +693,13 @@ class SlackChannel(object): return False def formatted_name(self, prepend="#", **kwargs): return prepend + self.slack_name + def full_name(self): + return self.team.domain + "." + self.formatted_name(prepend="#") def update_from_message_json(self, message_json): for key, value in message_json.items(): setattr(self, key, value) + def open(self): + self.create_buffer() def open_if_we_should(self, force=False): try: if self.is_archived: @@ -723,13 +732,16 @@ class SlackChannel(object): w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') w.buffer_set(self.channel_buffer, "localvar_set_channel", name) w.buffer_set(self.channel_buffer, "short_name", self.formatted_name()) - if self.server.alias: - w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) - else: - w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) + #if self.server.alias: + # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) + #else: + # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) self.eventrouter.weechat_controller.set_refresh_buffer_list(True) +# try: if self.unread_count != 0: w.buffer_set(self.channel_buffer, "hotlist", "1") +# except: +# pass #if self.unread_count != 0 and not self.muted: # w.buffer_set(self.channel_buffer, "hotlist", "1") def destroy_buffer(self, update_remote): @@ -1630,6 +1642,34 @@ def tag(tagset, user="unknown user"): ###### New/converted command_ commands +def command_talk(current_buffer, args): + """ + incomplete because globals hack + Open a chat with the specified user + /slack talk [user] + """ + e = EVENTROUTER + current = w.current_buffer() + team = e.weechat_controller.buffers[current].team + dbg(team) + c = team.get_channel_map() + if args not in c: + u = team.get_username_map() + if args in u: + s = SlackRequest(team.token, "im.open", {"user": u[args]}, team_hash=team.team_hash) + EVENTROUTER.receive(s) + dbg("found user") + #refresh channel map here + c = team.get_channel_map() + + if args in c: + dbg("found channel") + chan = team.channels[c[args]] + chan.open() + if config.switch_buffer_on_join: + w.buffer_set(chan.channel_buffer, "display", "1") + return + def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) if len(a) > 1: @@ -1637,12 +1677,33 @@ def slack_command_cb(data, current_buffer, args): else: function_name, args = a[0], None - try: - cmds[function_name](current_buffer, args) - except KeyError: - w.prnt("", "Command not found: " + function_name) +# try: + cmds[function_name](current_buffer, args) +# except KeyError: +# w.prnt("", "Command not found: " + function_name) return w.WEECHAT_RC_OK +def command_nodistractions(current_buffer, args): + #global hide_distractions + #hide_distractions = not hide_distractions + if config.distracting_channels != ['']: + for channel in config.distracting_channels: + #try: + for c in EVENTROUTER.weechat_controller.buffers.itervalues(): + if c == channel: + w.buffer_set(c.channel_buffer, "hidden", str(int(hide_distractions))) +# if channel in EVENTROUTER.weechat_controller.buffers.values(): +# cbuf = kwargs['team'].channels[item["channel"]].channel_buffer +# cbuf = EVENTROUTER.weechat_controller.buffers[channel].channel_buffer +# channel_buffer = channels.find(channel).channel_buffer +# if channel_buffer: +# w.buffer_set(channels.find(channel).channel_buffer, "hidden", str(int(hide_distractions))) + #except: + # dbg("Can't hide channel {} .. removing..".format(channel), main_buffer=True) +# config.distracting_channels.pop(config.distracting_channels.index(channel)) +# save_distracting_channels() + + def command_p(current_buffer, args): w.prnt("", "{}".format(eval(args))) -- cgit From 863b1a5a2853d945187870a62cfa4d22685ebf05 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 08:22:04 -0800 Subject: open needs work --- wee_slack.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 1dc44d8..f691842 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -698,7 +698,17 @@ class SlackChannel(object): def update_from_message_json(self, message_json): for key, value in message_json.items(): setattr(self, key, value) - def open(self): + def open(self, update_remote=True): + self.create_buffer() + #self.active = True + self.get_history() + if "info" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.slack_name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + EVENTROUTER.receive(s) + if update_remote: + if "join" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.slack_name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + EVENTROUTER.receive(s) self.create_buffer() def open_if_we_should(self, force=False): try: -- cgit From a9b851aaec3575e326e428249c0c68cb7f33f5af Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 09:05:00 -0800 Subject: much simplified --- wee_slack.py | 58 ++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index f691842..5d8ad70 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -670,7 +670,7 @@ class SlackChannel(object): #short name relates to the localvar we change for typing indication self.current_short_name = self.name def __eq__(self, compare_str): - if compare_str == self.slack_name or compare_str == self.name or compare_str == self.full_name(): + if compare_str == self.slack_name or compare_str == self.name or compare_str == self.formatted_name(style="long_default"): return True else: return False @@ -682,19 +682,24 @@ class SlackChannel(object): return self.rename() def rename(self): if self.channel_buffer: - if self.is_someone_typing(): - new_name = ">{}".format(self.formatted_name()[1:]) - else: - new_name = self.formatted_name() + new_name = self.formatted_name(typing=self.is_someone_typing()) if self.current_short_name != new_name: self.current_short_name = new_name w.buffer_set(self.channel_buffer, "short_name", new_name) return True return False - def formatted_name(self, prepend="#", **kwargs): - return prepend + self.slack_name - def full_name(self): - return self.team.domain + "." + self.formatted_name(prepend="#") + def formatted_name(self, style="default", typing=False, **kwargs): + if not typing: + prepend = "#" + else: + prepend = ">" + select = { + "default": prepend + self.slack_name, + "base": self.slack_name, + "long_default": "{}.{}{}".format(self.team.domain, prepend, self.slack_name), + "long_base": "{}.{}".format(self.team.domain, self.slack_name), + } + return select[style] def update_from_message_json(self, message_json): for key, value in message_json.items(): setattr(self, key, value) @@ -748,8 +753,8 @@ class SlackChannel(object): # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) self.eventrouter.weechat_controller.set_refresh_buffer_list(True) # try: - if self.unread_count != 0: - w.buffer_set(self.channel_buffer, "hotlist", "1") +# if self.unread_count != 0: +# w.buffer_set(self.channel_buffer, "hotlist", "1") # except: # pass #if self.unread_count != 0 and not self.muted: @@ -900,27 +905,32 @@ class SlackDMChannel(SlackChannel): else: self.color = "" self.color_name = "" - def formatted_name(self, prepend="", enable_color=True, basic=False): - if config.colorize_private_chats and enable_color and not basic: + def formatted_name(self, style="default", typing=False, present=True, enable_color=False, **kwargs): + if config.colorize_private_chats and enable_color: print_color = self.color else: print_color = "" - return print_color + prepend + self.name - - def refresh(self): - return self.rename() - + if not present: + prepend = " " + else: + prepend = "+" + select = { + "default": prepend + self.slack_name, + "base": self.slack_name, + "long_default": "{}.{}{}".format(self.team.domain, prepend, self.slack_name), + "long_base": "{}.{}".format(self.team.domain, self.slack_name), + } + return print_color + select[style] def rename(self): if self.channel_buffer: - if self.team.is_user_present(self.user): - new_name = "+{}".format(self.formatted_name()) - else: - new_name = " {}".format(self.formatted_name()) + new_name = self.formatted_name(present=self.team.is_user_present(self.user), enable_color=config.colorize_private_chats) if self.current_short_name != new_name: self.current_short_name = new_name w.buffer_set(self.channel_buffer, "short_name", new_name) return True return False + def refresh(self): + return self.rename() class SlackGroupChannel(SlackChannel): @@ -934,8 +944,8 @@ class SlackGroupChannel(SlackChannel): self.set_name(self.slack_name) def set_name(self, slack_name): self.name = "#" + slack_name - def formatted_name(self, prepend="#", enable_color=True, basic=False): - return prepend + self.slack_name + #def formatted_name(self, prepend="#", enable_color=True, basic=False): + # return prepend + self.slack_name class SlackMPDMChannel(SlackChannel): """ -- cgit From 1120ad129cd5fb262f54e99aa1addb7e9b2abe65 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 09:38:04 -0800 Subject: consolidate styles --- wee_slack.py | 51 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 5d8ad70..18be80f 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -682,7 +682,7 @@ class SlackChannel(object): return self.rename() def rename(self): if self.channel_buffer: - new_name = self.formatted_name(typing=self.is_someone_typing()) + new_name = self.formatted_name(typing=self.is_someone_typing(), style="sidebar") if self.current_short_name != new_name: self.current_short_name = new_name w.buffer_set(self.channel_buffer, "short_name", new_name) @@ -695,6 +695,7 @@ class SlackChannel(object): prepend = ">" select = { "default": prepend + self.slack_name, + "sidebar": prepend + self.slack_name, "base": self.slack_name, "long_default": "{}.{}{}".format(self.team.domain, prepend, self.slack_name), "long_base": "{}.{}".format(self.team.domain, self.slack_name), @@ -738,25 +739,27 @@ class SlackChannel(object): Creates the weechat buffer where the channel magic happens. """ if not self.channel_buffer: - name = self.formatted_name(basic=True) - self.channel_buffer = w.buffer_new("{}.{}".format(self.team.domain, name), "buffer_input_callback", "EVENTROUTER", "", "") + self.channel_buffer = w.buffer_new(self.formatted_name(style="long_default"), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) if self.type == "im": w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') else: w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') - w.buffer_set(self.channel_buffer, "localvar_set_channel", name) - w.buffer_set(self.channel_buffer, "short_name", self.formatted_name()) + w.buffer_set(self.channel_buffer, "localvar_set_channel", self.formatted_name()) + w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) #if self.server.alias: # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) #else: # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) self.eventrouter.weechat_controller.set_refresh_buffer_list(True) -# try: -# if self.unread_count != 0: -# w.buffer_set(self.channel_buffer, "hotlist", "1") -# except: -# pass + try: + if self.unread_count != 0: + w.buffer_set(self.channel_buffer, "hotlist", "1") + else: + print "no unread in {}".format(self.name) + except: + print "no unread count" + pass #if self.unread_count != 0 and not self.muted: # w.buffer_set(self.channel_buffer, "hotlist", "1") def destroy_buffer(self, update_remote): @@ -915,15 +918,16 @@ class SlackDMChannel(SlackChannel): else: prepend = "+" select = { - "default": prepend + self.slack_name, + "default": self.slack_name, + "sidebar": prepend + self.slack_name, "base": self.slack_name, - "long_default": "{}.{}{}".format(self.team.domain, prepend, self.slack_name), + "long_default": "{}.{}".format(self.team.domain, self.slack_name), "long_base": "{}.{}".format(self.team.domain, self.slack_name), } return print_color + select[style] def rename(self): if self.channel_buffer: - new_name = self.formatted_name(present=self.team.is_user_present(self.user), enable_color=config.colorize_private_chats) + new_name = self.formatted_name(style="sidebar", present=self.team.is_user_present(self.user), enable_color=config.colorize_private_chats) if self.current_short_name != new_name: self.current_short_name = new_name w.buffer_set(self.channel_buffer, "short_name", new_name) @@ -959,8 +963,23 @@ class SlackMPDMChannel(SlackChannel): self.type = "group" def set_name(self, n): self.name = "|".join("-".join(n.split("-")[1:-1]).split("--")) - def formatted_name(self, **kwargs): - return self.name + def formatted_name(self, style="default", typing=False, **kwargs): + adjusted_name = "|".join("-".join(self.slack_name.split("-")[1:-1]).split("--")) + if not typing: + prepend = " " + else: + prepend = ">" + select = { + "default": adjusted_name, + "sidebar": prepend + adjusted_name, + "base": adjusted_name, + "long_default": "{}.{}".format(self.team.domain, adjusted_name), + "long_base": "{}.{}".format(self.team.domain, adjusted_name), + } + return select[style] + +# def formatted_name(self, **kwargs): +# return self.name def rename(self): pass @@ -1250,12 +1269,14 @@ def process_message(message_json, eventrouter, store=True, **kwargs): text = text[1:-1] if message.sender != channel.server.nick: text = message.sender + " " + text + channel.unread_count += 1 channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, **kwargs) else: suffix = '' if 'edited' in message_json: suffix = ' (edited)' + channel.unread_count += 1 channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) if store: -- cgit From 8bd70ba07d9554a2267ae74994afd0a1f8bdf9e3 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 19:36:45 -0800 Subject: channels --- _pytest/test_everything.py | 1 + _pytest/test_slackchannel.py | 2 +- wee_slack.py | 28 +++++++++++++++++----------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index b1a2f23..1abfa42 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -29,6 +29,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): print "####################" data = json.loads(open(fname, 'r').read()) socket.add(data) + print data eventrouter.receive_ws_callback(t) eventrouter.handle_next() except ProcessNotImplemented as e: diff --git a/_pytest/test_slackchannel.py b/_pytest/test_slackchannel.py index b93c1dd..7d93afc 100644 --- a/_pytest/test_slackchannel.py +++ b/_pytest/test_slackchannel.py @@ -30,4 +30,4 @@ def test_SlackChannel(realish_eventrouter): print c == "dandom" print e.weechat_controller.buffers - assert False + #assert False diff --git a/wee_slack.py b/wee_slack.py index 18be80f..8a1714a 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -65,6 +65,7 @@ SLACK_API_TRANSLATOR = { } + NICK_GROUP_HERE = "0|Here" NICK_GROUP_AWAY = "1|Away" @@ -561,7 +562,7 @@ class SlackTeam(object): Team object under which users and channels live.. Does lots. """ def __init__(self, eventrouter, token, team, nick, myidentifier, users, bots, channels): - self.connected = False + self.state = "disconnected" self.ws = None self.ws_counter = 0 self.ws_replies = {} @@ -581,7 +582,7 @@ class SlackTeam(object): self.create_buffer() for c in self.channels.keys(): channels[c].set_related_server(self) - channels[c].open_if_we_should() + channels[c].check_should_open() # self.channel_set_related_server(c) # Last step is to make sure my nickname is the set color self.users[self.myidentifier].force_color(w.config_string(w.config_get('weechat.color.chat_nick_self'))) @@ -660,7 +661,7 @@ class SlackChannel(object): self.last_read = SlackTS(kwargs.get("last_read", SlackTS())) #print self.last_read self.channel_buffer = None - self.team = None + self.team = kwargs.get('team', None) self.got_history = False self.messages = {} self.new_messages = False @@ -710,13 +711,13 @@ class SlackChannel(object): self.get_history() if "info" in SLACK_API_TRANSLATOR[self.type]: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.slack_name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) - EVENTROUTER.receive(s) + self.eventrouter.receive(s) if update_remote: if "join" in SLACK_API_TRANSLATOR[self.type]: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.slack_name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) - EVENTROUTER.receive(s) + self.eventrouter.receive(s) self.create_buffer() - def open_if_we_should(self, force=False): + def check_should_open(self, force=False): try: if self.is_archived: return @@ -769,7 +770,7 @@ class SlackChannel(object): #if update_remote and not eventrouter.shutting_down: if update_remote and not self.eventrouter.shutting_down: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) - EVENTROUTER.receive(s) + self.eventrouter.receive(s) def buffer_prnt(self, nick, text, timestamp, **kwargs): data = "{}\t{}".format(nick, text) ts = SlackTS(timestamp) @@ -824,7 +825,7 @@ class SlackChannel(object): # for message in message_cache[self.identifier]: # process_message(json.loads(message), True) s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier) - EVENTROUTER.receive(s) + self.eventrouter.receive(s) #async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}) self.got_history = True def send_add_reaction(self, msg_number, reaction): @@ -1276,7 +1277,10 @@ def process_message(message_json, eventrouter, store=True, **kwargs): suffix = '' if 'edited' in message_json: suffix = ' (edited)' - channel.unread_count += 1 + try: + channel.unread_count += 1 + except: + channel.unread_count = 1 channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) if store: @@ -1381,16 +1385,18 @@ def process_mpim_marked(message_json, eventrouter, **kwargs): def process_channel_joined(message_json, eventrouter, **kwargs): item = message_json["channel"] kwargs['team'].channels[item["id"]].update_from_message_json(item) - kwargs['team'].channels[item["id"]].open_if_we_should() + kwargs['team'].channels[item["id"]].open() def process_channel_created(message_json, eventrouter, **kwargs): item = message_json["channel"] c = SlackChannel(eventrouter, team=kwargs["team"], **item) kwargs['team'].channels[item["id"]] = c + #print eventrouter.teams['d80c2b6c3127dbb1991917394ed219e8212a2606'].channels['C3ZM2GMGU'].team.domain + #raise def process_im_open(message_json, eventrouter, **kwargs): item = message_json - kwargs['team'].channels[item["channel"]].open_if_we_should(True) + kwargs['team'].channels[item["channel"]].check_should_open(True) def process_im_close(message_json, eventrouter, **kwargs): item = message_json -- cgit From 352f31322697fecfc572120230535334c4520726 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 19:42:26 -0800 Subject: add join command --- wee_slack.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 8a1714a..9c1721a 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1689,6 +1689,16 @@ def tag(tagset, user="unknown user"): ###### New/converted command_ commands +def join_command_cb(data, current_buffer, args): + args = args.split() + if len(args) < 2: + w.prnt(current_buffer, "Missing channel argument") + return w.WEECHAT_RC_OK_EAT + elif command_talk(current_buffer, args[1]): + return w.WEECHAT_RC_OK_EAT + else: + return w.WEECHAT_RC_OK + def command_talk(current_buffer, args): """ incomplete because globals hack -- cgit From bf662aeda1da97c6b5e08a6d5a4efe08529d1265 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 20:37:26 -0800 Subject: fix error conditions --- wee_slack.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 9c1721a..92ea69c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -757,10 +757,9 @@ class SlackChannel(object): if self.unread_count != 0: w.buffer_set(self.channel_buffer, "hotlist", "1") else: - print "no unread in {}".format(self.name) + dbg("no unread in {}".format(self.name)) except: - print "no unread count" - pass + dbg("exception no unread count") #if self.unread_count != 0 and not self.muted: # w.buffer_set(self.channel_buffer, "hotlist", "1") def destroy_buffer(self, update_remote): -- cgit From d5dddfe87c016f4bde2bcf975745665c7c8cff33 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 21:30:19 -0800 Subject: wee're getting there --- wee_slack.py | 113 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 92ea69c..237f7bf 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -671,7 +671,7 @@ class SlackChannel(object): #short name relates to the localvar we change for typing indication self.current_short_name = self.name def __eq__(self, compare_str): - if compare_str == self.slack_name or compare_str == self.name or compare_str == self.formatted_name(style="long_default"): + if compare_str == self.slack_name or compare_str == self.formatted_name() or compare_str == self.formatted_name(style="long_default"): return True else: return False @@ -1239,52 +1239,54 @@ def process_pong(message_json, eventrouter, **kwargs): def process_message(message_json, eventrouter, store=True, **kwargs): channel = kwargs["channel"] team = kwargs["team"] - #try: - # send these subtype messages elsewhere - known_subtypes = [ - #'thread_message', - #'message_replied', - 'message_changed', - 'message_deleted', - #'channel_join', - #'channel_leave', - #'channel_topic', - #'group_join', - #'group_leave', - ] - if "thread_ts" in message_json and "reply_count" not in message_json: - message_json["subtype"] = "thread_message" - subtype = message_json.get("subtype", None) - if subtype and subtype in known_subtypes: - f = eval('subprocess_' + subtype) - f(message_json, eventrouter, channel, team) - - else: - message = SlackMessage(message_json, team, channel) - text = message.render() - #print text - - # special case with actions. - if text.startswith("_") and text.endswith("_"): - text = text[1:-1] - if message.sender != channel.server.nick: - text = message.sender + " " + text - channel.unread_count += 1 - channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, **kwargs) + try: + # send these subtype messages elsewhere + known_subtypes = [ + #'thread_message', + #'message_replied', + 'message_changed', + 'message_deleted', + #'channel_join', + #'channel_leave', + #'channel_topic', + #'group_join', + #'group_leave', + ] + if "thread_ts" in message_json and "reply_count" not in message_json: + message_json["subtype"] = "thread_message" + subtype = message_json.get("subtype", None) + if subtype and subtype in known_subtypes: + f = eval('subprocess_' + subtype) + f(message_json, eventrouter, channel, team) else: - suffix = '' - if 'edited' in message_json: - suffix = ' (edited)' - try: + message = SlackMessage(message_json, team, channel) + text = message.render() + #print text + + # special case with actions. + if text.startswith("_") and text.endswith("_"): + text = text[1:-1] + if message.sender != channel.team.nick: + text = message.sender + " " + text channel.unread_count += 1 - except: - channel.unread_count = 1 - channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) + channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, **kwargs) + + else: + suffix = '' + if 'edited' in message_json: + suffix = ' (edited)' + try: + channel.unread_count += 1 + except: + channel.unread_count = 1 + channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) - if store: - channel.store_message(message, team) - dbg("NORMAL REPLY {}".format(message_json)) + if store: + channel.store_message(message, team) + dbg("NORMAL REPLY {}".format(message_json)) + except: + channel.buffer_prnt("WEE-SLACK-ERROR", json.dumps(message_json).encode('utf-8'), message_json["ts"], **kwargs) def subprocess_thread_message(message_json, eventrouter, channel, team): dbg("REPLIEDDDD: " + str(message_json)) @@ -1739,21 +1741,30 @@ def slack_command_cb(data, current_buffer, args): # w.prnt("", "Command not found: " + function_name) return w.WEECHAT_RC_OK +def command_distracting(current_buffer, args): + channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + if channel: + fullname = channel.formatted_name(style="long_default") + if config.distracting_channels.count(fullname) == 0: + config.distracting_channels.append(fullname) + else: + config.distracting_channels.pop(config.distracting_channels.index(fullname)) + save_distracting_channels() + +def save_distracting_channels(): + w.config_set_plugin('distracting_channels', ','.join(config.distracting_channels)) + def command_nodistractions(current_buffer, args): - #global hide_distractions - #hide_distractions = not hide_distractions + global hide_distractions + hide_distractions = not hide_distractions if config.distracting_channels != ['']: for channel in config.distracting_channels: + dbg('hiding channel {}'.format(channel)) #try: for c in EVENTROUTER.weechat_controller.buffers.itervalues(): if c == channel: + dbg('found channel {} to hide'.format(channel)) w.buffer_set(c.channel_buffer, "hidden", str(int(hide_distractions))) -# if channel in EVENTROUTER.weechat_controller.buffers.values(): -# cbuf = kwargs['team'].channels[item["channel"]].channel_buffer -# cbuf = EVENTROUTER.weechat_controller.buffers[channel].channel_buffer -# channel_buffer = channels.find(channel).channel_buffer -# if channel_buffer: -# w.buffer_set(channels.find(channel).channel_buffer, "hidden", str(int(hide_distractions))) #except: # dbg("Can't hide channel {} .. removing..".format(channel), main_buffer=True) # config.distracting_channels.pop(config.distracting_channels.index(channel)) -- cgit From 7e6368dd65f7d0a98b1da66951ebc465f4f7e4f4 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 3 Feb 2017 21:58:35 -0800 Subject: multiple tokens --- wee_slack.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 237f7bf..bf57425 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1125,6 +1125,8 @@ class SlackTS(object): return 1 elif s == other: return 0 + def __hash__(self): + return hash("{}.{}".format(self.major, self.minor)) def __repr__(self): return str("{0}.{1:06d}".format(self.major, self.minor)) def split(self, *args, **kwargs): @@ -1287,6 +1289,7 @@ def process_message(message_json, eventrouter, store=True, **kwargs): dbg("NORMAL REPLY {}".format(message_json)) except: channel.buffer_prnt("WEE-SLACK-ERROR", json.dumps(message_json).encode('utf-8'), message_json["ts"], **kwargs) + traceback.print_exc() def subprocess_thread_message(message_json, eventrouter, channel, team): dbg("REPLIEDDDD: " + str(message_json)) @@ -1945,6 +1948,8 @@ if __name__ == "__main__": w.prnt("", "\nERROR: Weechat version 1.3+ is required to use {}.\n\n".format(SCRIPT_NAME)) else: + global EVENTROUTER + EVENTROUTER = EventRouter() #setup_trace() WEECHAT_HOME = w.info_get("weechat_dir", "") @@ -2022,13 +2027,12 @@ if __name__ == "__main__": "nick_completion_cb", "") #w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') - tok = config.slack_api_token.split(',')[0] - s = SlackRequest(tok, 'rtm.start', {}) - global EVENTROUTER - EVENTROUTER = EventRouter() + tokens = config.slack_api_token.split(',') + for t in tokens: + s = SlackRequest(t, 'rtm.start', {}) + EVENTROUTER.receive(s) if config.record_events: EVENTROUTER.record() - EVENTROUTER.receive(s) EVENTROUTER.handle_next() w.hook_timer(10, 0, 0, "handle_next", "") # END attach to the weechat hooks we need -- cgit From e958af534073e006fa36feec9943a25589882450 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 09:44:17 -0800 Subject: change extension name and do one time migration --- _pytest/conftest.py | 1 - wee_slack.py | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/_pytest/conftest.py b/_pytest/conftest.py index 8fcaf77..0259ac2 100644 --- a/_pytest/conftest.py +++ b/_pytest/conftest.py @@ -69,7 +69,6 @@ class FakeWeechat(): return "testuser" def buffer_new(*args): return "0x8a8a8a8b" - def __getattr__(self, name): def method(*args): pass diff --git a/wee_slack.py b/wee_slack.py index bf57425..1895f6b 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -22,7 +22,7 @@ try: except: pass -SCRIPT_NAME = "slack_extension" +SCRIPT_NAME = "slack" SCRIPT_AUTHOR = "Ryan Huber " SCRIPT_VERSION = "1.99" SCRIPT_LICENSE = "MIT" @@ -1864,6 +1864,7 @@ class PluginConfig(object): # Set missing settings to their defaults. Load non-missing settings from # weechat configs. def __init__(self): + self.migrate() for key, default in self.settings.iteritems(): if not w.config_get_plugin(key): w.config_set_plugin(key, default) @@ -1908,6 +1909,19 @@ class PluginConfig(object): def get_slack_timeout(self, key): return int(w.config_get_plugin(key)) + def migrate(self): + """ + This is to migrate from slack_extension to slack + """ + if not w.config_get_plugin("migrated"): + for k in self.settings.keys(): + if not w.config_is_set_plugin(k): + p = w.config_get("plugins.var.python.slack_extension.{}".format(k)) + data = w.config_string(p) + if data != "": + w.config_set_plugin(k, data) + w.config_set_plugin("migrated", "true") + # to Trace execution, add `setup_trace()` to startup # and to a function and sys.settrace(trace_calls) to a function -- cgit From 7d04ad45305a3067ef19c77d4feabe5e54bda53b Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 09:47:10 -0800 Subject: clarify method --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 1895f6b..dd1a192 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1911,7 +1911,7 @@ class PluginConfig(object): def migrate(self): """ - This is to migrate from slack_extension to slack + This is to migrate the extension name from slack_extension to slack """ if not w.config_get_plugin("migrated"): for k in self.settings.keys(): -- cgit From f0108a584f48e951212f58199e466e2fa6fba0e2 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 12:55:29 -0800 Subject: make team buffer work properly --- wee_slack.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index dd1a192..f17c7f6 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -561,21 +561,22 @@ class SlackTeam(object): incomplete Team object under which users and channels live.. Does lots. """ - def __init__(self, eventrouter, token, team, nick, myidentifier, users, bots, channels): + def __init__(self, eventrouter, token, subdomain, nick, myidentifier, users, bots, channels): self.state = "disconnected" self.ws = None self.ws_counter = 0 self.ws_replies = {} self.eventrouter = eventrouter self.token = token - self.team = team - self.domain = team + ".slack.com" + self.team = self + self.subdomain = subdomain + self.domain = subdomain + ".slack.com" self.nick = nick self.myidentifier = myidentifier self.channels = channels self.users = users self.bots = bots - self.team_hash = str(sha.sha("{}{}".format(self.nick, self.team)).hexdigest()) + self.team_hash = str(sha.sha("{}{}".format(self.nick, self.subdomain)).hexdigest()) self.name = self.domain self.server_buffer = None self.got_history = True @@ -587,7 +588,7 @@ class SlackTeam(object): # Last step is to make sure my nickname is the set color self.users[self.myidentifier].force_color(w.config_string(w.config_get('weechat.color.chat_nick_self'))) def __eq__(self, compare_str): - if compare_str == self.token or compare_str == self.domain or compare_str == self.team: + if compare_str == self.token or compare_str == self.domain or compare_str == self.subdomain: return True else: return False -- cgit From e20c3aabc9ef1b55bfed19d4422e82831bb27b77 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 14:03:09 -0800 Subject: channels work properly --- wee_slack.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index f17c7f6..15e5391 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -609,7 +609,7 @@ class SlackTeam(object): def get_channel_map(self): return {v.slack_name: k for k, v in self.channels.iteritems()} def get_username_map(self): - return {v.name: k for k, v in self.users.iteritems()} + return {v.slack_name: k for k, v in self.users.iteritems()} def get_team_hash(self): return self.team_hash def refresh(self): @@ -711,11 +711,11 @@ class SlackChannel(object): #self.active = True self.get_history() if "info" in SLACK_API_TRANSLATOR[self.type]: - s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.slack_name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) if update_remote: if "join" in SLACK_API_TRANSLATOR[self.type]: - s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.slack_name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) self.create_buffer() def check_should_open(self, force=False): @@ -926,6 +926,18 @@ class SlackDMChannel(SlackChannel): "long_base": "{}.{}".format(self.team.domain, self.slack_name), } return print_color + select[style] + def open(self, update_remote=True): + self.create_buffer() + #self.active = True + self.get_history() + if "info" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + if update_remote: + if "join" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"user": self.user}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + self.create_buffer() def rename(self): if self.channel_buffer: new_name = self.formatted_name(style="sidebar", present=self.team.is_user_present(self.user), enable_color=config.colorize_private_chats) @@ -992,6 +1004,7 @@ class SlackUser(object): # We require these two things for a vaid object, # the rest we can just learn from slack self.identifier = kwargs["id"] + self.slack_name = kwargs["name"] self.name = kwargs["name"] for key, value in kwargs.items(): setattr(self, key, value) @@ -1033,6 +1046,7 @@ class SlackMessage(object): self.team = team self.channel = channel self.message_json = message_json + self.submessages = None self.sender = self.get_sender() self.suffix = '' self.ts = SlackTS(message_json['ts']) -- cgit From aa5fb6bf8c371d6a7569b276b39c93ab18050c75 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 14:05:58 -0800 Subject: return true to EAT the error --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 15e5391..dbf023c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1744,7 +1744,7 @@ def command_talk(current_buffer, args): chan.open() if config.switch_buffer_on_join: w.buffer_set(chan.channel_buffer, "display", "1") - return + return True def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) -- cgit From 2cf781d4bea952b2b0d3d67e3be3bc29f5239af9 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 15:05:23 -0800 Subject: typing notifications --- wee_slack.py | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index dbf023c..5353ed3 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -511,6 +511,39 @@ def buffer_list_update_callback(data, somecount): def quit_notification_callback(signal, sig_type, data): stop_talking_to_slack() +def typing_update_cb(data, remaining_calls): + w.bar_item_update("slack_typing_notice") + return w.WEECHAT_RC_OK + +def typing_bar_item_cb(data, current_buffer, args): + """ + Privides a bar item indicating who is typing in the current channel AND + why is typing a DM to you globally. + """ + typers = [] + current_buffer = w.current_buffer() + current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + + # first look for people typing in this channel + if current_channel: + if current_channel.is_someone_typing(): + typers += current_channel.get_typing_list() + + # here is where we notify you that someone is typing in DM + # regardless of which buffer you are in currently + for t in EVENTROUTER.teams.values(): + for channel in t.channels.values(): + if channel.type == "im": + if channel.is_someone_typing(): + typers.append("D/" + channel.slack_name) + pass + + typing = ", ".join(typers) + if typing != "": + typing = w.color('yellow') + "typing: " + typing + + return typing + def script_unloaded(): stop_talking_to_slack() return w.WEECHAT_RC_OK @@ -871,6 +904,8 @@ class SlackChannel(object): for user, timestamp in self.typing.iteritems(): if timestamp + 4 > time.time(): typing.append(user) + else: + del self.typing[user] return typing def mark_read(self, ts=None, update_remote=True, force=False): if not ts: @@ -1249,6 +1284,7 @@ def process_user_typing(message_json, eventrouter, **kwargs): team = kwargs["team"] if channel: channel.set_typing(team.users.get(message_json["user"]).name) + w.bar_item_update("slack_typing_notice") def process_pong(message_json, eventrouter, **kwargs): pass @@ -2016,7 +2052,9 @@ if __name__ == "__main__": #w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") # attach to the weechat hooks we need - #w.hook_timer(1000, 0, 0, "typing_update_cb", "") + w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') + w.hook_timer(1000, 0, 0, "typing_update_cb", "") + w.hook_timer(1000, 0, 0, "buffer_list_update_callback", "EVENTROUTER") w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") @@ -2054,7 +2092,6 @@ if __name__ == "__main__": w.hook_command_run('/away', 'away_command_cb', '') w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") - #w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') tokens = config.slack_api_token.split(',') for t in tokens: -- cgit From 2d1c7550c39170cf68417c0ef495f2e2cac43e49 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 15:09:39 -0800 Subject: handle case --- wee_slack.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 5353ed3..abbfa09 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -526,8 +526,12 @@ def typing_bar_item_cb(data, current_buffer, args): # first look for people typing in this channel if current_channel: - if current_channel.is_someone_typing(): - typers += current_channel.get_typing_list() + # this try is mostly becuase server buffers don't implement is_someone_typing + try: + if current_channel.is_someone_typing(): + typers += current_channel.get_typing_list() + except: + pass # here is where we notify you that someone is typing in DM # regardless of which buffer you are in currently -- cgit From 1d3a3b7049ced6d47eea3541f96d79c6105b8efb Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 15:23:47 -0800 Subject: just make slackteam act similar to slackchannel --- wee_slack.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index abbfa09..0656900 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -615,7 +615,7 @@ class SlackTeam(object): self.bots = bots self.team_hash = str(sha.sha("{}{}".format(self.nick, self.subdomain)).hexdigest()) self.name = self.domain - self.server_buffer = None + self.channel_buffer = None self.got_history = True self.create_buffer() for c in self.channels.keys(): @@ -635,14 +635,16 @@ class SlackTeam(object): # def connect_request_generate(self): # return SlackRequest(self.token, 'rtm.start', {}) def create_buffer(self): - if not self.server_buffer: - self.server_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") - self.eventrouter.weechat_controller.register_buffer(self.server_buffer, self) - if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': - w.buffer_merge(self.server_buffer, w.buffer_search_main()) - w.buffer_set(self.server_buffer, "nicklist", "1") + if not self.channel_buffer: + self.channel_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") + self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) + if w.config_string(w.config_get('irc.look.channel_buffer')) == 'merge_with_core': + w.buffer_merge(self.channel_buffer, w.buffer_search_main()) + w.buffer_set(self.channel_buffer, "nicklist", "1") + def formatted_name(self, **kwargs): + return self.domain def buffer_prnt(self, data): - w.prnt_date_tags(self.server_buffer, SlackTS().major, tag("backlog"), data) + w.prnt_date_tags(self.channel_buffer, SlackTS().major, tag("backlog"), data) def get_channel_map(self): return {v.slack_name: k for k, v in self.channels.iteritems()} def get_username_map(self): @@ -793,11 +795,17 @@ class SlackChannel(object): self.eventrouter.weechat_controller.set_refresh_buffer_list(True) try: if self.unread_count != 0: - w.buffer_set(self.channel_buffer, "hotlist", "1") + for c in range(1, self.unread_count): + if self.type == "im": + w.buffer_set(self.channel_buffer, "hotlist", "2") + else: + w.buffer_set(self.channel_buffer, "hotlist", "1") else: - dbg("no unread in {}".format(self.name)) + pass + #dbg("no unread in {}".format(self.name)) except: - dbg("exception no unread count") + pass + #dbg("exception no unread count") #if self.unread_count != 0 and not self.muted: # w.buffer_set(self.channel_buffer, "hotlist", "1") def destroy_buffer(self, update_remote): -- cgit From 773273b555f41203921264e699d2a3d4dc0d8615 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 15:43:18 -0800 Subject: nick completion --- wee_slack.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 0656900..c69fd9c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -548,6 +548,69 @@ def typing_bar_item_cb(data, current_buffer, args): return typing +def nick_completion_cb(data, completion_item, current_buffer, completion): + """ + Adds all @-prefixed nicks to completion list + """ + + current_buffer = w.current_buffer() + current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + + if current_channel is None or current_channel.members is None: + return w.WEECHAT_RC_OK + for m in current_channel.members: + u = current_channel.team.users.get(m, None) + if u: + w.hook_completion_list_add(completion, "@" + u.slack_name, 1, w.WEECHAT_LIST_POS_SORT) + return w.WEECHAT_RC_OK + + +def complete_next_cb(data, current_buffer, command): + """Extract current word, if it is equal to a nick, prefix it with @ and + rely on nick_completion_cb adding the @-prefixed versions to the + completion lists, then let Weechat's internal completion do its + thing + + """ + + current_buffer = w.current_buffer() + current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + + #channel = channels.find(current_buffer) + if current_channel is None or current_channel.members is None: + return w.WEECHAT_RC_OK + + line_input = w.buffer_get_string(current_buffer, "input") + current_pos = w.buffer_get_integer(current_buffer, "input_pos") - 1 + input_length = w.buffer_get_integer(current_buffer, "input_length") + + word_start = 0 + word_end = input_length + # If we're on a non-word, look left for something to complete + while current_pos >= 0 and line_input[current_pos] != '@' and not line_input[current_pos].isalnum(): + current_pos = current_pos - 1 + if current_pos < 0: + current_pos = 0 + for l in range(current_pos, 0, -1): + if line_input[l] != '@' and not line_input[l].isalnum(): + word_start = l + 1 + break + for l in range(current_pos, input_length): + if not line_input[l].isalnum(): + word_end = l + break + word = line_input[word_start:word_end] + + for m in current_channel.members: + u = current_channel.team.users.get(m, None) + if u and u.slack_name == word: + # Here, we cheat. Insert a @ in front and rely in the @ + # nicks being in the completion list + w.buffer_set(current_buffer, "input", line_input[:word_start] + "@" + line_input[word_start:]) + w.buffer_set(current_buffer, "input_pos", str(w.buffer_get_integer(current_buffer, "input_pos") + 1)) + return w.WEECHAT_RC_OK_EAT + return w.WEECHAT_RC_OK + def script_unloaded(): stop_talking_to_slack() return w.WEECHAT_RC_OK @@ -2102,8 +2165,7 @@ if __name__ == "__main__": w.hook_command_run('/label', 'label_command_cb', '') w.hook_command_run("/input complete_next", "complete_next_cb", "") w.hook_command_run('/away', 'away_command_cb', '') - w.hook_completion("nicks", "complete @-nicks for slack", - "nick_completion_cb", "") + w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") tokens = config.slack_api_token.split(',') for t in tokens: -- cgit From 384bad8dfbd224b7cfeeddd17c0cfe3e9a4f95a6 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 18:04:06 -0800 Subject: proper history handling --- wee_slack.py | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 19 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index c69fd9c..1bb0cae 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -329,7 +329,10 @@ def handle_next(*args): try: EVENTROUTER.handle_next() except: - pass + if config.debug_mode: + traceback.print_exc() + else: + pass return w.WEECHAT_RC_OK class WeechatController(object): @@ -756,6 +759,7 @@ class SlackChannel(object): def __init__(self, eventrouter, **kwargs): # We require these two things for a vaid object, # the rest we can just learn from slack + self.active = False for key, value in kwargs.items(): setattr(self, key, value) self.eventrouter = eventrouter @@ -810,7 +814,7 @@ class SlackChannel(object): setattr(self, key, value) def open(self, update_remote=True): self.create_buffer() - #self.active = True + self.active = True self.get_history() if "info" in SLACK_API_TRANSLATOR[self.type]: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) @@ -874,8 +878,10 @@ class SlackChannel(object): def destroy_buffer(self, update_remote): if self.channel_buffer is not None: self.channel_buffer = None + self.messages = {} self.got_history = False #if update_remote and not eventrouter.shutting_down: + self.active = False if update_remote and not self.eventrouter.shutting_down: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) @@ -907,6 +913,8 @@ class SlackChannel(object): self.team.send_to_websocket(request) self.mark_read(force=True) def store_message(self, message, team, from_me=False): + if not self.active: + return if from_me: message.message_json["user"] = team.myidentifier self.messages[SlackTS(message.ts)] = message @@ -929,13 +937,14 @@ class SlackChannel(object): def is_visible(self): return w.buffer_get_integer(self.channel_buffer, "hidden") == 0 def get_history(self): - #if config.cache_messages: - # for message in message_cache[self.identifier]: - # process_message(json.loads(message), True) - s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier) - self.eventrouter.receive(s) - #async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}) - self.got_history = True + if not self.got_history: + #if config.cache_messages: + # for message in message_cache[self.identifier]: + # process_message(json.loads(message), True) + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + #async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}) + self.got_history = True def send_add_reaction(self, msg_number, reaction): self.send_change_reaction("reactions.add", msg_number, reaction) def send_remove_reaction(self, msg_number, reaction): @@ -1106,6 +1115,56 @@ class SlackMPDMChannel(SlackChannel): def rename(self): pass +class SlackThreadChannel(object): + """ + A thread channel is a virtual channel. We don't inherit from + SlackChannel, because most of how it operates will be different. + """ + def __init__(self, eventrouter, **kwargs): + self.identifier = "" + self.name = "#" + kwargs['name'] + self.type = "group" + self.set_name(self.slack_name) + def set_name(self, slack_name): + self.name = "#" + slack_name + #def formatted_name(self, prepend="#", enable_color=True, basic=False): + # return prepend + self.slack_name + def create_buffer(self): + """ + incomplete (muted doesn't work) + Creates the weechat buffer where the channel magic happens. + """ + if not self.channel_buffer: + self.channel_buffer = w.buffer_new(self.formatted_name(style="long_default"), "buffer_input_callback", "EVENTROUTER", "", "") + self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) + if self.type == "im": + w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') + else: + w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') + w.buffer_set(self.channel_buffer, "localvar_set_channel", self.formatted_name()) + w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) + #if self.server.alias: + # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) + #else: + # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + try: + if self.unread_count != 0: + for c in range(1, self.unread_count): + if self.type == "im": + w.buffer_set(self.channel_buffer, "hotlist", "2") + else: + w.buffer_set(self.channel_buffer, "hotlist", "1") + else: + pass + #dbg("no unread in {}".format(self.name)) + except: + pass + #dbg("exception no unread count") + #if self.unread_count != 0 and not self.muted: + # w.buffer_set(self.channel_buffer, "hotlist", "1") + + class SlackUser(object): """ Represends an individual slack user. Also where you set their name formatting. @@ -1156,12 +1215,16 @@ class SlackMessage(object): self.team = team self.channel = channel self.message_json = message_json - self.submessages = None + self.submessages = [] self.sender = self.get_sender() self.suffix = '' self.ts = SlackTS(message_json['ts']) + def __hash__(self): + return hash(self.ts) def render(self, force=False): - return render(self.message_json, self.team, self.channel, force) + self.suffix + if len(self.submessages) > 0: + return "{} {} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix, "[Thread: {}]".format(self.ts)) + return "{} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix) def change_text(self, new_text): self.message_json["text"] = new_text dbg(self.message_json) @@ -1212,6 +1275,12 @@ class SlackMessage(object): else: pass +class SlackThreadMessage(SlackMessage): + def __init__(self, parent_id, *args): + super(SlackThreadMessage, self).__init__(*args) + #super(SlackBot, self).__init__(**kwargs) + self.parent_id = parent_id + class WeeSlackMetadata(object): """ @@ -1370,8 +1439,8 @@ def process_message(message_json, eventrouter, store=True, **kwargs): try: # send these subtype messages elsewhere known_subtypes = [ - #'thread_message', - #'message_replied', + 'thread_message', + 'message_replied', 'message_changed', 'message_deleted', #'channel_join', @@ -1418,7 +1487,19 @@ def process_message(message_json, eventrouter, store=True, **kwargs): traceback.print_exc() def subprocess_thread_message(message_json, eventrouter, channel, team): - dbg("REPLIEDDDD: " + str(message_json)) + print ("THREADED: " + str(message_json)) + parent_ts = message_json.get('thread_ts', None) + if parent_ts: + #parent_ts = SlackTS(parent_ts) + parent_ts = parent_ts + parent_message = channel.messages.get(SlackTS(parent_ts), None) + if parent_message: + message = SlackThreadMessage(parent_ts, message_json, team, channel) + channel.store_message(message, team) + channel.change_message(parent_ts) + parent_message.submessages.append(message) + print channel + print message # channel = channels.find(message_json["channel"]) # server = channel.server # #threadinfo = channel.get_message(message_json["thread_ts"]) @@ -1437,8 +1518,11 @@ def subprocess_thread_message(message_json, eventrouter, channel, team): #channel.change_message(message_json["thread_ts"], None, message_json["text"]) #channel.become_thread(message_json["item"]["ts"], message_json) +def subprocess_message_replied(message_json, eventrouter, channel, team): + print ("REPLIED: " + str(message_json)) def subprocess_message_changed(message_json, eventrouter, channel, team): + print "CHANGED!!" m = message_json.get("message", None) if m: new_message = m @@ -1503,8 +1587,11 @@ def process_channel_marked(message_json, eventrouter, **kwargs): complete """ channel = kwargs["channel"] - ts = kwargs["ts"] - channel.mark_read(ts=ts, update_remote=False) + ts = kwargs.get("ts", None) + if ts: + channel.mark_read(ts=ts, update_remote=False) + else: + dbg("tried to mark something weird {}".format(message_json)) def process_group_marked(message_json, eventrouter, **kwargs): process_channel_marked(message_json, eventrouter, **kwargs) def process_im_marked(message_json, eventrouter, **kwargs): @@ -1536,7 +1623,7 @@ def process_im_close(message_json, eventrouter, **kwargs): def process_reaction_added(message_json, eventrouter, **kwargs): channel = kwargs['team'].channels[message_json["item"]["channel"]] if message_json["item"].get("type") == "message": - ts = message_json['item']["ts"] + ts = SlackTS(message_json['item']["ts"]) channel.messages[ts].add_reaction(message_json["reaction"], message_json["user"]) channel.change_message(ts) @@ -1546,7 +1633,7 @@ def process_reaction_added(message_json, eventrouter, **kwargs): def process_reaction_removed(message_json, eventrouter, **kwargs): channel = kwargs['team'].channels[message_json["item"]["channel"]] if message_json["item"].get("type") == "message": - ts = message_json['item']["ts"] + ts = SlackTS(message_json['item']["ts"]) channel.messages[ts].remove_reaction(message_json["reaction"], message_json["user"]) channel.change_message(ts) @@ -1589,6 +1676,7 @@ def render(message_json, team, channel, force=False): # #for thread in self.threads: text += create_reaction_string(message_json.get("reactions", "")) + message_json["_rendered_text"] = text return text @@ -2132,7 +2220,7 @@ if __name__ == "__main__": w.hook_timer(1000, 0, 0, "buffer_list_update_callback", "EVENTROUTER") w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") - w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") + #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") w.hook_signal('buffer_closing', "buffer_closing_callback", "EVENTROUTER") #w.hook_signal('buffer_opened', "buffer_opened_cb", "") w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") -- cgit From a69224e10f205003f0bded5f990a8b8372d853b2 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 19:30:03 -0800 Subject: this code is a hot mess but it works --- wee_slack.py | 169 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 36 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 1bb0cae..6d643a2 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -823,7 +823,7 @@ class SlackChannel(object): if "join" in SLACK_API_TRANSLATOR[self.type]: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) - self.create_buffer() + #self.create_buffer() def check_should_open(self, force=False): try: if self.is_archived: @@ -847,6 +847,7 @@ class SlackChannel(object): Creates the weechat buffer where the channel magic happens. """ if not self.channel_buffer: + self.active = True self.channel_buffer = w.buffer_new(self.formatted_name(style="long_default"), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) if self.type == "im": @@ -1120,50 +1121,123 @@ class SlackThreadChannel(object): A thread channel is a virtual channel. We don't inherit from SlackChannel, because most of how it operates will be different. """ - def __init__(self, eventrouter, **kwargs): - self.identifier = "" - self.name = "#" + kwargs['name'] - self.type = "group" - self.set_name(self.slack_name) - def set_name(self, slack_name): - self.name = "#" + slack_name - #def formatted_name(self, prepend="#", enable_color=True, basic=False): - # return prepend + self.slack_name + def __init__(self, eventrouter, parent_message): + self.eventrouter = eventrouter + self.parent_message = parent_message + self.channel_buffer = None + #self.identifier = "" + #self.name = "#" + kwargs['name'] + self.type = "thread" + self.got_history = False + #self.set_name(self.slack_name) + #def set_name(self, slack_name): + # self.name = "#" + slack_name + def formatted_name(self, style="default", **kwargs): + styles = { + "default": " +{}".format(self.parent_message.ts), + "long_default": "{}.{}".format(self.parent_message.channel.formatted_name(style="long_default"), self.parent_message.ts), + "sidebar": " +{}".format(self.parent_message.ts), + } + return styles[style] + def refresh(self): + pass + def mark_read(self, ts=None, update_remote=True, force=False): + if self.channel_buffer: + w.buffer_set(self.channel_buffer, "unread", "") + w.buffer_set(self.channel_buffer, "hotlist", "-1") + + def buffer_prnt(self, nick, text, timestamp, **kwargs): + data = "{}\t{}".format(nick, text) + ts = SlackTS(timestamp) + if self.channel_buffer: + #backlog messages - we will update the read marker as we print these + #backlog = False + #if ts <= SlackTS(self.last_read): + # tags = tag("backlog") + # backlog = True + #elif self.type in ["im", "mpdm"]: + # tags = tag("dm") + # self.new_messages = True + #else: + tags = tag("default") + #self.new_messages = True + w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) + modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) + #if backlog: + # self.mark_read(ts, update_remote=False, force=True) + def get_history(self): + self.got_history = True + for message in self.parent_message.submessages: + + #message = SlackMessage(message_json, team, channel) + text = message.render() + #print text + + suffix = '' + if 'edited' in message.message_json: + suffix = ' (edited)' + #try: + # channel.unread_count += 1 + #except: + # channel.unread_count = 1 + self.buffer_prnt(message.sender, text + suffix, message.ts) + + def send_message(self, message): + #team = self.eventrouter.teams[self.team] + message = linkify_text(message, self.parent_message.team, self) + dbg(message) + request = {"type": "message", "channel": self.parent_message.channel.identifier, "text": message, "_team": self.parent_message.team.team_hash, "user": self.parent_message.team.myidentifier, "thread_ts": str(self.parent_message.ts)} + self.parent_message.team.send_to_websocket(request) + self.mark_read(force=True) + + def open(self, update_remote=True): + self.create_buffer() + self.active = True + self.get_history() + #if "info" in SLACK_API_TRANSLATOR[self.type]: + # s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + # self.eventrouter.receive(s) + #if update_remote: + # if "join" in SLACK_API_TRANSLATOR[self.type]: + # s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + # self.eventrouter.receive(s) + self.create_buffer() + def create_buffer(self): """ incomplete (muted doesn't work) - Creates the weechat buffer where the channel magic happens. + Creates the weechat buffer where the thread magic happens. """ if not self.channel_buffer: self.channel_buffer = w.buffer_new(self.formatted_name(style="long_default"), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) - if self.type == "im": - w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') - else: - w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') + w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') w.buffer_set(self.channel_buffer, "localvar_set_channel", self.formatted_name()) w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) - #if self.server.alias: - # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) - #else: - # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) - self.eventrouter.weechat_controller.set_refresh_buffer_list(True) - try: - if self.unread_count != 0: - for c in range(1, self.unread_count): - if self.type == "im": - w.buffer_set(self.channel_buffer, "hotlist", "2") - else: - w.buffer_set(self.channel_buffer, "hotlist", "1") - else: - pass - #dbg("no unread in {}".format(self.name)) - except: - pass + + #self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + + #try: + # if self.unread_count != 0: + # for c in range(1, self.unread_count): + # if self.type == "im": + # w.buffer_set(self.channel_buffer, "hotlist", "2") + # else: + # w.buffer_set(self.channel_buffer, "hotlist", "1") + # else: + # pass + # #dbg("no unread in {}".format(self.name)) + #except: + # pass #dbg("exception no unread count") #if self.unread_count != 0 and not self.muted: # w.buffer_set(self.channel_buffer, "hotlist", "1") - + def destroy_buffer(self, update_remote): + if self.channel_buffer is not None: + self.channel_buffer = None + self.got_history = False + #if update_remote and not eventrouter.shutting_down: + self.active = False class SlackUser(object): """ @@ -1216,6 +1290,7 @@ class SlackMessage(object): self.channel = channel self.message_json = message_json self.submessages = [] + self.thread_channel = None self.sender = self.get_sender() self.suffix = '' self.ts = SlackTS(message_json['ts']) @@ -1495,11 +1570,15 @@ def subprocess_thread_message(message_json, eventrouter, channel, team): parent_message = channel.messages.get(SlackTS(parent_ts), None) if parent_message: message = SlackThreadMessage(parent_ts, message_json, team, channel) + parent_message.submessages.append(message) channel.store_message(message, team) channel.change_message(parent_ts) - parent_message.submessages.append(message) - print channel - print message + + text = message.render() + #channel.buffer_prnt(message.sender, text, message.ts, **kwargs) + if parent_message.thread_channel: + parent_message.thread_channel.buffer_prnt(message.sender, text, message.ts) + # channel = channels.find(message_json["channel"]) # server = channel.server # #threadinfo = channel.get_message(message_json["thread_ts"]) @@ -1945,6 +2024,23 @@ def command_talk(current_buffer, args): w.buffer_set(chan.channel_buffer, "display", "1") return True +def thread_command_callback(data, current_buffer, args): + current = w.current_buffer() + channel = EVENTROUTER.weechat_controller.buffers[current] + args = args.split() + if len(args) < 2: + w.prnt(current, "Missing thread id argument") + return w.WEECHAT_RC_OK_EAT + else: + pm = channel.messages[SlackTS(args[1])] + tc = SlackThreadChannel(EVENTROUTER, pm) + pm.thread_channel = tc + tc.open() + #tc.create_buffer() + return w.WEECHAT_RC_OK_EAT + return w.WEECHAT_RC_OK + + def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) if len(a) > 1: @@ -2249,6 +2345,7 @@ if __name__ == "__main__": w.hook_command_run('/part', 'part_command_cb', '') w.hook_command_run('/leave', 'part_command_cb', '') w.hook_command_run('/topic', 'topic_command_cb', '') + w.hook_command_run('/thread', 'thread_command_callback', '') w.hook_command_run('/msg', 'msg_command_cb', '') w.hook_command_run('/label', 'label_command_cb', '') w.hook_command_run("/input complete_next", "complete_next_cb", "") -- cgit From 3770e91c8943acf965502397d916ff25ea719c53 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 19:34:29 -0800 Subject: threads finally --- wee_slack.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 6d643a2..5c63891 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2083,6 +2083,12 @@ def command_nodistractions(current_buffer, args): # config.distracting_channels.pop(config.distracting_channels.index(channel)) # save_distracting_channels() +def label_command_cb(data, current_buffer, args): + channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer) + if channel and channel.type == 'thread': + aargs = args.split(None, 2) + new_name = " +" + aargs[1] + w.buffer_set(channel.channel_buffer, "short_name", new_name) def command_p(current_buffer, args): w.prnt("", "{}".format(eval(args))) -- cgit From 3bab35891014695958914746af75d10cea4e355b Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 19:45:24 -0800 Subject: remove debug messages and add tiny doc --- README.md | 8 ++++++++ wee_slack.py | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 347e7f7..87447f7 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ [![Build Status](https://travis-ci.org/wee-slack/wee-ng.svg?branch=master)](https://travis-ci.org/wee-slack/wee-ng) + + +### Using threads in wee-ng beta (note: you can't start threads yet, but you can participate in existing ones): + +1) look for [Threaded: 12345677.0000] at the end of a message. this means it has threads. +2) in the buffer with the threaded message type `/thread 12345677.0000` +3) a new buffer will open named ` +12345677.0000`, which is the thread buffer +4) if you'd like to give thread buffers a friendly name, switch to them and type `/label [newname]` diff --git a/wee_slack.py b/wee_slack.py index 5c63891..c30be56 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1562,7 +1562,7 @@ def process_message(message_json, eventrouter, store=True, **kwargs): traceback.print_exc() def subprocess_thread_message(message_json, eventrouter, channel, team): - print ("THREADED: " + str(message_json)) + #print ("THREADED: " + str(message_json)) parent_ts = message_json.get('thread_ts', None) if parent_ts: #parent_ts = SlackTS(parent_ts) @@ -1598,7 +1598,8 @@ def subprocess_thread_message(message_json, eventrouter, channel, team): #channel.become_thread(message_json["item"]["ts"], message_json) def subprocess_message_replied(message_json, eventrouter, channel, team): - print ("REPLIED: " + str(message_json)) + pass + #print ("REPLIED: " + str(message_json)) def subprocess_message_changed(message_json, eventrouter, channel, team): print "CHANGED!!" -- cgit From 3dcab7eae762b87f002f2d6b6372e64767102671 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 19:46:07 -0800 Subject: i still suck at markdown --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 87447f7..89309a0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### Using threads in wee-ng beta (note: you can't start threads yet, but you can participate in existing ones): -1) look for [Threaded: 12345677.0000] at the end of a message. this means it has threads. -2) in the buffer with the threaded message type `/thread 12345677.0000` -3) a new buffer will open named ` +12345677.0000`, which is the thread buffer -4) if you'd like to give thread buffers a friendly name, switch to them and type `/label [newname]` +1. look for [Threaded: 12345677.0000] at the end of a message. this means it has threads. +2. in the buffer with the threaded message type `/thread 12345677.0000` +3. a new buffer will open named ` +12345677.0000`, which is the thread buffer +4. if you'd like to give thread buffers a friendly name, switch to them and type `/label [newname]` -- cgit From 614751f3fb9d4e69390ed8faf118459ef2149655 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 20:02:23 -0800 Subject: remove debug msg --- wee_slack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index c30be56..6726244 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1602,7 +1602,6 @@ def subprocess_message_replied(message_json, eventrouter, channel, team): #print ("REPLIED: " + str(message_json)) def subprocess_message_changed(message_json, eventrouter, channel, team): - print "CHANGED!!" m = message_json.get("message", None) if m: new_message = m -- cgit From a110414a4f0e667a2148c2991eb7730df3a61f08 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 20:20:41 -0800 Subject: default diff color for thread suffix --- wee_slack.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 6726244..51fe1be 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1298,7 +1298,7 @@ class SlackMessage(object): return hash(self.ts) def render(self, force=False): if len(self.submessages) > 0: - return "{} {} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix, "[Thread: {}]".format(self.ts)) + return "{} {} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix, "{}[ Thread: {} Replies: {} ]".format(w.color(config.thread_suffix_color), self.ts, len(self.submessages))) return "{} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix) def change_text(self, new_text): self.message_json["text"] = new_text @@ -2175,6 +2175,7 @@ class PluginConfig(object): 'unfurl_ignore_alt_text': 'false', 'cache_messages': 'true', 'record_events': 'false', + 'thread_suffix_color': 'lightcyan', } # Set missing settings to their defaults. Load non-missing settings from @@ -2222,6 +2223,9 @@ class PluginConfig(object): else: return token + def get_thread_suffix_color(self, key): + return w.config_get_plugin("thread_suffix_color") + def get_slack_timeout(self, key): return int(w.config_get_plugin(key)) -- cgit From 381da055cdfd7156815663471625be9254c70458 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 20:37:42 -0800 Subject: new option to unhide buffers when they have new activity --- wee_slack.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 51fe1be..fcc835b 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -902,6 +902,9 @@ class SlackChannel(object): tags = tag("default") self.new_messages = True + if config.unhide_buffers_with_activity and not self.is_visible(): + w.buffer_set(self.channel_buffer, "hidden", "0") + w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) if backlog: @@ -2176,6 +2179,7 @@ class PluginConfig(object): 'cache_messages': 'true', 'record_events': 'false', 'thread_suffix_color': 'lightcyan', + 'unhide_buffers_with_activity': 'false', } # Set missing settings to their defaults. Load non-missing settings from -- cgit From 55e760cdedb2c57311883a6e38bc84404f249db5 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 20:54:06 -0800 Subject: don't show muted channel actvity when hidden --- wee_slack.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index fcc835b..eee977a 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -664,7 +664,7 @@ class SlackTeam(object): incomplete Team object under which users and channels live.. Does lots. """ - def __init__(self, eventrouter, token, subdomain, nick, myidentifier, users, bots, channels): + def __init__(self, eventrouter, token, subdomain, nick, myidentifier, users, bots, channels, **kwargs): self.state = "disconnected" self.ws = None self.ws_counter = 0 @@ -684,6 +684,7 @@ class SlackTeam(object): self.channel_buffer = None self.got_history = True self.create_buffer() + self.muted_channels = [x for x in kwargs.get('muted_channels', []).split(',')] for c in self.channels.keys(): channels[c].set_related_server(self) channels[c].check_should_open() @@ -902,7 +903,7 @@ class SlackChannel(object): tags = tag("default") self.new_messages = True - if config.unhide_buffers_with_activity and not self.is_visible(): + if config.unhide_buffers_with_activity and not self.is_visible() and (self.identifier not in self.team.muted_channels): w.buffer_set(self.channel_buffer, "hidden", "0") w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) @@ -1448,6 +1449,7 @@ def handle_rtmstart(login_data, eventrouter): users, bots, channels, + muted_channels=login_data["self"]["prefs"]["muted_channels"], ) eventrouter.register_team(t) -- cgit From 0e17aed54659008daad66dacc379213ddf1d0bb0 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 21:10:07 -0800 Subject: allow tmp mute --- wee_slack.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index eee977a..01876bf 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -684,7 +684,7 @@ class SlackTeam(object): self.channel_buffer = None self.got_history = True self.create_buffer() - self.muted_channels = [x for x in kwargs.get('muted_channels', []).split(',')] + self.muted_channels = {x for x in kwargs.get('muted_channels', []).split(',')} for c in self.channels.keys(): channels[c].set_related_server(self) channels[c].check_should_open() @@ -2029,6 +2029,19 @@ def command_talk(current_buffer, args): w.buffer_set(chan.channel_buffer, "display", "1") return True +def command_tmpmute(current_buffer, args): + current = w.current_buffer() + channel_id = EVENTROUTER.weechat_controller.buffers[current].identifier + team = EVENTROUTER.weechat_controller.buffers[current].team + if channel_id not in team.muted_channels: + team.muted_channels.add(channel_id) + else: + team.muted_channels.discard(channel_id) + +def command_showmuted(current_buffer, args): + current = w.current_buffer() + w.prnt(EVENTROUTER.weechat_controller.buffers[current].team.channel_buffer, str(EVENTROUTER.weechat_controller.buffers[current].team.muted_channels)) + def thread_command_callback(data, current_buffer, args): current = w.current_buffer() channel = EVENTROUTER.weechat_controller.buffers[current] -- cgit From a4a2f5ae20e6583721e3a14eda7267e34314982b Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sat, 4 Feb 2017 23:25:18 -0800 Subject: disable unused hook --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 01876bf..e6733f8 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2352,7 +2352,7 @@ if __name__ == "__main__": w.hook_signal('window_switch', "buffer_switch_callback", "EVENTROUTER") #w.hook_signal('input_text_changed', "typing_notification_cb", "") w.hook_signal('quit', "quit_notification_cb", "") - w.hook_signal('window_scrolled', "scrolled_cb", "") + #w.hook_signal('window_scrolled', "scrolled_cb", "") cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} w.hook_command( # Command name and description -- cgit From 805aed26e81882eae2c21f4e808cb7cb1ff235a6 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 09:34:17 -0800 Subject: move hook setup to function --- wee_slack.py | 115 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 56 insertions(+), 59 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index e6733f8..f0a2dea 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -9,7 +9,7 @@ import re import urllib import sys import traceback -import collections +#import collections import ssl import random import string @@ -2146,6 +2146,42 @@ def create_slack_debug_buffer(): slack_debug = w.buffer_new("slack-debug", "", "", "closed_slack_debug_buffer_cb", "") w.buffer_set(slack_debug, "notify", "0") +def setup_hooks(): + cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} + w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') + w.hook_timer(1000, 0, 0, "typing_update_cb", "") + w.hook_timer(1000, 0, 0, "buffer_list_update_callback", "EVENTROUTER") + w.hook_signal('buffer_closing', "buffer_closing_callback", "EVENTROUTER") + w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") + w.hook_signal('window_switch', "buffer_switch_callback", "EVENTROUTER") + w.hook_signal('quit', "quit_notification_cb", "") + w.hook_command( + # Command name and description + 'slack', 'Plugin to allow typing notification and sync of read markers for slack.com', + # Usage + '[command] [command options]', + # Description of arguments + 'Commands:\n' + + '\n'.join(cmds.keys()) + + '\nUse /slack help [command] to find out more\n', + # Completions + '|'.join(cmds.keys()), + # Function name + 'slack_command_cb', '') + w.hook_command('me', 'me_command_cb', '') + w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') + w.hook_command_run('/query', 'join_command_cb', '') + w.hook_command_run('/join', 'join_command_cb', '') + w.hook_command_run('/part', 'part_command_cb', '') + w.hook_command_run('/leave', 'part_command_cb', '') + w.hook_command_run('/topic', 'topic_command_cb', '') + w.hook_command_run('/thread', 'thread_command_callback', '') + w.hook_command_run('/msg', 'msg_command_cb', '') + w.hook_command_run('/label', 'label_command_cb', '') + w.hook_command_run("/input complete_next", "complete_next_cb", "") + w.hook_command_run('/away', 'away_command_cb', '') + w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") + ##### END NEW @@ -2305,81 +2341,42 @@ if __name__ == "__main__": EVENTROUTER = EventRouter() #setup_trace() - WEECHAT_HOME = w.info_get("weechat_dir", "") - CACHE_NAME = "slack.cache" - STOP_TALKING_TO_SLACK = False + #WEECHAT_HOME = w.info_get("weechat_dir", "") + #CACHE_NAME = "slack.cache" + #STOP_TALKING_TO_SLACK = False # Global var section slack_debug = None config = PluginConfig() config_changed_cb = config.config_changed - typing_timer = time.time() - domain = None - previous_buffer = None - slack_buffer = None - - never_away = False - hide_distractions = False - hotlist = w.infolist_get("hotlist", "", "") - main_weechat_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, "DOESNOTEXIST!@#$")) + #typing_timer = time.time() + #domain = None + #previous_buffer = None + #slack_buffer = None - message_cache = collections.defaultdict(list) - if config.cache_messages: - cache_load() + #never_away = False + #hide_distractions = False + #hotlist = w.infolist_get("hotlist", "", "") + #main_weechat_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, "DOESNOTEXIST!@#$")) - #servers = SearchList() - #for token in config.slack_api_token.split(','): - # server = SlackServer(token) - # servers.append(server) - #channels = SearchList() - #users = SearchList() - #threads = SearchList() + #message_cache = collections.defaultdict(list) + #if config.cache_messages: + # cache_load() w.hook_config("plugins.var.python." + SCRIPT_NAME + ".*", "config_changed_cb", "") - #w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") + + setup_hooks() # attach to the weechat hooks we need - w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') - w.hook_timer(1000, 0, 0, "typing_update_cb", "") - w.hook_timer(1000, 0, 0, "buffer_list_update_callback", "EVENTROUTER") - w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") + # Hooks to fix/implement + #w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") - w.hook_signal('buffer_closing', "buffer_closing_callback", "EVENTROUTER") #w.hook_signal('buffer_opened', "buffer_opened_cb", "") - w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") - w.hook_signal('window_switch', "buffer_switch_callback", "EVENTROUTER") #w.hook_signal('input_text_changed', "typing_notification_cb", "") - w.hook_signal('quit', "quit_notification_cb", "") #w.hook_signal('window_scrolled', "scrolled_cb", "") - cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} - w.hook_command( - # Command name and description - 'slack', 'Plugin to allow typing notification and sync of read markers for slack.com', - # Usage - '[command] [command options]', - # Description of arguments - 'Commands:\n' + - '\n'.join(cmds.keys()) + - '\nUse /slack help [command] to find out more\n', - # Completions - '|'.join(cmds.keys()), - # Function name - 'slack_command_cb', '') - w.hook_command('me', 'me_command_cb', '') - w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') - w.hook_command_run('/query', 'join_command_cb', '') - w.hook_command_run('/join', 'join_command_cb', '') - w.hook_command_run('/part', 'part_command_cb', '') - w.hook_command_run('/leave', 'part_command_cb', '') - w.hook_command_run('/topic', 'topic_command_cb', '') - w.hook_command_run('/thread', 'thread_command_callback', '') - w.hook_command_run('/msg', 'msg_command_cb', '') - w.hook_command_run('/label', 'label_command_cb', '') - w.hook_command_run("/input complete_next", "complete_next_cb", "") - w.hook_command_run('/away', 'away_command_cb', '') - w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") + #w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") tokens = config.slack_api_token.split(',') for t in tokens: -- cgit From 19efdd77dbc585ad40a0f4e0d52025d06b3dff2f Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 12:36:46 -0800 Subject: fix commands --- wee_slack.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index f0a2dea..d040b82 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -531,7 +531,7 @@ def typing_bar_item_cb(data, current_buffer, args): if current_channel: # this try is mostly becuase server buffers don't implement is_someone_typing try: - if current_channel.is_someone_typing(): + if current_channel.type != 'im' and current_channel.is_someone_typing(): typers += current_channel.get_typing_list() except: pass @@ -750,6 +750,7 @@ class SlackTeam(object): self.ws.send(message) dbg("Sent {}...".format(message[:100])) except: + print "WS ERROR" dbg("Unexpected error: {}\nSent: {}".format(sys.exc_info()[0], data)) self.connected = False @@ -2067,7 +2068,7 @@ def slack_command_cb(data, current_buffer, args): function_name, args = a[0], None # try: - cmds[function_name](current_buffer, args) + EVENTROUTER.cmds[function_name](current_buffer, args) # except KeyError: # w.prnt("", "Command not found: " + function_name) return w.WEECHAT_RC_OK -- cgit From 1c415023c6fd835bf2717d083d3171b3ac9c58ae Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 13:16:32 -0800 Subject: use fast reconnect --- wee_slack.py | 56 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index d040b82..62b9437 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -78,9 +78,9 @@ if hasattr(ssl, "get_default_verify_paths") and callable(ssl.get_default_verify_ ##### BEGIN NEW IGNORED_EVENTS = [ - "reconnect_url", "hello", "pref_change", + #"reconnect_url", ] ###### New central Event router @@ -182,6 +182,12 @@ class EventRouter(object): else: raise InvalidType(type(team)) + def reconnect_if_disconnected(self): + for team_id, team in self.teams.iteritems(): + if not team.connected: + team.reconnect() + print team + def receive_ws_callback(self, team_hash): """ incomplete (reconnect) @@ -421,6 +427,10 @@ def receive_ws_callback(*args): EVENTROUTER.receive_ws_callback(args[0]) return w.WEECHAT_RC_OK +def reconnect_callback(*args): + EVENTROUTER.reconnect_if_disconnected() + return w.WEECHAT_RC_OK + def buffer_closing_callback(signal, sig_type, data): """ complete @@ -665,7 +675,6 @@ class SlackTeam(object): Team object under which users and channels live.. Does lots. """ def __init__(self, eventrouter, token, subdomain, nick, myidentifier, users, bots, channels, **kwargs): - self.state = "disconnected" self.ws = None self.ws_counter = 0 self.ws_replies = {} @@ -683,6 +692,7 @@ class SlackTeam(object): self.name = self.domain self.channel_buffer = None self.got_history = True + self.ws_reconnect_url = None self.create_buffer() self.muted_channels = {x for x in kwargs.get('muted_channels', []).split(',')} for c in self.channels.keys(): @@ -732,10 +742,24 @@ class SlackTeam(object): return False def mark_read(self): pass + def reconnect(self): + if not self.connected and self.ws_reconnect_url: + try: + ws = create_connection(self.ws_reconnect_url, sslopt=sslopt_ca_certs) + w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", self.get_team_hash()) + ws.sock.setblocking(0) + self.attach_websocket(ws) + self.set_connected() + except Exception as e: + dbg("websocket connection error: {}".format(e)) + self.set_reconnect_url(None) + return False def set_connected(self): self.connected = True def set_disconnected(self): self.connected = False + def set_reconnect_url(self, url): + self.ws_reconnect_url = url def next_ws_transaction_id(self): if self.ws_counter > 999: self.ws_counter = 0 @@ -752,7 +776,7 @@ class SlackTeam(object): except: print "WS ERROR" dbg("Unexpected error: {}\nSent: {}".format(sys.exc_info()[0], data)) - self.connected = False + self.set_connected() class SlackChannel(object): """ @@ -1497,6 +1521,9 @@ def handle_history(message_json, eventrouter, **kwargs): ###### New/converted process_ and subprocess_ methods +def process_reconnect_url(message_json, eventrouter, **kwargs): + kwargs['team'].set_reconnect_url(message_json['url']) + def process_manual_presence_change(message_json, eventrouter, **kwargs): process_presence_change(message_json, eventrouter, **kwargs) @@ -2149,13 +2176,18 @@ def create_slack_debug_buffer(): def setup_hooks(): cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} + w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') + w.hook_timer(1000, 0, 0, "typing_update_cb", "") w.hook_timer(1000, 0, 0, "buffer_list_update_callback", "EVENTROUTER") + w.hook_timer(3000, 0, 0, "reconnect_callback", "EVENTROUTER") + w.hook_signal('buffer_closing', "buffer_closing_callback", "EVENTROUTER") w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") w.hook_signal('window_switch', "buffer_switch_callback", "EVENTROUTER") w.hook_signal('quit', "quit_notification_cb", "") + w.hook_command( # Command name and description 'slack', 'Plugin to allow typing notification and sync of read markers for slack.com', @@ -2171,6 +2203,7 @@ def setup_hooks(): 'slack_command_cb', '') w.hook_command('me', 'me_command_cb', '') w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') + w.hook_command_run('/query', 'join_command_cb', '') w.hook_command_run('/join', 'join_command_cb', '') w.hook_command_run('/part', 'part_command_cb', '') @@ -2181,8 +2214,17 @@ def setup_hooks(): w.hook_command_run('/label', 'label_command_cb', '') w.hook_command_run("/input complete_next", "complete_next_cb", "") w.hook_command_run('/away', 'away_command_cb', '') + w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") + # Hooks to fix/implement + #w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") + #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") + #w.hook_signal('buffer_opened', "buffer_opened_cb", "") + #w.hook_signal('input_text_changed', "typing_notification_cb", "") + #w.hook_signal('window_scrolled', "scrolled_cb", "") + #w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") + ##### END NEW @@ -2371,14 +2413,6 @@ if __name__ == "__main__": # attach to the weechat hooks we need - # Hooks to fix/implement - #w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") - #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") - #w.hook_signal('buffer_opened', "buffer_opened_cb", "") - #w.hook_signal('input_text_changed', "typing_notification_cb", "") - #w.hook_signal('window_scrolled', "scrolled_cb", "") - #w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") - tokens = config.slack_api_token.split(',') for t in tokens: s = SlackRequest(t, 'rtm.start', {}) -- cgit From 2a12c3111d99cdf89b2e79430074fbf6ec7ddfca Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 16:30:45 -0800 Subject: move websocket handler - lots of reconnect --- wee_slack.py | 322 +++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 202 insertions(+), 120 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 62b9437..fea2674 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -80,7 +80,7 @@ if hasattr(ssl, "get_default_verify_paths") and callable(ssl.get_default_verify_ IGNORED_EVENTS = [ "hello", "pref_change", - #"reconnect_url", + "reconnect_url", ] ###### New central Event router @@ -98,6 +98,8 @@ class EventRouter(object): to the location specified in RECORD_DIR. """ self.queue = [] + self.slow_queue = [] + self.slow_queue_timer = 0 self.teams = {} self.context = {} self.weechat_controller = WeechatController(self) @@ -185,7 +187,7 @@ class EventRouter(object): def reconnect_if_disconnected(self): for team_id, team in self.teams.iteritems(): if not team.connected: - team.reconnect() + team.connect() print team def receive_ws_callback(self, team_hash): @@ -228,23 +230,35 @@ class EventRouter(object): request_metadata = self.retrieve_context(data) dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out))) if return_code == 0: - if request_metadata.response_id in self.reply_buffer: - self.reply_buffer[request_metadata.response_id] += out + if len(out) > 0: + if request_metadata.response_id in self.reply_buffer: + #dbg("found response id in reply_buffer", True) + self.reply_buffer[request_metadata.response_id] += out + else: + #dbg("didn't find response id in reply_buffer", True) + self.reply_buffer[request_metadata.response_id] = "" + self.reply_buffer[request_metadata.response_id] += out + try: + j = json.loads(self.reply_buffer[request_metadata.response_id]) + except: + pass + #dbg("Incomplete json, awaiting more", True) + try: + j["wee_slack_process_method"] = request_metadata.request_normalized + j["wee_slack_request_metadata"] = pickle.dumps(request_metadata) + self.reply_buffer.pop(request_metadata.response_id) + if self.recording: + self.record_event(j, 'wee_slack_process_method') + self.receive_json(json.dumps(j)) + self.delete_context(data) + except: + dbg("HTTP REQUEST CALLBACK FAILED", True) + pass + # We got an empty reply and this is weird so just ditch it and retry else: - self.reply_buffer[request_metadata.response_id] = "" - self.reply_buffer[request_metadata.response_id] += out - try: - j = json.loads(self.reply_buffer[request_metadata.response_id]) - j["wee_slack_process_method"] = request_metadata.request_normalized - j["wee_slack_request_metadata"] = pickle.dumps(request_metadata) - self.reply_buffer.pop(request_metadata.response_id) - if self.recording: - self.record_event(j, 'wee_slack_process_method') - self.receive_json(json.dumps(j)) + dbg("length was zero, probably a bug..") self.delete_context(data) - except: - dbg("HTTP REQUEST CALLBACK FAILED", True) - pass + self.receive(request_metadata) elif return_code != -1: self.reply_buffer.pop(request_metadata.response_id, None) else: @@ -277,54 +291,65 @@ class EventRouter(object): via callback to drain events from the queue. It also attaches useful metadata and context to events as they are processed. """ + if len(self.slow_queue) > 0 and ((self.slow_queue_timer + 1) < time.time()): + for q in self.slow_queue[:]: + self.queue.append(q) + self.slow_queue = [] + self.slow_queue_timer = time.time() if len(self.queue) > 0: j = self.queue.pop(0) # Reply is a special case of a json reply from websocket. kwargs = {} if isinstance(j, SlackRequest): if j.should_try(): - local_process_async_slack_api_request(j, self) - return - - if "reply_to" in j: - dbg("SET FROM REPLY") - function_name = "reply" - elif "type" in j: - dbg("SET FROM type") - function_name = j["type"] - elif "wee_slack_process_method" in j: - dbg("SET FROM META") - function_name = j["wee_slack_process_method"] + if j.retry_ready(): + local_process_async_slack_api_request(j, self) + else: + self.slow_queue.append(j) + else: + dbg("Max retries for Slackrequest") + else: - dbg("SET FROM NADA") - function_name = "unknown" - # Here we are passing the actual objects. No more lookups. - meta = j.get("wee_slack_metadata", None) - if meta: - try: - if isinstance(meta, str): - dbg("string of metadata") - team = meta.get("team", None) - if team: - kwargs["team"] = self.teams[team] - if "user" in j: - kwargs["user"] = self.teams[team].users[j["user"]] - if "channel" in j: - kwargs["channel"] = self.teams[team].channels[j["channel"]] - except: - dbg("metadata failure") - - if function_name not in IGNORED_EVENTS: - dbg("running {}".format(function_name)) - if function_name.startswith("local_") and function_name in self.local_proc: - self.local_proc[function_name](j, self, **kwargs) - elif function_name in self.proc: - self.proc[function_name](j, self, **kwargs) - elif function_name in self.handlers: - self.handlers[function_name](j, self, **kwargs) + if "reply_to" in j: + dbg("SET FROM REPLY") + function_name = "reply" + elif "type" in j: + dbg("SET FROM type") + function_name = j["type"] + elif "wee_slack_process_method" in j: + dbg("SET FROM META") + function_name = j["wee_slack_process_method"] else: - raise ProcessNotImplemented(function_name) + dbg("SET FROM NADA") + function_name = "unknown" + + # Here we are passing the actual objects. No more lookups. + meta = j.get("wee_slack_metadata", None) + if meta: + try: + if isinstance(meta, str): + dbg("string of metadata") + team = meta.get("team", None) + if team: + kwargs["team"] = self.teams[team] + if "user" in j: + kwargs["user"] = self.teams[team].users[j["user"]] + if "channel" in j: + kwargs["channel"] = self.teams[team].channels[j["channel"]] + except: + dbg("metadata failure") + + if function_name not in IGNORED_EVENTS: + dbg("running {}".format(function_name)) + if function_name.startswith("local_") and function_name in self.local_proc: + self.local_proc[function_name](j, self, **kwargs) + elif function_name in self.proc: + self.proc[function_name](j, self, **kwargs) + elif function_name in self.handlers: + self.handlers[function_name](j, self, **kwargs) + else: + raise ProcessNotImplemented(function_name) def handle_next(*args): """ @@ -401,9 +426,12 @@ def local_process_async_slack_api_request(request, event_router): """ if not event_router.shutting_down: weechat_request = 'url:{}'.format(request.request_string()) + weechat_request += '&nonce={}'.format(''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(4))) params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} request.tried() 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, config.slack_timeout, "receive_httprequest_callback", context) ###### New Callbacks @@ -651,6 +679,7 @@ class SlackRequest(object): for key, value in kwargs.items(): setattr(self, key, value) self.tries = 0 + self.start_time = time.time() self.domain = 'api.slack.com' self.request = request self.request_normalized = re.sub(r'\W+', '', request) @@ -659,23 +688,30 @@ class SlackRequest(object): self.post_data = post_data self.params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} self.url = 'https://{}/api/{}?{}'.format(self.domain, request, urllib.urlencode(post_data)) - self.response_id = sha.sha("{}{}".format(self.url, time.time())).hexdigest() + self.response_id = sha.sha("{}{}".format(self.url, self.start_time)).hexdigest() + self.retries = kwargs.get('retries', 3) # def __repr__(self): # return "URL: {} Tries: {} ID: {}".format(self.url, self.tries, self.response_id) def request_string(self): return "{}".format(self.url) def tried(self): self.tries += 1 + self.response_id = sha.sha("{}{}".format(self.url, time.time())).hexdigest() def should_try(self): - return self.tries < 3 + return self.tries < self.retries + def retry_ready(self): + return (self.start_time + (self.tries**2)) < time.time() class SlackTeam(object): """ incomplete Team object under which users and channels live.. Does lots. """ - def __init__(self, eventrouter, token, subdomain, nick, myidentifier, users, bots, channels, **kwargs): - self.ws = None + def __init__(self, eventrouter, token, websocket_url, subdomain, nick, myidentifier, users, bots, channels, **kwargs): + self.ws_url = websocket_url + self.connected = False + self.connecting = False + #self.ws = None self.ws_counter = 0 self.ws_replies = {} self.eventrouter = eventrouter @@ -685,14 +721,22 @@ class SlackTeam(object): self.domain = subdomain + ".slack.com" self.nick = nick self.myidentifier = myidentifier - self.channels = channels + try: + if self.channels: + for c in channels.keys(): + print 'got {} '.format(c) + if not self.channels.get(c): + print 'new {}'.format(c) + self.channels[c] = channels[c] + except: + self.channels = channels self.users = users self.bots = bots - self.team_hash = str(sha.sha("{}{}".format(self.nick, self.subdomain)).hexdigest()) + self.team_hash = SlackTeam.generate_team_hash(self.nick, self.subdomain) + #self.team_hash = str(sha.sha("{}{}".format(self.nick, self.subdomain)).hexdigest()) self.name = self.domain self.channel_buffer = None self.got_history = True - self.ws_reconnect_url = None self.create_buffer() self.muted_channels = {x for x in kwargs.get('muted_channels', []).split(',')} for c in self.channels.keys(): @@ -711,6 +755,11 @@ class SlackTeam(object): channel.set_related_server(self) # def connect_request_generate(self): # return SlackRequest(self.token, 'rtm.start', {}) + #def close_all_buffers(self): + # for channel in self.channels: + # self.eventrouter.weechat_controller.unregister_buffer(channel.channel_buffer, update_remote=False, close_buffer=True) + # #also close this server buffer + # self.eventrouter.weechat_controller.unregister_buffer(self.channel_buffer, update_remote=False, close_buffer=True) def create_buffer(self): if not self.channel_buffer: self.channel_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") @@ -728,12 +777,15 @@ class SlackTeam(object): return {v.slack_name: k for k, v in self.users.iteritems()} def get_team_hash(self): return self.team_hash + @staticmethod + def generate_team_hash(nick, subdomain): + return str(sha.sha("{}{}".format(nick, subdomain)).hexdigest()) def refresh(self): self.rename() def rename(self): pass - def attach_websocket(self, ws): - self.ws = ws + #def attach_websocket(self, ws): + # self.ws = ws def is_user_present(self, user_id): user = self.users.get(user_id) if user.presence == 'active': @@ -742,24 +794,38 @@ class SlackTeam(object): return False def mark_read(self): pass - def reconnect(self): - if not self.connected and self.ws_reconnect_url: - try: - ws = create_connection(self.ws_reconnect_url, sslopt=sslopt_ca_certs) - w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", self.get_team_hash()) - ws.sock.setblocking(0) - self.attach_websocket(ws) - self.set_connected() - except Exception as e: - dbg("websocket connection error: {}".format(e)) - self.set_reconnect_url(None) - return False + def connect(self): + if not self.connected and not self.connecting: + self.connecting = True + if self.ws_url: + try: + ws = create_connection(self.ws_url, sslopt=sslopt_ca_certs) + w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", self.get_team_hash()) + ws.sock.setblocking(0) + self.ws = ws + #self.attach_websocket(ws) + self.set_connected() + self.connecting = False + except Exception as e: + dbg("websocket connection error: {}".format(e)) + #self.set_reconnect_url(None) + return False + self.connecting = False + else: + #The fast reconnect failed, so start over-ish + for chan in self.channels: + self.channels[chan].got_history = False + s = SlackRequest(self.token, 'rtm.start', {}, retries=999) + self.eventrouter.receive(s) + self.connecting = False + #del self.eventrouter.teams[self.get_team_hash()] + self.set_reconnect_url(None) def set_connected(self): self.connected = True def set_disconnected(self): self.connected = False def set_reconnect_url(self, url): - self.ws_reconnect_url = url + self.ws_url = url def next_ws_transaction_id(self): if self.ws_counter > 999: self.ws_counter = 0 @@ -887,6 +953,8 @@ class SlackChannel(object): #else: # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + #else: + # self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) try: if self.unread_count != 0: for c in range(1, self.unread_count): @@ -968,6 +1036,9 @@ class SlackChannel(object): return w.buffer_get_integer(self.channel_buffer, "hidden") == 0 def get_history(self): if not self.got_history: + #we have probably reconnected. flush the buffer + if self.team.connected: + w.buffer_clear(self.channel_buffer) #if config.cache_messages: # for message in message_cache[self.identifier]: # process_message(json.loads(message), True) @@ -1441,54 +1512,65 @@ def handle_rtmstart(login_data, eventrouter): This handles the main entry call to slack, rtm.start """ if login_data["ok"]: + metadata = pickle.loads(login_data["wee_slack_request_metadata"]) - users = {} - for item in login_data["users"]: - users[item["id"]] = SlackUser(**item) - #users.append(SlackUser(**item)) + #Let's reuse a team if we have it already. + th = SlackTeam.generate_team_hash(login_data['self']['name'], login_data['team']['domain']) + if not eventrouter.teams.get(th): - bots = {} - for item in login_data["bots"]: - bots[item["id"]] = SlackBot(**item) + users = {} + for item in login_data["users"]: + users[item["id"]] = SlackUser(**item) + #users.append(SlackUser(**item)) - channels = {} - for item in login_data["channels"]: - channels[item["id"]] = SlackChannel(eventrouter, **item) + bots = {} + for item in login_data["bots"]: + bots[item["id"]] = SlackBot(**item) - for item in login_data["ims"]: - channels[item["id"]] = SlackDMChannel(eventrouter, users, **item) + channels = {} + for item in login_data["channels"]: + channels[item["id"]] = SlackChannel(eventrouter, **item) - for item in login_data["groups"]: - if item["name"].startswith('mpdm-'): - channels[item["id"]] = SlackMPDMChannel(eventrouter, **item) - else: - channels[item["id"]] = SlackGroupChannel(eventrouter, **item) - - t = SlackTeam( - eventrouter, - metadata.token, - login_data["team"]["domain"], - login_data["self"]["name"], - login_data["self"]["id"], - users, - bots, - channels, - muted_channels=login_data["self"]["prefs"]["muted_channels"], - ) - eventrouter.register_team(t) - - web_socket_url = login_data['url'] - try: - ws = create_connection(web_socket_url, sslopt=sslopt_ca_certs) - w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", t.get_team_hash()) - #ws_hook = w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", pickle.dumps(t)) - ws.sock.setblocking(0) - t.attach_websocket(ws) - t.set_connected() - except Exception as e: - dbg("websocket connection error: {}".format(e)) - return False + for item in login_data["ims"]: + channels[item["id"]] = SlackDMChannel(eventrouter, users, **item) + + for item in login_data["groups"]: + if item["name"].startswith('mpdm-'): + channels[item["id"]] = SlackMPDMChannel(eventrouter, **item) + else: + channels[item["id"]] = SlackGroupChannel(eventrouter, **item) + + t = SlackTeam( + eventrouter, + metadata.token, + login_data['url'], + login_data["team"]["domain"], + login_data["self"]["name"], + login_data["self"]["id"], + users, + bots, + channels, + muted_channels=login_data["self"]["prefs"]["muted_channels"], + ) + eventrouter.register_team(t) + + else: + t = eventrouter.teams.get(th) + t.set_reconnect_url(login_data['url']) + t.connect() + + #web_socket_url = login_data['url'] + #try: + # ws = create_connection(web_socket_url, sslopt=sslopt_ca_certs) + # w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", t.get_team_hash()) + # #ws_hook = w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", pickle.dumps(t)) + # ws.sock.setblocking(0) + # t.attach_websocket(ws) + # t.set_connected() + #except Exception as e: + # dbg("websocket connection error: {}".format(e)) + # return False t.buffer_prnt('Connected to Slack') t.buffer_prnt('{:<20} {}'.format(u"Websocket URL", login_data["url"])) -- cgit From c6fc6c1582542fe6dc72d08cca0019be216ed47e Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 16:31:54 -0800 Subject: remove debug - use fast reconnect again --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index fea2674..bdf699f 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -80,7 +80,7 @@ if hasattr(ssl, "get_default_verify_paths") and callable(ssl.get_default_verify_ IGNORED_EVENTS = [ "hello", "pref_change", - "reconnect_url", + #"reconnect_url", ] ###### New central Event router -- cgit From bc1848fb3ce09ec4f3be9ac768a749fc66681ade Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 16:35:27 -0800 Subject: make debug message --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index bdf699f..9402990 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -188,7 +188,7 @@ class EventRouter(object): for team_id, team in self.teams.iteritems(): if not team.connected: team.connect() - print team + dbg("reconnecting {}".format(team)) def receive_ws_callback(self, team_hash): """ -- cgit From 93ce428ce1c641fd6498391a340134256cc81454 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 16:40:25 -0800 Subject: before return --- wee_slack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 9402990..9923f9d 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -261,6 +261,7 @@ class EventRouter(object): self.receive(request_metadata) elif return_code != -1: self.reply_buffer.pop(request_metadata.response_id, None) + self.delete_context(data) else: if request_metadata.response_id not in self.reply_buffer: self.reply_buffer[request_metadata.response_id] = "" @@ -808,9 +809,8 @@ class SlackTeam(object): self.connecting = False except Exception as e: dbg("websocket connection error: {}".format(e)) - #self.set_reconnect_url(None) - return False self.connecting = False + return False else: #The fast reconnect failed, so start over-ish for chan in self.channels: -- cgit From f11840f4f9227671bd71404a43465e7e34cb04bb Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 19:22:16 -0800 Subject: restore --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 9923f9d..0959419 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2481,7 +2481,7 @@ if __name__ == "__main__": #slack_buffer = None #never_away = False - #hide_distractions = False + hide_distractions = False #hotlist = w.infolist_get("hotlist", "", "") #main_weechat_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, "DOESNOTEXIST!@#$")) -- cgit From d9ec818b440aafff1e8949592300d6b06a2333ba Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 20:32:30 -0800 Subject: fix debug --- wee_slack.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 0959419..1bdc244 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -145,6 +145,7 @@ class EventRouter(object): """ identifier = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(40)) self.context[identifier] = data + #dbg("stored context {} {} ".format(identifier, data.url)) return identifier def retrieve_context(self, identifier): @@ -155,6 +156,7 @@ class EventRouter(object): """ data = self.context.get(identifier, None) if data: + #dbg("retrieved eontext {} ".format(identifier)) return data def delete_context(self, identifier): @@ -162,6 +164,7 @@ class EventRouter(object): Requests can span multiple requests, so we may need to delete this as a last step """ if identifier in self.context: + #dbg("deleted eontext {} ".format(identifier)) del self.context[identifier] def shutdown(self): @@ -228,7 +231,11 @@ class EventRouter(object): where the request originated and route properly. """ request_metadata = self.retrieve_context(data) - dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out))) + try: + dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out))) + except: + dbg(request_metadata) + return if return_code == 0: if len(out) > 0: if request_metadata.response_id in self.reply_buffer: -- cgit From 03a9a9f8dfbbbe1cf75d9861efdb0e2887c340d7 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 20:39:30 -0800 Subject: recorder improvement --- wee_slack.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 1bdc244..504e829 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -5,6 +5,7 @@ import time import json import pickle import sha +import os import re import urllib import sys @@ -120,11 +121,10 @@ class EventRouter(object): """ self.recording = not self.recording if self.recording: - import os if not os.path.exists(RECORD_DIR): os.makedirs(RECORD_DIR) - def record_event(self, message_json, file_name_field): + def record_event(self, message_json, file_name_field, subdir=None): """ complete Called each time you want to record an event. @@ -132,8 +132,14 @@ class EventRouter(object): file_name_field is the json key whose value you want to be part of the file name """ now = time.time() + if subdir: + directory = "{}/{}".format(RECORD_DIR, subdir) + else: + directory = RECORD_DIR + if not os.path.exists(directory): + os.makedirs(directory) mtype = message_json.get(file_name_field, 'unknown') - f = open('{}/{}-{}.json'.format(RECORD_DIR, now, mtype), 'w') + f = open('{}/{}-{}.json'.format(directory, now, mtype), 'w') f.write("{}".format(json.dumps(message_json))) f.close() @@ -211,7 +217,7 @@ class EventRouter(object): }).jsonify() message_json["wee_slack_metadata"] = metadata if self.recording: - self.record_event(message_json, 'type') + self.record_event(message_json, 'type', 'websocket') self.receive_json(json.dumps(message_json)) except WebSocketConnectionClosedException: #TODO: handle reconnect here @@ -255,7 +261,7 @@ class EventRouter(object): j["wee_slack_request_metadata"] = pickle.dumps(request_metadata) self.reply_buffer.pop(request_metadata.response_id) if self.recording: - self.record_event(j, 'wee_slack_process_method') + self.record_event(j, 'wee_slack_process_method', 'http') self.receive_json(json.dumps(j)) self.delete_context(data) except: -- cgit From 5a06d9ec8e24478857f9dce5cab2f01095a253cd Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 20:46:13 -0800 Subject: outbound typing notification works --- wee_slack.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 504e829..10c0bb4 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -566,6 +566,21 @@ def buffer_list_update_callback(data, somecount): def quit_notification_callback(signal, sig_type, data): stop_talking_to_slack() +def typing_notification_cb(signal, sig_type, data): + msg = w.buffer_get_string(data, "input") + if len(msg) > 8 and msg[:1] != "/": + global typing_timer + now = time.time() + if typing_timer + 4 < now: + current_buffer = w.current_buffer() + channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + if channel: + identifier = channel.identifier + request = {"type": "typing", "channel": identifier} + channel.team.send_to_websocket(request, expect_reply=False) + typing_timer = now + return w.WEECHAT_RC_OK + def typing_update_cb(data, remaining_calls): w.bar_item_update("slack_typing_notice") return w.WEECHAT_RC_OK @@ -2282,6 +2297,7 @@ def setup_hooks(): w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") w.hook_signal('window_switch', "buffer_switch_callback", "EVENTROUTER") w.hook_signal('quit', "quit_notification_cb", "") + w.hook_signal('input_text_changed', "typing_notification_cb", "") w.hook_command( # Command name and description @@ -2316,7 +2332,6 @@ def setup_hooks(): #w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") #w.hook_signal('buffer_opened', "buffer_opened_cb", "") - #w.hook_signal('input_text_changed', "typing_notification_cb", "") #w.hook_signal('window_scrolled', "scrolled_cb", "") #w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") @@ -2488,7 +2503,7 @@ if __name__ == "__main__": config = PluginConfig() config_changed_cb = config.config_changed - #typing_timer = time.time() + typing_timer = time.time() #domain = None #previous_buffer = None #slack_buffer = None -- cgit From d120c46ba3b9b825c1ca131b5014933eece13074 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 21:03:36 -0800 Subject: nicklists --- wee_slack.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 10c0bb4..9952a6b 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -897,6 +897,7 @@ class SlackChannel(object): self.set_name(self.slack_name) #short name relates to the localvar we change for typing indication self.current_short_name = self.name + self.update_nicklist() def __eq__(self, compare_str): if compare_str == self.slack_name or compare_str == self.formatted_name() or compare_str == self.formatted_name(style="long_default"): return True @@ -995,7 +996,9 @@ class SlackChannel(object): #dbg("no unread in {}".format(self.name)) except: pass - #dbg("exception no unread count") + + self.update_nicklist() + #dbg("exception no unread count") #if self.unread_count != 0 and not self.muted: # w.buffer_set(self.channel_buffer, "hotlist", "1") def destroy_buffer(self, update_remote): @@ -1131,6 +1134,42 @@ class SlackChannel(object): s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": ts}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) self.new_messages = False + def update_nicklist(self, user=None): + if not self.channel_buffer and self.type in ("channel"): + return + w.buffer_set(self.channel_buffer, "nicklist", "1") + # create nicklists for the current channel if they don't exist + # if they do, use the existing pointer + here = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_HERE) + if not here: + here = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_HERE, "weechat.color.nicklist_group", 1) + afk = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_AWAY) + if not afk: + afk = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 1) + + if user: + user = self.team.users[user] + nick = w.nicklist_search_nick(self.channel_buffer, "", user.slack_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.. + w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) + + # if we didn't get a user, build a complete list. this is expensive. + else: + if len(self.members) < 1000: + try: + for user in self.members: + user = self.team.users[user] + #if user.deleted: + # continue + w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) + except Exception as e: + dbg("DEBUG: {} {} {}".format(self.identifier, self.name, e)) + else: + for fn in ("too", "many", "users"): + fn.nicklist_add_nick(self.channel_buffer, here, w, "", "", "", 1) + class SlackDMChannel(SlackChannel): """ -- cgit From ff347d311feb347954908e1c9b1078c0fbc16657 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 21:16:28 -0800 Subject: nicklist --- wee_slack.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 9952a6b..048a34b 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1135,17 +1135,21 @@ class SlackChannel(object): self.eventrouter.receive(s) self.new_messages = False def update_nicklist(self, user=None): - if not self.channel_buffer and self.type in ("channel"): + if not self.channel_buffer: + return + if self.type not in ["channel", "group"]: return + print self.type w.buffer_set(self.channel_buffer, "nicklist", "1") # create nicklists for the current channel if they don't exist # if they do, use the existing pointer - here = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_HERE) - if not here: - here = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_HERE, "weechat.color.nicklist_group", 1) - afk = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_AWAY) - if not afk: - afk = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 1) + #TODO: put this back for mithrandir + #here = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_HERE) + #if not here: + # here = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_HERE, "weechat.color.nicklist_group", 1) + #afk = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_AWAY) + #if not afk: + # afk = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 1) if user: user = self.team.users[user] @@ -1153,7 +1157,8 @@ class SlackChannel(object): # 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.. - w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) + w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, "", "", 1) + #w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) # if we didn't get a user, build a complete list. this is expensive. else: @@ -1163,12 +1168,13 @@ class SlackChannel(object): user = self.team.users[user] #if user.deleted: # continue - w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) + w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, "", "", 1) + #w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) except Exception as e: dbg("DEBUG: {} {} {}".format(self.identifier, self.name, e)) else: - for fn in ("too", "many", "users"): - fn.nicklist_add_nick(self.channel_buffer, here, w, "", "", "", 1) + for fn in ["too", "many", "users", "to", "show"]: + w.nicklist_add_nick(self.channel_buffer, afk, fn, w.color('white'), "", "", 1) class SlackDMChannel(SlackChannel): -- cgit From 7cfb6eed5b74a68a3f175b13f98b2048179dbdfc Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Sun, 5 Feb 2017 21:29:13 -0800 Subject: message edit/delete --- wee_slack.py | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 048a34b..672d960 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -502,17 +502,17 @@ def buffer_input_callback(signal, buffer_ptr, data): channel.send_add_reaction(int(reaction.group(1) or 1), reaction.group(3)) elif reaction.group(2) == "-": channel.send_remove_reaction(int(reaction.group(1) or 1), reaction.group(3)) -# elif data.startswith('s/'): -# try: -# old, new, flags = re.split(r'(? Date: Sun, 5 Feb 2017 21:40:50 -0800 Subject: remove debug --- wee_slack.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 672d960..239a69c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -753,9 +753,9 @@ class SlackTeam(object): try: if self.channels: for c in channels.keys(): - print 'got {} '.format(c) + #print 'got {} '.format(c) if not self.channels.get(c): - print 'new {}'.format(c) + #print 'new {}'.format(c) self.channels[c] = channels[c] except: self.channels = channels @@ -1065,7 +1065,6 @@ class SlackChannel(object): return True def edit_previous_message(self, old, new, flags): message = self.my_last_message() - print message if new == "" and old == "": s = SlackRequest(self.team.token, "chat.delete", {"channel": self.identifier, "ts": message['ts']}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) @@ -1077,8 +1076,6 @@ class SlackChannel(object): if new_message != message["text"]: s = SlackRequest(self.team.token, "chat.update", {"channel": self.identifier, "ts": message['ts'], "text": new_message.encode("utf-8")}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) - - async_slack_api_request(self.server.domain, self.server.token, 'chat.update', {"channel": self.identifier, "ts": message['ts'], "text": new_message.encode("utf-8")}) def my_last_message(self): for message in reversed(self.sorted_message_keys()): m = self.messages[message] @@ -1160,7 +1157,6 @@ class SlackChannel(object): return if self.type not in ["channel", "group"]: return - print self.type w.buffer_set(self.channel_buffer, "nicklist", "1") # create nicklists for the current channel if they don't exist # if they do, use the existing pointer @@ -2250,17 +2246,21 @@ def thread_command_callback(data, current_buffer, args): current = w.current_buffer() channel = EVENTROUTER.weechat_controller.buffers[current] args = args.split() - if len(args) < 2: - w.prnt(current, "Missing thread id argument") - return w.WEECHAT_RC_OK_EAT - else: + if len(args) == 2: pm = channel.messages[SlackTS(args[1])] tc = SlackThreadChannel(EVENTROUTER, pm) pm.thread_channel = tc tc.open() #tc.create_buffer() return w.WEECHAT_RC_OK_EAT - return w.WEECHAT_RC_OK + elif len(args) == 3: + if args[1] == "reply": + target = reversed(channel.sorted_message_keys()) + print target + print channel.messages[target][args[2]] + w.prnt(current, "Invalid thread command.") + return w.WEECHAT_RC_OK_EAT + #return w.WEECHAT_RC_OK def slack_command_cb(data, current_buffer, args): -- cgit From bf7cd668b265ace9b76f5881e54d7f77025c5409 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 00:13:21 -0800 Subject: add rehistory - fix deleted users --- wee_slack.py | 53 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 239a69c..8c3ea98 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1034,11 +1034,12 @@ class SlackChannel(object): modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) if backlog: self.mark_read(ts, update_remote=False, force=True) - def send_message(self, message): + def send_message(self, message, request_dict_ext={}): #team = self.eventrouter.teams[self.team] message = linkify_text(message, self.team, self) dbg(message) request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} + request.update(request_dict_ext) self.team.send_to_websocket(request) self.mark_read(force=True) def store_message(self, message, team, from_me=False): @@ -1183,15 +1184,15 @@ class SlackChannel(object): try: for user in self.members: user = self.team.users[user] - #if user.deleted: - # continue + if user.deleted: + continue w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, "", "", 1) #w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) except Exception as e: dbg("DEBUG: {} {} {}".format(self.identifier, self.name, e)) else: for fn in ["too", "many", "users", "to", "show"]: - w.nicklist_add_nick(self.channel_buffer, afk, fn, w.color('white'), "", "", 1) + w.nicklist_add_nick(self.channel_buffer, "", fn, w.color('white'), "", "", 1) class SlackDMChannel(SlackChannel): @@ -2244,24 +2245,34 @@ def command_showmuted(current_buffer, args): def thread_command_callback(data, current_buffer, args): current = w.current_buffer() - channel = EVENTROUTER.weechat_controller.buffers[current] - args = args.split() - if len(args) == 2: - pm = channel.messages[SlackTS(args[1])] - tc = SlackThreadChannel(EVENTROUTER, pm) - pm.thread_channel = tc - tc.open() - #tc.create_buffer() + channel = EVENTROUTER.weechat_controller.buffers.get(current) + if channel: + args = args.split() + if args[0] == '/thread': + if len(args) == 2: + pm = channel.messages[SlackTS(args[1])] + tc = SlackThreadChannel(EVENTROUTER, pm) + pm.thread_channel = tc + tc.open() + #tc.create_buffer() + return w.WEECHAT_RC_OK_EAT + elif args[0] == '/reply': + count = int(args[1]) + msg = " ".join(args[2:]) + mkeys = channel.sorted_message_keys() + mkeys.reverse() + parent_id = str(mkeys[count - 1]) + channel.send_message(msg, request_dict_ext={"thread_ts": parent_id}) + return w.WEECHAT_RC_OK_EAT + w.prnt(current, "Invalid thread command.") return w.WEECHAT_RC_OK_EAT - elif len(args) == 3: - if args[1] == "reply": - target = reversed(channel.sorted_message_keys()) - print target - print channel.messages[target][args[2]] - w.prnt(current, "Invalid thread command.") - return w.WEECHAT_RC_OK_EAT - #return w.WEECHAT_RC_OK +def rehistory_command_callback(data, current_buffer, args): + current = w.current_buffer() + channel = EVENTROUTER.weechat_controller.buffers.get(current) + channel.got_history = False + w.buffer_clear(channel.channel_buffer) + channel.get_history() def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) @@ -2387,6 +2398,8 @@ def setup_hooks(): w.hook_command_run('/leave', 'part_command_cb', '') w.hook_command_run('/topic', 'topic_command_cb', '') w.hook_command_run('/thread', 'thread_command_callback', '') + w.hook_command_run('/reply', 'thread_command_callback', '') + w.hook_command_run('/rehistory', 'rehistory_command_callback', '') w.hook_command_run('/msg', 'msg_command_cb', '') w.hook_command_run('/label', 'label_command_cb', '') w.hook_command_run("/input complete_next", "complete_next_cb", "") -- cgit From 573939bd73fb65cd2ca7c8392b7917037e7cb29e Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 00:35:14 -0800 Subject: mark server buffer --- wee_slack.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 8c3ea98..a320a2b 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -793,6 +793,7 @@ class SlackTeam(object): if not self.channel_buffer: self.channel_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) + w.buffer_set(self.channel_buffer, "localvar_set_type", 'server') if w.config_string(w.config_get('irc.look.channel_buffer')) == 'merge_with_core': w.buffer_merge(self.channel_buffer, w.buffer_search_main()) w.buffer_set(self.channel_buffer, "nicklist", "1") @@ -1023,6 +1024,8 @@ class SlackChannel(object): elif self.type in ["im", "mpdm"]: tags = tag("dm") self.new_messages = True + #elif nick in [x.strip() for x in w.prefix("join"), w.prefix("quit")]: + # tags = tag("joinleave") else: tags = tag("default") self.new_messages = True -- cgit From ccfc3ef979359def5a4d463ad4f5c8f0cea7e302 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 00:41:41 -0800 Subject: server buffer merge fix --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index a320a2b..f41a051 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -794,7 +794,7 @@ class SlackTeam(object): self.channel_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) w.buffer_set(self.channel_buffer, "localvar_set_type", 'server') - if w.config_string(w.config_get('irc.look.channel_buffer')) == 'merge_with_core': + if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': w.buffer_merge(self.channel_buffer, w.buffer_search_main()) w.buffer_set(self.channel_buffer, "nicklist", "1") def formatted_name(self, **kwargs): -- cgit From 982f100a1b4e02f97ebc909fdc68a53c3d4fb3b7 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 02:12:51 -0800 Subject: fix channel join/leave --- wee_slack.py | 191 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 113 insertions(+), 78 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index f41a051..529b469 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -80,7 +80,7 @@ if hasattr(ssl, "get_default_verify_paths") and callable(ssl.get_default_verify_ IGNORED_EVENTS = [ "hello", - "pref_change", + #"pref_change", #"reconnect_url", ] @@ -935,16 +935,16 @@ class SlackChannel(object): for key, value in message_json.items(): setattr(self, key, value) def open(self, update_remote=True): + if update_remote: + if "join" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) self.create_buffer() self.active = True self.get_history() if "info" in SLACK_API_TRANSLATOR[self.type]: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) - if update_remote: - if "join" in SLACK_API_TRANSLATOR[self.type]: - s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) - self.eventrouter.receive(s) #self.create_buffer() def check_should_open(self, force=False): try: @@ -1017,26 +1017,29 @@ class SlackChannel(object): ts = SlackTS(timestamp) if self.channel_buffer: #backlog messages - we will update the read marker as we print these - backlog = False - if ts <= SlackTS(self.last_read): - tags = tag("backlog") - backlog = True - elif self.type in ["im", "mpdm"]: - tags = tag("dm") - self.new_messages = True - #elif nick in [x.strip() for x in w.prefix("join"), w.prefix("quit")]: - # tags = tag("joinleave") - else: - tags = tag("default") - self.new_messages = True + try: + backlog = False + if ts <= SlackTS(self.last_read): + tags = tag("backlog") + backlog = True + elif self.type in ["im", "mpdm"]: + tags = tag("dm") + self.new_messages = True + elif nick in [x.strip() for x in w.prefix("join"), w.prefix("quit")]: + tags = tag("joinleave") + else: + tags = tag("default") + self.new_messages = True - if config.unhide_buffers_with_activity and not self.is_visible() and (self.identifier not in self.team.muted_channels): - w.buffer_set(self.channel_buffer, "hidden", "0") + if config.unhide_buffers_with_activity and not self.is_visible() and (self.identifier not in self.team.muted_channels): + w.buffer_set(self.channel_buffer, "hidden", "0") - w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) - modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) - if backlog: - self.mark_read(ts, update_remote=False, force=True) + w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) + modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) + if backlog: + self.mark_read(ts, update_remote=False, force=True) + except: + dbg("Problem processing buffer_prnt") def send_message(self, message, request_dict_ext={}): #team = self.eventrouter.teams[self.team] message = linkify_text(message, self.team, self) @@ -1156,6 +1159,17 @@ class SlackChannel(object): s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": ts}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) self.new_messages = False + def user_joined(self, user_id): + pass + #print type(self.members) + #print self.members + #print self + #self.members.append(user_id) + #self.update_nicklist(user_id) + def user_left(self, user_id): + pass + #filter(lambda u: u != user_id, self.members) + #self.update_nicklist(user_id) def update_nicklist(self, user=None): if not self.channel_buffer: return @@ -1178,7 +1192,8 @@ class SlackChannel(object): # 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.. - w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, "", "", 1) + if user.identifier in self.members: + w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, "", "", 1) #w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) # if we didn't get a user, build a complete list. this is expensive. @@ -1479,13 +1494,16 @@ class SlackMessage(object): These are modifiable and can be rerendered to change a message, delete a message, add a reaction, add a thread. """ - def __init__(self, message_json, team, channel): + def __init__(self, message_json, team, channel, override_sender=None): self.team = team self.channel = channel self.message_json = message_json self.submessages = [] self.thread_channel = None - self.sender = self.get_sender() + if override_sender: + self.sender = override_sender + else: + self.sender = self.get_sender() self.suffix = '' self.ts = SlackTS(message_json['ts']) def __hash__(self): @@ -1720,55 +1738,55 @@ def process_pong(message_json, eventrouter, **kwargs): def process_message(message_json, eventrouter, store=True, **kwargs): channel = kwargs["channel"] team = kwargs["team"] - try: - # send these subtype messages elsewhere - known_subtypes = [ - 'thread_message', - 'message_replied', - 'message_changed', - 'message_deleted', - #'channel_join', - #'channel_leave', - #'channel_topic', - #'group_join', - #'group_leave', - ] - if "thread_ts" in message_json and "reply_count" not in message_json: - message_json["subtype"] = "thread_message" - subtype = message_json.get("subtype", None) - if subtype and subtype in known_subtypes: - f = eval('subprocess_' + subtype) - f(message_json, eventrouter, channel, team) + #try: + # send these subtype messages elsewhere + known_subtypes = [ + 'thread_message', + 'message_replied', + 'message_changed', + 'message_deleted', + 'channel_join', + 'channel_leave', + #'channel_topic', + #'group_join', + #'group_leave', + ] + if "thread_ts" in message_json and "reply_count" not in message_json: + message_json["subtype"] = "thread_message" + subtype = message_json.get("subtype", None) + if subtype and subtype in known_subtypes: + f = eval('subprocess_' + subtype) + f(message_json, eventrouter, channel, team) - else: - message = SlackMessage(message_json, team, channel) - text = message.render() - #print text + else: + message = SlackMessage(message_json, team, channel) + text = message.render() + #print text + + # special case with actions. + if text.startswith("_") and text.endswith("_"): + text = text[1:-1] + if message.sender != channel.team.nick: + text = message.sender + " " + text + channel.unread_count += 1 + channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, **kwargs) - # special case with actions. - if text.startswith("_") and text.endswith("_"): - text = text[1:-1] - if message.sender != channel.team.nick: - text = message.sender + " " + text + else: + suffix = '' + if 'edited' in message_json: + suffix = ' (edited)' + try: channel.unread_count += 1 - channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, **kwargs) - - else: - suffix = '' - if 'edited' in message_json: - suffix = ' (edited)' - try: - channel.unread_count += 1 - except: - channel.unread_count = 1 - channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) + except: + channel.unread_count = 1 + channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) - if store: - channel.store_message(message, team) - dbg("NORMAL REPLY {}".format(message_json)) - except: - channel.buffer_prnt("WEE-SLACK-ERROR", json.dumps(message_json).encode('utf-8'), message_json["ts"], **kwargs) - traceback.print_exc() + if store: + channel.store_message(message, team) + dbg("NORMAL REPLY {}".format(message_json)) + #except: + # channel.buffer_prnt("WEE-SLACK-ERROR", json.dumps(message_json).encode('utf-8'), message_json["ts"], **kwargs) + # traceback.print_exc() def subprocess_thread_message(message_json, eventrouter, channel, team): #print ("THREADED: " + str(message_json)) @@ -1806,6 +1824,20 @@ def subprocess_thread_message(message_json, eventrouter, channel, team): #channel.change_message(message_json["thread_ts"], None, message_json["text"]) #channel.become_thread(message_json["item"]["ts"], message_json) +def subprocess_channel_join(message_json, eventrouter, channel, team): + joinprefix = w.prefix("join") + message = SlackMessage(message_json, team, channel, override_sender=joinprefix) + channel.buffer_prnt(joinprefix, message.render(), message_json["ts"]) + channel.user_joined(message_json['user']) + +def subprocess_channel_leave(message_json, eventrouter, channel, team): + leaveprefix = w.prefix("quit") + message = SlackMessage(message_json, team, channel, override_sender=leaveprefix) + channel.buffer_prnt(leaveprefix, message.render(), message_json["ts"]) + channel.user_left(message_json['user']) + #channel.update_nicklist(message_json['user']) + #channel.update_nicklist() + def subprocess_message_replied(message_json, eventrouter, channel, team): pass #print ("REPLIED: " + str(message_json)) @@ -2205,29 +2237,32 @@ def join_command_cb(data, current_buffer, args): else: return w.WEECHAT_RC_OK -def command_talk(current_buffer, args): +def command_talk(current_buffer, arg): """ incomplete because globals hack Open a chat with the specified user /slack talk [user] """ + print arg e = EVENTROUTER current = w.current_buffer() team = e.weechat_controller.buffers[current].team dbg(team) c = team.get_channel_map() - if args not in c: + if arg not in c: u = team.get_username_map() - if args in u: - s = SlackRequest(team.token, "im.open", {"user": u[args]}, team_hash=team.team_hash) + if arg in u: + s = SlackRequest(team.token, "im.open", {"user": u[arg]}, team_hash=team.team_hash) EVENTROUTER.receive(s) dbg("found user") #refresh channel map here c = team.get_channel_map() - if args in c: - dbg("found channel") - chan = team.channels[c[args]] + if arg.startswith('#'): + arg = arg[1:] + if arg in c: + print("found channel") + chan = team.channels[c[arg]] chan.open() if config.switch_buffer_on_join: w.buffer_set(chan.channel_buffer, "display", "1") -- cgit From 1f3cbb910b9b7e245af912c9d7b23523aa30318e Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 02:27:47 -0800 Subject: fix up join/leave --- wee_slack.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 529b469..67a8635 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -883,6 +883,7 @@ class SlackChannel(object): self.active = False for key, value in kwargs.items(): setattr(self, key, value) + self.members = set(kwargs.get('members', set())) self.eventrouter = eventrouter self.slack_name = kwargs["name"] self.identifier = kwargs["id"] @@ -1019,14 +1020,14 @@ class SlackChannel(object): #backlog messages - we will update the read marker as we print these try: backlog = False - if ts <= SlackTS(self.last_read): + if nick in [w.prefix("join"), w.prefix("quit")]: + tags = tag("joinleave") + elif ts <= SlackTS(self.last_read): tags = tag("backlog") backlog = True elif self.type in ["im", "mpdm"]: tags = tag("dm") self.new_messages = True - elif nick in [x.strip() for x in w.prefix("join"), w.prefix("quit")]: - tags = tag("joinleave") else: tags = tag("default") self.new_messages = True @@ -1160,16 +1161,15 @@ class SlackChannel(object): self.eventrouter.receive(s) self.new_messages = False def user_joined(self, user_id): - pass - #print type(self.members) - #print self.members - #print self - #self.members.append(user_id) - #self.update_nicklist(user_id) + #ugly hack - for some reason this gets turned into a list + self.members = set(self.members) + self.members.add(user_id) + self.update_nicklist(user_id) def user_left(self, user_id): - pass + #pass #filter(lambda u: u != user_id, self.members) - #self.update_nicklist(user_id) + self.members.discard(user_id) + self.update_nicklist(user_id) def update_nicklist(self, user=None): if not self.channel_buffer: return -- cgit From d575b847aab06c54f1f38b79da74ad24d9b97ea0 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 07:25:11 -0800 Subject: check for message before modifying --- wee_slack.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 67a8635..6815ed1 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1945,8 +1945,10 @@ def process_reaction_added(message_json, eventrouter, **kwargs): if message_json["item"].get("type") == "message": ts = SlackTS(message_json['item']["ts"]) - channel.messages[ts].add_reaction(message_json["reaction"], message_json["user"]) - channel.change_message(ts) + message = channel.messages.get(ts, None) + if message: + message.add_reaction(message_json["reaction"], message_json["user"]) + channel.change_message(ts) else: dbg("reaction to item type not supported: " + str(message_json)) @@ -1955,8 +1957,10 @@ def process_reaction_removed(message_json, eventrouter, **kwargs): if message_json["item"].get("type") == "message": ts = SlackTS(message_json['item']["ts"]) - channel.messages[ts].remove_reaction(message_json["reaction"], message_json["user"]) - channel.change_message(ts) + message = channel.messages.get(ts, None) + if message: + message.remove_reaction(message_json["reaction"], message_json["user"]) + channel.change_message(ts) else: dbg("Reaction to item type not supported: " + str(message_json)) -- cgit From 82f311a42d8a9e8a66b71cff2397c1b2f03f5bf1 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 08:06:35 -0800 Subject: fix huge channel messaging --- wee_slack.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 6815ed1..246ea7a 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1186,7 +1186,7 @@ class SlackChannel(object): #if not afk: # afk = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 1) - if user: + if user and len(self.members) < 1000: user = self.team.users[user] nick = w.nicklist_search_nick(self.channel_buffer, "", user.slack_name) # since this is a change just remove it regardless of where it is @@ -1209,8 +1209,8 @@ class SlackChannel(object): except Exception as e: dbg("DEBUG: {} {} {}".format(self.identifier, self.name, e)) else: - for fn in ["too", "many", "users", "to", "show"]: - w.nicklist_add_nick(self.channel_buffer, "", fn, w.color('white'), "", "", 1) + for fn in ["1| too", "2| many", "3| users", "4| to", "5| show"]: + w.nicklist_add_group(self.channel_buffer, '', fn, w.color('white'), 1) class SlackDMChannel(SlackChannel): -- cgit From 47c0e750b48d8bae74b38ae92a76a98eb37c1301 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 08:59:22 -0800 Subject: handle muting --- wee_slack.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 246ea7a..db696df 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -767,7 +767,7 @@ class SlackTeam(object): self.channel_buffer = None self.got_history = True self.create_buffer() - self.muted_channels = {x for x in kwargs.get('muted_channels', []).split(',')} + self.set_muted_channels(kwargs.get('muted_channels', "")) for c in self.channels.keys(): channels[c].set_related_server(self) channels[c].check_should_open() @@ -797,6 +797,8 @@ class SlackTeam(object): if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': w.buffer_merge(self.channel_buffer, w.buffer_search_main()) w.buffer_set(self.channel_buffer, "nicklist", "1") + def set_muted_channels(self, muted_str): + self.muted_channels = {x for x in muted_str.split(',')} def formatted_name(self, **kwargs): return self.domain def buffer_prnt(self, data): @@ -1725,6 +1727,13 @@ def process_manual_presence_change(message_json, eventrouter, **kwargs): def process_presence_change(message_json, eventrouter, **kwargs): kwargs["user"].presence = message_json["presence"] +def process_pref_change(message_json, eventrouter, **kwargs): + team = kwargs["team"] + if message_json['name'] == u'muted_channels': + team.set_muted_channels(message_json['value']) + else: + dbg("Preference change not implemented: {}\n".format(message_json['name'])) + def process_user_typing(message_json, eventrouter, **kwargs): channel = kwargs["channel"] team = kwargs["team"] @@ -2272,15 +2281,6 @@ def command_talk(current_buffer, arg): w.buffer_set(chan.channel_buffer, "display", "1") return True -def command_tmpmute(current_buffer, args): - current = w.current_buffer() - channel_id = EVENTROUTER.weechat_controller.buffers[current].identifier - team = EVENTROUTER.weechat_controller.buffers[current].team - if channel_id not in team.muted_channels: - team.muted_channels.add(channel_id) - else: - team.muted_channels.discard(channel_id) - def command_showmuted(current_buffer, args): current = w.current_buffer() w.prnt(EVENTROUTER.weechat_controller.buffers[current].team.channel_buffer, str(EVENTROUTER.weechat_controller.buffers[current].team.muted_channels)) @@ -2342,6 +2342,18 @@ def command_distracting(current_buffer, args): def save_distracting_channels(): w.config_set_plugin('distracting_channels', ','.join(config.distracting_channels)) +def command_mute(current_buffer, args): + current = w.current_buffer() + channel_id = EVENTROUTER.weechat_controller.buffers[current].identifier + team = EVENTROUTER.weechat_controller.buffers[current].team + if channel_id not in team.muted_channels: + team.muted_channels.add(channel_id) + else: + team.muted_channels.discard(channel_id) + s = SlackRequest(team.token, "users.prefs.set", {"name": "muted_channels", "value": ",".join(team.muted_channels)}, team_hash=team.team_hash, channel_identifier=channel_id) + EVENTROUTER.receive(s) + + def command_nodistractions(current_buffer, args): global hide_distractions hide_distractions = not hide_distractions -- cgit From 838f26a3fd82e9f9e5cf801cb46b71c1cce382c1 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 09:12:21 -0800 Subject: update hotlist with new message when opening channel --- wee_slack.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index db696df..e883248 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1941,8 +1941,10 @@ def process_channel_created(message_json, eventrouter, **kwargs): #raise def process_im_open(message_json, eventrouter, **kwargs): + channel = kwargs['channel'] item = message_json kwargs['team'].channels[item["channel"]].check_should_open(True) + w.buffer_set(channel.channel_buffer, "hotlist", "2") def process_im_close(message_json, eventrouter, **kwargs): item = message_json -- cgit From 6bdc0d9cdddc6a84716550395699da61e04bd9ff Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 10:01:40 -0800 Subject: remove debug --- wee_slack.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index e883248..2572de5 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -151,7 +151,7 @@ class EventRouter(object): """ identifier = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(40)) self.context[identifier] = data - #dbg("stored context {} {} ".format(identifier, data.url)) + dbg("stored context {} {} ".format(identifier, data.url)) return identifier def retrieve_context(self, identifier): @@ -2258,7 +2258,6 @@ def command_talk(current_buffer, arg): Open a chat with the specified user /slack talk [user] """ - print arg e = EVENTROUTER current = w.current_buffer() team = e.weechat_controller.buffers[current].team @@ -2276,7 +2275,6 @@ def command_talk(current_buffer, arg): if arg.startswith('#'): arg = arg[1:] if arg in c: - print("found channel") chan = team.channels[c[arg]] chan.open() if config.switch_buffer_on_join: @@ -2318,6 +2316,10 @@ def rehistory_command_callback(data, current_buffer, args): w.buffer_clear(channel.channel_buffer) channel.get_history() +def hide_command_callback(data, current_buffer, args): + current = w.current_buffer() + w.buffer_set(current, "hidden", str(int(hide_distractions))) + def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) if len(a) > 1: @@ -2456,6 +2458,7 @@ def setup_hooks(): w.hook_command_run('/thread', 'thread_command_callback', '') w.hook_command_run('/reply', 'thread_command_callback', '') w.hook_command_run('/rehistory', 'rehistory_command_callback', '') + w.hook_command_run('/hide', 'hide_command_callback', '') w.hook_command_run('/msg', 'msg_command_cb', '') w.hook_command_run('/label', 'label_command_cb', '') w.hook_command_run("/input complete_next", "complete_next_cb", "") -- cgit From 8c4b8baeba8db45e98550fb2f92fe13322f4562c Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 11:44:06 -0800 Subject: highlights server side --- wee_slack.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 2572de5..beaeec5 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -753,9 +753,7 @@ class SlackTeam(object): try: if self.channels: for c in channels.keys(): - #print 'got {} '.format(c) if not self.channels.get(c): - #print 'new {}'.format(c) self.channels[c] = channels[c] except: self.channels = channels @@ -774,6 +772,8 @@ class SlackTeam(object): # self.channel_set_related_server(c) # Last step is to make sure my nickname is the set color self.users[self.myidentifier].force_color(w.config_string(w.config_get('weechat.color.chat_nick_self'))) + # This highlight step must happen after we have set related server + self.set_highlight_words(kwargs.get('highlight_words', "")) def __eq__(self, compare_str): if compare_str == self.token or compare_str == self.domain or compare_str == self.subdomain: return True @@ -799,6 +799,11 @@ class SlackTeam(object): w.buffer_set(self.channel_buffer, "nicklist", "1") def set_muted_channels(self, muted_str): self.muted_channels = {x for x in muted_str.split(',')} + def set_highlight_words(self, highlight_str): + self.highlight_words = {x for x in highlight_str.split(',')} + if len(self.highlight_words) > 0: + for v in self.channels.itervalues(): + v.set_highlights() def formatted_name(self, **kwargs): return self.domain def buffer_prnt(self, data): @@ -966,6 +971,12 @@ class SlackChannel(object): pass def set_related_server(self, team): self.team = team + def set_highlights(self): + #highlight my own name and any set highlights + if self.channel_buffer: + highlights = self.team.highlight_words.union({'@' + self.team.nick}) + h_str = ",".join(highlights) + w.buffer_set(self.channel_buffer, "highlight_words", h_str) def create_buffer(self): """ incomplete (muted doesn't work) @@ -1018,6 +1029,9 @@ class SlackChannel(object): def buffer_prnt(self, nick, text, timestamp, **kwargs): data = "{}\t{}".format(nick, text) ts = SlackTS(timestamp) + #without this, DMs won't open automatically + if not self.channel_buffer: + self.open(update_remote=False) if self.channel_buffer: #backlog messages - we will update the read marker as we print these try: @@ -1666,6 +1680,7 @@ def handle_rtmstart(login_data, eventrouter): bots, channels, muted_channels=login_data["self"]["prefs"]["muted_channels"], + highlight_words=login_data["self"]["prefs"]["highlight_words"], ) eventrouter.register_team(t) @@ -1731,6 +1746,8 @@ def process_pref_change(message_json, eventrouter, **kwargs): team = kwargs["team"] if message_json['name'] == u'muted_channels': team.set_muted_channels(message_json['value']) + elif message_json['name'] == u'highlight_words': + team.set_highlight_words(message_json['value']) else: dbg("Preference change not implemented: {}\n".format(message_json['name'])) -- cgit From 99822b22b4e1cb82782100564b6504c6a240dd05 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 11:55:48 -0800 Subject: highlight slack keywords --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index beaeec5..daaa74d 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -974,7 +974,7 @@ class SlackChannel(object): def set_highlights(self): #highlight my own name and any set highlights if self.channel_buffer: - highlights = self.team.highlight_words.union({'@' + self.team.nick}) + highlights = self.team.highlight_words.union({'@' + self.team.nick, "!here", "!channel", "!everyone"}) h_str = ",".join(highlights) w.buffer_set(self.channel_buffer, "highlight_words", h_str) def create_buffer(self): -- cgit From 207b927eb97a089e4979c805583645b2ca78df80 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 12:26:10 -0800 Subject: option to use server buffers w/o full domain name --- wee_slack.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index daaa74d..fd09b49 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -791,7 +791,8 @@ class SlackTeam(object): # self.eventrouter.weechat_controller.unregister_buffer(self.channel_buffer, update_remote=False, close_buffer=True) def create_buffer(self): if not self.channel_buffer: - self.channel_buffer = w.buffer_new("{}".format(self.domain), "buffer_input_callback", "EVENTROUTER", "", "") + bname = self.subdomain if config.short_buffer_names else self.domain + self.channel_buffer = w.buffer_new("{}".format(bname), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) w.buffer_set(self.channel_buffer, "localvar_set_type", 'server') if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': @@ -2539,6 +2540,7 @@ class PluginConfig(object): 'record_events': 'false', 'thread_suffix_color': 'lightcyan', 'unhide_buffers_with_activity': 'false', + 'short_buffer_names': 'false', } # Set missing settings to their defaults. Load non-missing settings from -- cgit From 52a7c943e3a034959e0c196e188dadaad2f45dca Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 12:49:04 -0800 Subject: simplify tags and notify when getting history --- wee_slack.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index fd09b49..c2ac5a6 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1027,7 +1027,7 @@ class SlackChannel(object): if update_remote and not self.eventrouter.shutting_down: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) - def buffer_prnt(self, nick, text, timestamp, **kwargs): + def buffer_prnt(self, nick, text, timestamp=str(time.time()), tagset=None, **kwargs): data = "{}\t{}".format(nick, text) ts = SlackTS(timestamp) #without this, DMs won't open automatically @@ -1035,20 +1035,21 @@ class SlackChannel(object): self.open(update_remote=False) if self.channel_buffer: #backlog messages - we will update the read marker as we print these - try: - backlog = False - if nick in [w.prefix("join"), w.prefix("quit")]: - tags = tag("joinleave") - elif ts <= SlackTS(self.last_read): - tags = tag("backlog") - backlog = True - elif self.type in ["im", "mpdm"]: - tags = tag("dm") - self.new_messages = True - else: - tags = tag("default") - self.new_messages = True + backlog = True if ts <= SlackTS(self.last_read) else False + if tagset: + tags = tag(tagset) + + #we have to infer the tagset because we weren't told + elif ts <= SlackTS(self.last_read): + tags = tag("backlog") + elif self.type in ["im", "mpdm"]: + tags = tag("dm") + self.new_messages = True + else: + tags = tag("default") + self.new_messages = True + try: if config.unhide_buffers_with_activity and not self.is_visible() and (self.identifier not in self.team.muted_channels): w.buffer_set(self.channel_buffer, "hidden", "0") @@ -1113,12 +1114,9 @@ class SlackChannel(object): #we have probably reconnected. flush the buffer if self.team.connected: w.buffer_clear(self.channel_buffer) - #if config.cache_messages: - # for message in message_cache[self.identifier]: - # process_message(json.loads(message), True) + self.buffer_prnt('', 'getting channel history...', tagset='backlog') s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) - #async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}) self.got_history = True def send_add_reaction(self, msg_number, reaction): self.send_change_reaction("reactions.add", msg_number, reaction) @@ -1854,13 +1852,13 @@ def subprocess_thread_message(message_json, eventrouter, channel, team): def subprocess_channel_join(message_json, eventrouter, channel, team): joinprefix = w.prefix("join") message = SlackMessage(message_json, team, channel, override_sender=joinprefix) - channel.buffer_prnt(joinprefix, message.render(), message_json["ts"]) + channel.buffer_prnt(joinprefix, message.render(), message_json["ts"], tagset='joinleave') channel.user_joined(message_json['user']) def subprocess_channel_leave(message_json, eventrouter, channel, team): leaveprefix = w.prefix("quit") message = SlackMessage(message_json, team, channel, override_sender=leaveprefix) - channel.buffer_prnt(leaveprefix, message.render(), message_json["ts"]) + channel.buffer_prnt(leaveprefix, message.render(), message_json["ts"], tagset='joinleave') channel.user_left(message_json['user']) #channel.update_nicklist(message_json['user']) #channel.update_nicklist() -- cgit From 9e666945fc81f4220efffe79b3202ae39acd60e5 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 12:54:28 -0800 Subject: print a message when getting history --- wee_slack.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index c2ac5a6..b7f4f20 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1115,7 +1115,7 @@ class SlackChannel(object): if self.team.connected: w.buffer_clear(self.channel_buffer) self.buffer_prnt('', 'getting channel history...', tagset='backlog') - s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier, clear=True) self.eventrouter.receive(s) self.got_history = True def send_add_reaction(self, msg_number, reaction): @@ -1725,7 +1725,14 @@ 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] kwargs['channel'] = kwargs['team'].channels[request_metadata.channel_identifier] + try: + clear = request_metadata.clear + except: + clear = False + print clear kwargs['output_type'] = "backlog" + if clear: + w.buffer_clear(kwargs['channel'].channel_buffer) for message in reversed(message_json["messages"]): process_message(message, eventrouter, **kwargs) -- cgit From 2f653c80b355af63990c390313274f469bb2f3c1 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 13:12:35 -0800 Subject: debug levels --- wee_slack.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index b7f4f20..9e98456 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +#-*- coding: utf-8 -*- # import time @@ -1729,7 +1729,7 @@ def handle_history(message_json, eventrouter, **kwargs): clear = request_metadata.clear except: clear = False - print clear + dbg(clear, 3) kwargs['output_type'] = "backlog" if clear: w.buffer_clear(kwargs['channel'].channel_buffer) @@ -2500,24 +2500,25 @@ def setup_hooks(): ##### END NEW -def dbg(message, main_buffer=False, fout=False): +def dbg(message, level=0, main_buffer=False, fout=False): """ send debug output to the slack-debug buffer and optionally write to a file. """ #TODO: do this smarter #return - global debug_string - message = "DEBUG: {}".format(message) - # message = message.encode('utf-8', 'replace') - if fout: - file('/tmp/debug.log', 'a+').writelines(message + '\n') - if main_buffer: - #w.prnt("", "---------") - w.prnt("", "slack: " + message) - else: - if slack_debug and (not debug_string or debug_string in message): - #w.prnt(slack_debug, "---------") - w.prnt(slack_debug, message) + if level >= config.debug_level: + global debug_string + message = "DEBUG: {}".format(message) + # message = message.encode('utf-8', 'replace') + if fout: + file('/tmp/debug.log', 'a+').writelines(message + '\n') + if main_buffer: + #w.prnt("", "---------") + w.prnt("", "slack: " + message) + else: + if slack_debug and (not debug_string or debug_string in message): + #w.prnt(slack_debug, "---------") + w.prnt(slack_debug, message) ###### Config code @@ -2534,6 +2535,7 @@ class PluginConfig(object): 'colorize_nicks': 'true', 'colorize_private_chats': 'false', 'debug_mode': 'false', + 'debug_level': '3', 'distracting_channels': '', 'show_reaction_nicks': 'false', 'slack_api_token': 'INSERT VALID KEY HERE!', @@ -2546,6 +2548,7 @@ class PluginConfig(object): 'thread_suffix_color': 'lightcyan', 'unhide_buffers_with_activity': 'false', 'short_buffer_names': 'false', + 'background_load_all_history': 'false', } # Set missing settings to their defaults. Load non-missing settings from @@ -2596,6 +2599,9 @@ class PluginConfig(object): def get_thread_suffix_color(self, key): return w.config_get_plugin("thread_suffix_color") + def get_debug_level(self, key): + return int(w.config_get_plugin(key)) + def get_slack_timeout(self, key): return int(w.config_get_plugin(key)) -- cgit From 6a41a6ef571d353e3473d96763557872b5e0cbe8 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 13:12:49 -0800 Subject: this isn't level 3 --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 9e98456..a6aca4d 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1729,7 +1729,7 @@ def handle_history(message_json, eventrouter, **kwargs): clear = request_metadata.clear except: clear = False - dbg(clear, 3) + dbg(clear) kwargs['output_type'] = "backlog" if clear: w.buffer_clear(kwargs['channel'].channel_buffer) -- cgit From 91fcccd081f1f64528aedbec64afe54cc4f863fa Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 13:31:08 -0800 Subject: lazy load in bg --- wee_slack.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index a6aca4d..9938d86 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -298,6 +298,15 @@ class EventRouter(object): """ dbg("RECEIVED FROM QUEUE") self.queue.append(dataobj) + def receive_slow(self, dataobj): + """ + complete + Receives a raw object and places it on the slow queue for + processing. Object must be known to handle_next or + be JSON. + """ + dbg("RECEIVED FROM QUEUE") + self.slow_queue.append(dataobj) def handle_next(self): """ complete @@ -305,10 +314,11 @@ class EventRouter(object): via callback to drain events from the queue. It also attaches useful metadata and context to events as they are processed. """ - if len(self.slow_queue) > 0 and ((self.slow_queue_timer + 1) < time.time()): - for q in self.slow_queue[:]: - self.queue.append(q) - self.slow_queue = [] + if len(self.slow_queue) > 0 and ((self.slow_queue_timer + 5) < time.time()): + #for q in self.slow_queue[0]: + dbg("from slow queue", 0) + self.queue.append(self.slow_queue.pop()) + #self.slow_queue = [] self.slow_queue_timer = time.time() if len(self.queue) > 0: j = self.queue.pop(0) @@ -968,6 +978,8 @@ class SlackChannel(object): try: if eval("self." + reason): self.create_buffer() + if config.background_load_all_history: + self.get_history(slow_queue=True) except: pass def set_related_server(self, team): @@ -1109,14 +1121,17 @@ class SlackChannel(object): return m.message_json def is_visible(self): return w.buffer_get_integer(self.channel_buffer, "hidden") == 0 - def get_history(self): + def get_history(self, slow_queue=False): if not self.got_history: #we have probably reconnected. flush the buffer if self.team.connected: w.buffer_clear(self.channel_buffer) self.buffer_prnt('', 'getting channel history...', tagset='backlog') s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier, clear=True) - self.eventrouter.receive(s) + if not slow_queue: + self.eventrouter.receive(s) + else: + self.eventrouter.receive_slow(s) self.got_history = True def send_add_reaction(self, msg_number, reaction): self.send_change_reaction("reactions.add", msg_number, reaction) @@ -1793,7 +1808,7 @@ def process_message(message_json, eventrouter, store=True, **kwargs): else: message = SlackMessage(message_json, team, channel) text = message.render() - #print text + dbg(text) # special case with actions. if text.startswith("_") and text.endswith("_"): -- cgit From 92688afb3b3d5213dc7b4413867bbdee2b96fd43 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 14:31:15 -0800 Subject: add weemoji --- wee_slack.py | 32 ++++++++++++++++++++++++++++++++ weemoji.json | 1 + 2 files changed, 33 insertions(+) create mode 100644 weemoji.json diff --git a/wee_slack.py b/wee_slack.py index 9938d86..7ad39bd 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -644,6 +644,22 @@ def nick_completion_cb(data, completion_item, current_buffer, completion): w.hook_completion_list_add(completion, "@" + u.slack_name, 1, w.WEECHAT_LIST_POS_SORT) return w.WEECHAT_RC_OK +def emoji_completion_cb(data, completion_item, current_buffer, completion): + """ + Adds all :-prefixed emoji to completion list + """ + + current_buffer = w.current_buffer() + current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + + if current_channel is None: + return w.WEECHAT_RC_OK + + for e in EMOJI['emoji']: + w.hook_completion_list_add(completion, ":" + e + ":", 0, w.WEECHAT_LIST_POS_SORT) + + return w.WEECHAT_RC_OK + def complete_next_cb(data, current_buffer, command): """Extract current word, if it is equal to a nick, prefix it with @ and @@ -2457,6 +2473,20 @@ def create_slack_debug_buffer(): slack_debug = w.buffer_new("slack-debug", "", "", "closed_slack_debug_buffer_cb", "") w.buffer_set(slack_debug, "notify", "0") +def load_emoji(): + try: + global EMOJI + DIR = w.info_get("weechat_dir", "") + #no idea why this does't work w/o checking the type?! + dbg(type(DIR), 0) + ef = open('{}/weemoji.json'.format(DIR), 'r') + EMOJI = json.loads(ef.read()) + ef.close() + except: + dbg("Unexpected error: {}".format(sys.exc_info()), 5) + return w.WEECHAT_RC_OK + + def setup_hooks(): cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} @@ -2503,6 +2533,7 @@ def setup_hooks(): w.hook_command_run('/away', 'away_command_cb', '') w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") + w.hook_completion("emoji", "complete :emoji: for slack", "emoji_completion_cb", "") # Hooks to fix/implement #w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") @@ -2702,6 +2733,7 @@ if __name__ == "__main__": w.hook_config("plugins.var.python." + SCRIPT_NAME + ".*", "config_changed_cb", "") + load_emoji() setup_hooks() # attach to the weechat hooks we need diff --git a/weemoji.json b/weemoji.json new file mode 100644 index 0000000..eb19849 --- /dev/null +++ b/weemoji.json @@ -0,0 +1 @@ +{"emoji": ["wine_glass", "flag-tl", "flag-tn", "clock830", "flag-th", "rabbit", "flag-tj", "european_post_office", "flag-nr", "tram", "wink", "flag-tg", "department_store", "flag-ta", "slightly_smiling_face", "alien", "crocodile", "flag-au", "flag-tz", "bulb", "heavy_heart_exclamation_mark_ornament", "bomb", "flag-tv", "golfer", "hole", "flag-tr", "maple_leaf", "building_construction", "face_with_rolling_eyes", "man_with_gua_pi_mao", "person_with_ball", "e-mail", "tv", "open_hands", "sweat_drops", "pager", "file_cabinet", "laughing", "part_alternation_mark", "flag-td", "tm", "mountain_cableway", "melon", "smile", "snow_cloud", "large_blue_circle", "persevere", "sound", "fax", "woman", "eight_pointed_black_star", "thought_balloon", "end", "oncoming_automobile", "wave", "u7a7a", "woman-woman-boy-boy", "flag-cd", "hammer_and_wrench", "ticket", "flag-tc", "ramen", "twisted_rightwards_arrows", "cool", "four", "school", "small_airplane", "high_brightness", "nerd_face", "upside_down_face", "deciduous_tree", "notes", "white_flower", "biohazard_sign", "gun", "video_game", "saxophone", "car", "flag-ic", "notebook_with_decorative_cover", "triumph", "flag-io", "flag-in", "flag-im", "slightly_frowning_face", "black_right_pointing_double_triangle_with_vertical_bar", "tea", "flag-ls", "flag-bn", "dove_of_peace", "flag-ie", "arrow_left", "old_key", "flag-tt", "zero", "small_orange_diamond", "a", "white_square_button", "flag-is", "hankey", "flag-iq", "cactus", "spaghetti", "white_small_square", "ribbon", "flag-it", "toilet", "mega", "abc", "hocho", "flag-sr", "knife_fork_plate", "flag-fo", "purple_heart", "love_letter", "flag-fk", "file_folder", "flag-fi", "clipboard", "baby_bottle", "new", "bird", "flag-ua", "1234", "peace_symbol", "spock-hand", "couch_and_lamp", "no_smoking", "no_bicycles", "herb", "pouting_cat", "vertical_traffic_light", "leo", "house_with_garden", "flag-pm", "baseball", "busstop", "new_moon", "kissing", "man-woman-boy-boy", "100", "flag-na", "boy", "flag-sl", "capital_abcd", "no_entry", "wheel_of_dharma", "metro", "leaves", "heavy_plus_sign", "roller_coaster", "game_die", "man-man-girl-girl", "classical_building", "hamster", "flag-gy", "pick", "popcorn", "cold_sweat", "massage", "fleur_de_lis", "flag-pr", "chains", "flag-pt", "apple", "family", "scales", "sleeping_accommodation", "rice_cracker", "wind_blowing_face", "inbox_tray", "flag-ma", "flag-pa", "green_heart", "mahjong", "flag-pf", "flag-pg", "flag-ph", "flag-ec", "sleuth_or_spy", "clock330", "flag-ca", "dango", "honey_pot", "eye", "keycap_star", "baby", "sake", "confounded", "hospital", "poodle", "frog", "musical_note", "camera", "sleeping", "crescent_moon", "world_map", "aries", "flag-nl", "ear_of_rice", "flag-si", "video_camera", "mouse2", "chestnut", "flag-mg", "guardsman", "clock230", "baby_symbol", "atom_symbol", "steam_locomotive", "man_in_business_suit_levitating", "motor_boat", "tangerine", "blue_heart", "mantelpiece_clock", "recycle", "train", "beers", "water_buffalo", "flag-cz", "first_quarter_moon_with_face", "mailbox_closed", "curly_loop", "lower_left_fountain_pen", "pouch", "flag-ba", "jack_o_lantern", "izakaya_lantern", "palm_tree", "derelict_house_building", "tired_face", "cat", "dizzy", "nine", "chocolate_bar", "v", "running_shirt_with_sash", "ferry", "arrow_lower_left", "put_litter_in_its_place", "coffin", "abcd", "heart", "chart_with_upwards_trend", "arrow_backward", "hamburger", "pushpin", "lock", "flag-eu", "dolphin", "flag-es", "confused", "accept", "night_with_stars", "studio_microphone", "pig2", "white_medium_small_square", "flag-eh", "flag-eg", "sunglasses", "airplane", "trumpet", "flag-ee", "bow", "flag-bj", "clock12", "earth_americas", "see_no_evil", "scorpius", "flag-bo", "flag-bl", "flag-bm", "flag-bb", "mouse", "speedboat", "six", "snowman_without_snow", "ledger", "flag-bd", "flag-be", "flag-bz", "small_blue_diamond", "leftwards_arrow_with_hook", "amphora", "rewind", "flag-br", "no_bell", "flag-mc", "earth_asia", "flag-bv", "goat", "flag-bt", "pizza", "heavy_check_mark", "trident", "briefcase", "cocktail", "kissing_closed_eyes", "sunny", "star_of_david", "flag-bh", "customs", "motorway", "fork_and_knife", "birthday", "fast_forward", "heartpulse", "mag", "taco", "sparkler", "sparkles", "flag-va", "shirt", "tomato", "womens", "octopus", "wheelchair", "volleyball", "dragon", "mostly_sunny", "tulip", "flag-cu", "truck", "wrench", "flag-je", "ambulance", "sa", "point_up_2", "egg", "small_red_triangle", "umbrella_with_rain_drops", "flag-gp", "shield", "office", "mute", "clapper", "flag-bf", "funeral_urn", "haircut", "soon", "flag-bg", "symbols", "black_square_button", "flag-jp", "keyboard", "japan", "post_office", "last_quarter_moon_with_face", "flag-sb", "rosette", "pray", "linked_paperclips", "flushed", "flag-sa", "dark_sunglasses", "dizzy_face", "rugby_football", "currency_exchange", "flag-by", "paperclip", "moneybag", "mailbox_with_no_mail", "man-woman-girl-girl", "sob", "soccer", "dolls", "flag-gr", "coffee", "tiger2", "flag-la", "flag-lb", "neutral_face", "black_right_pointing_triangle_with_double_vertical_bar", "monorail", "elephant", "flag-li", "open_mouth", "bar_chart", "flag-lt", "european_castle", "flag-lv", "page_with_curl", "woman-heart-woman", "snake", "kiss", "blue_car", "confetti_ball", "flag-ly", "bank", "bread", "minidisc", "flag-mt", "flag-bq", "rice_ball", "oncoming_police_car", "capricorn", "point_left", "flag-gw", "tokyo_tower", "barely_sunny", "weary", "flag-bw", "clock930", "fishing_pole_and_fish", "repeat_one", "bowling", "volcano", "older_woman", "railway_car", "smiley_cat", "flag-er", "information_source", "cry", "telescope", "beginner", "earth_africa", "postal_horn", "house", "fish", "construction_worker", "money_mouth_face", "spider", "u7121", "bride_with_veil", "camera_with_flash", "books", "keycap_ten", "fist", "beetle", "lock_with_ink_pen", "8ball", "worried", "weight_lifter", "sunrise", "exclamation", "no_good", "flag-zm", "lipstick", "lower_left_crayon", "flag-ps", "smirk", "racing_car", "card_file_box", "factory", "baggage_claim", "cherry_blossom", "om_symbol", "sparkle", "fountain", "point_right", "cyclone", "-1", "blue_book", "reminder_ribbon", "dancers", "sheep", "flower_playing_cards", "umbrella", "flag-np", "hatching_chick", "black_circle_for_record", "flag-vi", "free", "traffic_light", "five", "grimacing", "cookie", "poultry_leg", "grapes", "raised_hand_with_fingers_splayed", "smirk_cat", "flag-ws", "diamond_shape_with_a_dot_inside", "lollipop", "flag-id", "man-heart-man", "high_heel", "dagger_knife", "black_medium_small_square", "green_book", "flag-kw", "headphones", "no_mobile_phones", "sun_with_face", "mailbox", "mosque", "passport_control", "bookmark", "+1", "notebook", "yum", "closed_lock_with_key", "heartbeat", "man-woman-girl", "blush", "radioactive_sign", "bullettrain_front", "flag-mh", "ophiuchus", "flag-mp", "bouquet", "sports_medal", "flag-uy", "fire_engine", "one", "feet", "date", "flag-vu", "cow2", "scissors", "ring", "disappointed_relieved", "whale", "zap", "children_crossing", "national_park", "clock430", "horse", "basketball", "monkey", "thinking_face", "blossom", "gift_heart", "top", "flag-il", "spider_web", "clock630", "crossed_swords", "station", "clock730", "man", "banana", "flag-mv", "shaved_ice", "eyes", "shell", "waving_white_flag", "gear", "flag-hn", "radio_button", "memo", "hotel", "small_red_triangle_down", "broken_heart", "suspension_railway", "railway_track", "nut_and_bolt", "aerial_tramway", "flag-hr", "seat", "latin_cross", "flag-hu", "panda_face", "middle_finger", "minibus", "b", "unamused", "flag-af", "flag-ae", "flag-ad", "evergreen_tree", "flag-ao", "mailbox_with_mail", "bee", "scream_cat", "smile_cat", "flag-aq", "flag-ve", "flag-aw", "hourglass_flowing_sand", "clock11", "round_pushpin", "tophat", "six_pointed_star", "dog2", "grinning", "tractor", "flag-vc", "u6709", "u6708", "flag-za", "crying_cat_face", "angel", "nail_care", "runner", "table_tennis_paddle_and_ball", "ram", "writing_hand", "bathtub", "ant", "rat", "flag-hk", "information_desk_person", "flag-ir", "rice_scene", "bookmark_tabs", "milky_way", "pencil2", "mountain", "microphone", "koala", "necktie", "atm", "bullettrain_side", "kissing_cat", "relieved", "thermometer", "flag-xk", "u55b6", "globe_with_meridians", "snowflake", "woman-kiss-woman", "loudspeaker", "princess", "printer", "flag-sy", "flag-sx", "flag-sz", "tornado", "flag-st", "flag-sv", "chart", "flag-ss", "credit_card", "flag-sm", "checkered_flag", "flag-so", "flag-sn", "eight", "flag-sh", "flag-sk", "flag-sj", "handbag", "pensive", "flag-sg", "flag-py", "medal", "arrows_clockwise", "flag-sc", "ballot_box_with_check", "eject", "fried_shrimp", "mans_shoe", "card_index_dividers", "m", "dog", "dollar", "police_car", "new_moon_with_face", "shinto_shrine", "ideograph_advantage", "pineapple", "airplane_arriving", "link", "scream", "bell", "speak_no_evil", "walking", "flag-fm", "golf", "satellite_antenna", "flag-fj", "dromedary_camel", "flag-om", "horse_racing", "three_button_mouse", "lower_left_ballpoint_pen", "radio", "flag-cv", "partly_sunny_rain", "point_down", "chicken", "unicorn_face", "umbrella_on_ground", "flag-tm", "copyright", "arrow_lower_right", "city_sunset", "yen", "waning_crescent_moon", "cupid", "mens", "virgo", "libra", "busts_in_silhouette", "straight_ruler", "flag-fr", "two", "rice", "lips", "flag-gs", "flag-ge", "flag-ac", "alarm_clock", "couplekiss", "sagittarius", "flag-dz", "electric_plug", "circus_tent", "flag-gu", "watch", "arrow_up", "bear", "face_with_head_bandage", "frowning", "flag-dm", "incoming_envelope", "flag-do", "watermelon", "rotating_light", "flag-dj", "wedding", "flag-ag", "flag-dg", "flag-gd", "yellow_heart", "gem", "flag-to", "negative_squared_cross_mark", "girl", "rage", "calling", "flag-at", "microscope", "cheese_wedge", "whale2", "x", "interrobang", "japanese_ogre", "fuelpump", "oncoming_taxi", "man_with_turban", "flag-lk", "arrow_up_small", "art", "smiling_imp", "hear_no_evil", "star_and_crescent", "convenience_store", "up", "flag-ye", "flag-cw", "computer", "arrow_down", "vhs", "flag-ky", "parking", "flag-vn", "pisces", "calendar", "flag-al", "hammer", "hourglass", "hibiscus", "shower", "black_joker", "ferris_wheel", "flag-ar", "camping", "bicyclist", "no_mouth", "postbox", "large_blue_diamond", "non-potable_water", "label", "icecream", "admission_tickets", "lower_left_paintbrush", "flag-hm", "diamonds", "champagne", "email", "older_man", "tent", "flag-ax", "raising_hand", "wc", "bed", "zipper_mouth_face", "joy", "hot_pepper", "aquarius", "waving_black_flag", "couple_with_heart", "guitar", "four_leaf_clover", "key", "flag-az", "flag-tk", "dress", "surfer", "statue_of_liberty", "crystal_ball", "cop", "clock1230", "tropical_drink", "cow", "flag-cp", "no_pedestrians", "oncoming_bus", "moyai", "restroom", "white_large_square", "kaaba", "eggplant", "comet", "low_brightness", "flag-tf", "ok_woman", "space_invader", "pig_nose", "flag-kp", "cancer", "ice_skate", "battery", "man-kiss-man", "wastebasket", "jeans", "cd", "flag-ke", "carousel_horse", "hotsprings", "page_facing_up", "flag-mn", "church", "boar", "black_square_for_stop", "flag-dk", "flag-kn", "flag-ki", "flag-kh", "boat", "turkey", "flag-am", "person_with_blond_hair", "swimmer", "wavy_dash", "three", "oden", "secret", "woman-woman-girl-boy", "stadium", "chipmunk", "stuck_out_tongue_closed_eyes", "helicopter", "heavy_division_sign", "flag-mm", "passenger_ship", "u7981", "mushroom", "fire", "two_hearts", "revolving_hearts", "arrow_down_small", "tiger", "desktop_computer", "flag-de", "foggy", "skin-tone-2", "skin-tone-3", "skin-tone-4", "skin-tone-5", "skin-tone-6", "heart_eyes", "open_file_folder", "dash", "blowfish", "speech_balloon", "wind_chime", "arrow_right_hook", "seedling", "fearful", "envelope_with_arrow", "flag-yt", "closed_umbrella", "film_projector", "bikini", "warning", "taxi", "u5408", "newspaper", "card_index", "raised_hands", "anchor", "loop", "flag-zw", "potable_water", "seven", "pound", "two_women_holding_hands", "timer_clock", "flag-rs", "registered", "sushi", "purse", "monkey_face", "u5272", "rooster", "shamrock", "anger", "rain_cloud", "vs", "flag-ro", "flag-pl", "frame_with_picture", "arrow_forward", "violin", "name_badge", "orthodox_cross", "id", "helmet_with_white_cross", "flag-re", "shopping_bags", "synagogue", "house_buildings", "white_circle", "balloon", "flag-lc", "heart_decoration", "flag-mz", "joy_cat", "kimono", "speaker", "flag-my", "train2", "first_quarter_moon", "dragon_face", "left_luggage", "flag-mx", "meat_on_bone", "light_rail", "bellhop_bell", "satellite", "arrow_heading_up", "snail", "black_small_square", "u6307", "leopard", "hand", "flag-bi", "flag-pn", "badminton_racquet_and_shuttlecock", "barber", "christmas_tree", "cityscape", "slot_machine", "ice_cream", "flag-qa", "euro", "anguished", "crossed_flags", "burrito", "rolled_up_newspaper", "musical_score", "white_frowning_face", "triangular_ruler", "ballot_box_with_ballot", "ocean", "flag-kr", "signal_strength", "flags", "the_horns", "hearts", "joystick", "muscle", "love_hotel", "hotdog", "snowman", "eyeglasses", "flag-lr", "rocket", "camel", "flag-gq", "boot", "u7533", "racehorse", "sleepy", "flag-gt", "heart_eyes_cat", "green_apple", "flag-gi", "flag-gh", "racing_motorcycle", "flag-gm", "flag-gl", "flag-gn", "flag-ga", "bridge_at_night", "flag-pe", "flag-gb", "face_with_thermometer", "clock130", "flag-gg", "flag-gf", "flashlight", "womans_hat", "flag-mf", "sandal", "white_medium_square", "snowboarder", "sunflower", "grey_exclamation", "person_frowning", "rose", "cl", "flag-cf", "cherries", "innocent", "arrow_up_down", "stopwatch", "left_speech_bubble", "ski", "pill", "musical_keyboard", "skier", "full_moon", "hugging_face", "flag-bs", "orange_book", "flag-wf", "flag-ug", "mount_fuji", "couple", "yin_yang", "japanese_goblin", "flag-as", "dart", "clock1", "clock2", "clock3", "clock4", "clock5", "clock6", "clock7", "clock8", "clock9", "doughnut", "flag-kz", "disappointed", "grin", "place_of_worship", "womans_clothes", "flag-vg", "gift", "prayer_beads", "bangbang", "stuck_out_tongue_winking_eye", "flag-kg", "candy", "lightning", "arrows_counterclockwise", "two_men_holding_hands", "dancer", "skull_and_crossbones", "trolleybus", "woman-woman-girl-girl", "bamboo", "flag-um", "trackball", "clap", "outbox_tray", "left_right_arrow", "japanese_castle", "door", "waning_gibbous_moon", "crown", "back", "flag-et", "flag-pw", "flag-us", "sparkling_heart", "clubs", "repeat", "candle", "crab", "man-woman-girl-boy", "smoking", "flag-pk", "man-man-boy-boy", "bento", "robot_face", "moon", "thunder_cloud_and_rain", "tanabata_tree", "fog", "cloud", "large_orange_diamond", "heavy_minus_sign", "o2", "nose", "flag-no", "point_up", "smiley", "facepunch", "zzz", "flag-ni", "flag-nf", "flag-ng", "corn", "flag-ne", "flag-nc", "loud_sound", "kissing_smiling_eyes", "fish_cake", "flag-ms", "flag-nz", "stew", "santa", "kissing_heart", "flag-nu", "tropical_fish", "eight_spoked_asterisk", "trophy", "on", "ok", "city_sunrise", "package", "arrow_right", "school_satchel", "o", "film_frames", "chart_with_downwards_trend", "clock10", "hammer_and_pick", "wolf", "sweat", "ox", "flag-rw", "mountain_railway", "tongue", "speaking_head_in_silhouette", "curry", "angry", "alembic", "baby_chick", "double_vertical_bar", "underage", "do_not_litter", "man-man-boy", "field_hockey_stick_and_ball", "waxing_crescent_moon", "full_moon_with_face", "bath", "flag-se", "sos", "red_circle", "flag-sd", "syringe", "last_quarter_moon", "tada", "ok_hand", "custard", "rowboat", "compression", "clock530", "heavy_multiplication_x", "white_check_mark", "tennis", "question", "beer", "flag-jo", "flag-tw", "lion_face", "flag-ru", "stars", "flag-jm", "stuck_out_tongue", "woman-woman-boy", "iphone", "flag-cm", "sweat_smile", "flag-cl", "flag-uz", "bus", "relaxed", "fireworks", "flag-md", "right_anger_bubble", "level_slider", "construction", "black_circle", "fallen_leaf", "airplane_departure", "astonished", "flag-ci", "turtle", "ear", "black_left_pointing_double_triangle_with_vertical_bar", "bug", "penguin", "arrow_heading_down", "congratulations", "snow_capped_mountain", "flag-ck", "skull", "mobile_phone_off", "flag-ht", "control_knobs", "expressionless", "fries", "grey_question", "arrow_upper_left", "strawberry", "cat2", "athletic_shoe", "unlock", "star2", "cake", "gemini", "man-man-girl-boy", "arrow_double_up", "cricket_bat_and_ball", "flag-me", "ab", "hash", "sweet_potato", "mortar_board", "cinema", "flag-mo", "hatched_chick", "triangular_flag_on_post", "flag-ml", "flag-mk", "flag-ai", "black_nib", "pig", "flag-mw", "floppy_disk", "flag-mu", "black_large_square", "koko", "flag-mr", "flag-mq", "person_with_pouting_face", "flag-ea", "bow_and_arrow", "ship", "ice_hockey_stick_and_puck", "telephone_receiver", "performing_arts", "rainbow", "movie_camera", "lemon", "arrow_double_down", "peach", "arrow_upper_right", "ng", "mountain_bicyclist", "book", "clock1130", "boom", "spiral_calendar_pad", "clock1030", "flag-km", "beach_with_umbrella", "imp", "bust_in_silhouette", "star", "rabbit2", "man-man-girl", "footprints", "football", "pear", "taurus", "articulated_lorry", "no_entry_sign", "u6e80", "money_with_wings", "flag-lu", "bike", "black_medium_square", "closed_book", "desert", "woman-woman-girl", "oil_drum", "ghost", "droplet", "flag-co", "flag-cn", "spades", "flag-ch", "vibration_mode", "phone", "dvd", "flag-cg", "menorah_with_nine_branches", "mask", "flag-cc", "mag_right", "scorpion", "flag-cy", "flag-cx", "hushed", "desert_island", "sunrise_over_mountains", "partly_sunny", "spiral_note_pad", "heavy_dollar_sign", "scroll", "flag-cr"]} -- cgit From d25d610e965be6eb9775f7b2daa0f85c0e794f1d Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 15:00:43 -0800 Subject: remove lines --- wee_slack.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 7ad39bd..dc6e252 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -654,13 +654,10 @@ def emoji_completion_cb(data, completion_item, current_buffer, completion): if current_channel is None: return w.WEECHAT_RC_OK - for e in EMOJI['emoji']: w.hook_completion_list_add(completion, ":" + e + ":", 0, w.WEECHAT_LIST_POS_SORT) - return w.WEECHAT_RC_OK - def complete_next_cb(data, current_buffer, command): """Extract current word, if it is equal to a nick, prefix it with @ and rely on nick_completion_cb adding the @-prefixed versions to the -- cgit From 7b6467ee87d6730a12f951deaa5fb5319a83f54a Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 15:58:02 -0800 Subject: fix tags --- wee_slack.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index dc6e252..44cc015 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1068,8 +1068,11 @@ class SlackChannel(object): elif ts <= SlackTS(self.last_read): tags = tag("backlog") elif self.type in ["im", "mpdm"]: - tags = tag("dm") - self.new_messages = True + if nick != self.team.nick: + tags = tag("dm") + self.new_messages = True + else: + tags = tag("dmfromme") else: tags = tag("default") self.new_messages = True @@ -2282,11 +2285,12 @@ def tag(tagset, user="unknown user"): "highlightme": "notify_highlight,log1", #when receiving a direct message "dm": "notify_private,notify_message,log1,irc_privmsg", + "dmfromme": "notify_none,log1,irc_privmsg", #when this is a join/leave, attach for smart filter ala: #if user in [x.strip() for x in w.prefix("join"), w.prefix("quit")] "joinleave": "irc_smart_filter", #catchall ? - "default": "notify_message,log1,irc_privmsg", + "default": "notify_message,log1", } return default_tag + "," + tagsets[tagset] -- cgit From 78b8290da9a8c18f21e4df77df7351ea6371e38d Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 16:17:15 -0800 Subject: never away now a config option --- wee_slack.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 44cc015..7acb5b5 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -595,6 +595,17 @@ def typing_update_cb(data, remaining_calls): w.bar_item_update("slack_typing_notice") return w.WEECHAT_RC_OK +def slack_never_away_cb(data, remaining_calls): + dbg("check", 5) + if config.never_away: + for t in EVENTROUTER.teams.values(): + slackbot = t.get_channel_map()['slackbot'] + channel = t.channels[slackbot] + request = {"type": "typing", "channel": channel.identifier} + channel.team.send_to_websocket(request, expect_reply=False) + dbg("YAY", 5) + return w.WEECHAT_RC_OK + def typing_bar_item_cb(data, current_buffer, args): """ Privides a bar item indicating who is typing in the current channel AND @@ -2496,6 +2507,7 @@ def setup_hooks(): w.hook_timer(1000, 0, 0, "typing_update_cb", "") w.hook_timer(1000, 0, 0, "buffer_list_update_callback", "EVENTROUTER") w.hook_timer(3000, 0, 0, "reconnect_callback", "EVENTROUTER") + w.hook_timer(1000 * 60 * 5, 0, 0, "slack_never_away_cb", "") w.hook_signal('buffer_closing', "buffer_closing_callback", "EVENTROUTER") w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") @@ -2537,7 +2549,6 @@ def setup_hooks(): w.hook_completion("emoji", "complete :emoji: for slack", "emoji_completion_cb", "") # Hooks to fix/implement - #w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") #w.hook_signal('buffer_opened', "buffer_opened_cb", "") #w.hook_signal('window_scrolled', "scrolled_cb", "") @@ -2596,6 +2607,7 @@ class PluginConfig(object): 'unhide_buffers_with_activity': 'false', 'short_buffer_names': 'false', 'background_load_all_history': 'false', + 'never_away': 'false', } # Set missing settings to their defaults. Load non-missing settings from -- cgit From 17d21c0c07183972aee672866582c467527992df Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 16:18:24 -0800 Subject: remove dbg --- wee_slack.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 7acb5b5..b655b16 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -596,14 +596,12 @@ def typing_update_cb(data, remaining_calls): return w.WEECHAT_RC_OK def slack_never_away_cb(data, remaining_calls): - dbg("check", 5) if config.never_away: for t in EVENTROUTER.teams.values(): slackbot = t.get_channel_map()['slackbot'] channel = t.channels[slackbot] request = {"type": "typing", "channel": channel.identifier} channel.team.send_to_websocket(request, expect_reply=False) - dbg("YAY", 5) return w.WEECHAT_RC_OK def typing_bar_item_cb(data, current_buffer, args): -- cgit From e924b06077f7e3532664aa7edc3b95a406f87cea Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 17:35:27 -0800 Subject: openweb --- wee_slack.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index b655b16..52ff1ee 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2421,6 +2421,10 @@ def command_mute(current_buffer, args): s = SlackRequest(team.token, "users.prefs.set", {"name": "muted_channels", "value": ",".join(team.muted_channels)}, team_hash=team.team_hash, channel_identifier=channel_id) EVENTROUTER.receive(s) +def command_openweb(current_buffer, args): + channel = EVENTROUTER.weechat_controller.buffers[current_buffer] + url = "https://{}/archives/{}".format(channel.team.domain, channel.slack_name) + w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) def command_nodistractions(current_buffer, args): global hide_distractions -- cgit From e3c0b42889fabc53d30e9f5ba1b7bbae635d43b8 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 19:32:56 -0800 Subject: channel renames --- wee_slack.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 52ff1ee..4b83afb 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2003,6 +2003,11 @@ def process_channel_created(message_json, eventrouter, **kwargs): #print eventrouter.teams['d80c2b6c3127dbb1991917394ed219e8212a2606'].channels['C3ZM2GMGU'].team.domain #raise +def process_channel_rename(message_json, eventrouter, **kwargs): + item = message_json["channel"] + channel = kwargs['team'].channels[item["id"]] + channel.slack_name = message_json['channel']['name'] + def process_im_open(message_json, eventrouter, **kwargs): channel = kwargs['channel'] item = message_json -- cgit From abb2a995bffadee33a15cdcf5438afa2be0d0d9a Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 19:48:14 -0800 Subject: hide channels only if they are in 'distracting' already --- wee_slack.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 4b83afb..3ff3b04 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2386,8 +2386,12 @@ def rehistory_command_callback(data, current_buffer, args): channel.get_history() def hide_command_callback(data, current_buffer, args): - current = w.current_buffer() - w.buffer_set(current, "hidden", str(int(hide_distractions))) + c = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + if c: + name = c.formatted_name(style='long_default') + if name in config.distracting_channels: + w.buffer_set(c.channel_buffer, "hidden", "1") + return w.WEECHAT_RC_OK_EAT def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) -- cgit From 62c5c99a525e3a803934b376acdca7aae4a9fd15 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 6 Feb 2017 22:23:55 -0800 Subject: fix unicode rendering --- wee_slack.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 3ff3b04..8eb799e 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1945,7 +1945,6 @@ def subprocess_message_deleted(message_json, eventrouter, channel, team): def process_reply(message_json, eventrouter, **kwargs): dbg('processing reply') - #dbg(message_json, True) team = kwargs["team"] identifier = message_json["reply_to"] try: @@ -1968,6 +1967,7 @@ def process_reply(message_json, eventrouter, **kwargs): # channels.find(message_json["channel"]).store_message(m, from_me=True) # channels.find(message_json["channel"]).buffer_prnt(server.nick, m.render(), m.ts) + process_message(m.message_json, eventrouter, channel=channel, team=team) channel.mark_read(update_remote=True, force=True) dbg("REPLY {}".format(message_json)) @@ -2072,6 +2072,9 @@ def render(message_json, team, channel, force=False): text = text.replace("<", "<") text = text.replace(">", ">") text = text.replace("&", "&") + + if type(text) is not unicode: + text = text.decode('UTF-8', 'replace') text = text.encode('utf-8') # if self.threads: -- cgit From c5080f60bb85b4cb144f20c918aa6ff8f6a498a8 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Tue, 7 Feb 2017 10:46:10 -0800 Subject: ts being included gives us more results --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 8eb799e..5bb32de 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2435,7 +2435,7 @@ def command_mute(current_buffer, args): def command_openweb(current_buffer, args): channel = EVENTROUTER.weechat_controller.buffers[current_buffer] - url = "https://{}/archives/{}".format(channel.team.domain, channel.slack_name) + url = "https://{}/archives/{}/p{}".format(channel.team.domain, channel.slack_name, SlackTS()) w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) def command_nodistractions(current_buffer, args): -- cgit From 887fdd9b08b9c364083fb7503472239291a14f34 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Tue, 7 Feb 2017 10:49:52 -0800 Subject: lolfix --- wee_slack.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 5bb32de..8e1da1c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2435,7 +2435,8 @@ def command_mute(current_buffer, args): def command_openweb(current_buffer, args): channel = EVENTROUTER.weechat_controller.buffers[current_buffer] - url = "https://{}/archives/{}/p{}".format(channel.team.domain, channel.slack_name, SlackTS()) + now = SlackTS() + url = "https://{}/archives/{}/p{}{}".format(channel.team.domain, channel.slack_name, now.majorstr(), now.minorstr()) w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) def command_nodistractions(current_buffer, args): -- cgit From 9ba8a72431f961dbfa210c082a7f75cd0239f543 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Tue, 7 Feb 2017 10:56:03 -0800 Subject: final fix --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 8e1da1c..3caa868 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2436,7 +2436,7 @@ def command_mute(current_buffer, args): def command_openweb(current_buffer, args): channel = EVENTROUTER.weechat_controller.buffers[current_buffer] now = SlackTS() - url = "https://{}/archives/{}/p{}{}".format(channel.team.domain, channel.slack_name, now.majorstr(), now.minorstr()) + url = "https://{}/archives/{}/p{}000000".format(channel.team.domain, channel.slack_name, now.majorstr()) w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) def command_nodistractions(current_buffer, args): -- cgit From e411a9d01e1c6e67c2ad413342b0e52efab0d5ca Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 8 Feb 2017 08:43:48 -0800 Subject: team join fixed + test --- _pytest/test_everything.py | 2 +- _pytest/test_processteamjoin.py | 54 +++++++++++++++++++++++++++++++++++++++++ wee_slack.py | 13 ++++++++-- 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 _pytest/test_processteamjoin.py diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py index 1abfa42..a121541 100644 --- a/_pytest/test_everything.py +++ b/_pytest/test_everything.py @@ -44,7 +44,7 @@ def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): print "####################" print len(eventrouter.queue) -# assert False + #assert False diff --git a/_pytest/test_processteamjoin.py b/_pytest/test_processteamjoin.py new file mode 100644 index 0000000..00a8b4c --- /dev/null +++ b/_pytest/test_processteamjoin.py @@ -0,0 +1,54 @@ +import glob +import json + +from wee_slack import ProcessNotImplemented + +def test_process_reply(monkeypatch, mock_websocket, realish_eventrouter): + + eventrouter = realish_eventrouter + + t = eventrouter.teams.keys()[0] + #u = eventrouter.teams[t].users.keys()[0] + + #user = eventrouter.teams[t].users[u] + #print user + + #delete charles so we can add him + del eventrouter.teams[t].users['U4096CBHC'] + + assert len(eventrouter.teams[t].users) == 3 + + socket = mock_websocket + eventrouter.teams[t].ws = socket + + datafiles = glob.glob("_pytest/data/websocket/1485975606.59-team_join.json") + + print datafiles + #assert False + + notimplemented = set() + + for fname in datafiles: + try: + print "####################" + data = json.loads(open(fname, 'r').read()) + socket.add(data) + print data + eventrouter.receive_ws_callback(t) + eventrouter.handle_next() + except ProcessNotImplemented as e: + notimplemented.add(str(e)) + #this handles some message data not existing - need to fix + except KeyError: + pass + + if len(notimplemented) > 0: + print "####################" + print sorted(notimplemented) + print "####################" + + #print len(eventrouter.queue) + assert len(eventrouter.teams[t].users) == 4 + + + diff --git a/wee_slack.py b/wee_slack.py index 3caa868..0ee162b 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1804,6 +1804,11 @@ def process_user_typing(message_json, eventrouter, **kwargs): channel.set_typing(team.users.get(message_json["user"]).name) w.bar_item_update("slack_typing_notice") +def process_team_join(message_json, eventrouter, **kwargs): + user = message_json['user'] + team = kwargs["team"] + team.users[user["id"]] = SlackUser(**user) + def process_pong(message_json, eventrouter, **kwargs): pass @@ -2434,9 +2439,13 @@ def command_mute(current_buffer, args): EVENTROUTER.receive(s) def command_openweb(current_buffer, args): + #if done from server buffer, open slack for reals channel = EVENTROUTER.weechat_controller.buffers[current_buffer] - now = SlackTS() - url = "https://{}/archives/{}/p{}000000".format(channel.team.domain, channel.slack_name, now.majorstr()) + if isinstance(channel, SlackTeam): + url = "https://{}".format(channel.team.domain) + else: + now = SlackTS() + url = "https://{}/archives/{}/p{}000000".format(channel.team.domain, channel.slack_name, now.majorstr()) w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) def command_nodistractions(current_buffer, args): -- cgit From 369e7f2d26ac5a8f3604ad79a5b1fbceb0a8ccd1 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 8 Feb 2017 20:31:54 -0800 Subject: fix read marker --- wee_slack.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 0ee162b..9390363 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1205,6 +1205,7 @@ class SlackChannel(object): del self.typing[user] return typing def mark_read(self, ts=None, update_remote=True, force=False): + dbg("called mark_read!", 5) if not ts: ts = SlackTS() if self.new_messages or force: @@ -1984,9 +1985,9 @@ def process_channel_marked(message_json, eventrouter, **kwargs): complete """ channel = kwargs["channel"] - ts = kwargs.get("ts", None) + ts = message_json.get("ts", None) if ts: - channel.mark_read(ts=ts, update_remote=False) + channel.mark_read(ts=ts, force=True, update_remote=False) else: dbg("tried to mark something weird {}".format(message_json)) def process_group_marked(message_json, eventrouter, **kwargs): -- cgit From 979ddc9d0c084f3069a746b27aa197c8ed71bcd4 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 8 Feb 2017 20:56:40 -0800 Subject: unread_count_display is new and better --- wee_slack.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 9390363..75784d4 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -996,7 +996,7 @@ class SlackChannel(object): if force: self.create_buffer() else: - for reason in ["is_member", "is_open", "unread_count"]: + for reason in ["is_member", "is_open", "unread_count_display"]: try: if eval("self." + reason): self.create_buffer() @@ -1035,8 +1035,8 @@ class SlackChannel(object): #else: # self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) try: - if self.unread_count != 0: - for c in range(1, self.unread_count): + if self.unread_count_display != 0: + for c in range(0, self.unread_count_display): if self.type == "im": w.buffer_set(self.channel_buffer, "hotlist", "2") else: @@ -1064,17 +1064,18 @@ class SlackChannel(object): def buffer_prnt(self, nick, text, timestamp=str(time.time()), tagset=None, **kwargs): data = "{}\t{}".format(nick, text) ts = SlackTS(timestamp) + last_read = SlackTS(self.last_read) #without this, DMs won't open automatically - if not self.channel_buffer: + if not self.channel_buffer and ts <= last_read: self.open(update_remote=False) if self.channel_buffer: #backlog messages - we will update the read marker as we print these - backlog = True if ts <= SlackTS(self.last_read) else False + backlog = True if ts <= last_read else False if tagset: tags = tag(tagset) #we have to infer the tagset because we weren't told - elif ts <= SlackTS(self.last_read): + elif ts <= last_read: tags = tag("backlog") elif self.type in ["im", "mpdm"]: if nick != self.team.nick: @@ -1846,7 +1847,7 @@ def process_message(message_json, eventrouter, store=True, **kwargs): text = text[1:-1] if message.sender != channel.team.nick: text = message.sender + " " + text - channel.unread_count += 1 + channel.unread_count_display += 1 channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, **kwargs) else: @@ -1854,9 +1855,9 @@ def process_message(message_json, eventrouter, store=True, **kwargs): if 'edited' in message_json: suffix = ' (edited)' try: - channel.unread_count += 1 + channel.unread_count_display += 1 except: - channel.unread_count = 1 + channel.unread_count_display = 1 channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) if store: -- cgit From d6d7b23763abc1a67d1340782b3ff2d980d3df4d Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 08:11:21 -0800 Subject: fix nick tags --- wee_slack.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 75784d4..8bbc3b6 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1061,7 +1061,7 @@ class SlackChannel(object): if update_remote and not self.eventrouter.shutting_down: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) - def buffer_prnt(self, nick, text, timestamp=str(time.time()), tagset=None, **kwargs): + def buffer_prnt(self, nick, text, timestamp=str(time.time()), tagset=None, tag_nick=None, **kwargs): data = "{}\t{}".format(nick, text) ts = SlackTS(timestamp) last_read = SlackTS(self.last_read) @@ -1072,19 +1072,19 @@ class SlackChannel(object): #backlog messages - we will update the read marker as we print these backlog = True if ts <= last_read else False if tagset: - tags = tag(tagset) + tags = tag(tagset, user=tag_nick) #we have to infer the tagset because we weren't told elif ts <= last_read: - tags = tag("backlog") + tags = tag("backlog", user=tag_nick) elif self.type in ["im", "mpdm"]: if nick != self.team.nick: - tags = tag("dm") + tags = tag("dm", user=tag_nick) self.new_messages = True else: tags = tag("dmfromme") else: - tags = tag("default") + tags = tag("default", user=tag_nick) self.new_messages = True try: @@ -1577,6 +1577,7 @@ class SlackMessage(object): dbg(self.message_json) def get_sender(self, utf8=True): name = u"" + self.sender_plain = u"" if 'bot_id' in self.message_json and self.message_json['bot_id'] is not None: name = u"{} :]".format(self.team.bots[self.message_json["bot_id"]].formatted_name()) elif 'user' in self.message_json: @@ -1588,6 +1589,7 @@ class SlackMessage(object): name = u"{} :]".format(u.formatted_name()) else: name = u"{}".format(u.formatted_name()) + self.sender_plain = u"{}".format(u.formatted_name(enable_color=False)) elif 'username' in self.message_json: name = u"-{}-".format(self.message_json["username"]) elif 'service_name' in self.message_json: @@ -1848,7 +1850,7 @@ def process_message(message_json, eventrouter, store=True, **kwargs): if message.sender != channel.team.nick: text = message.sender + " " + text channel.unread_count_display += 1 - channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, **kwargs) + channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, tag_nick=message.sender_plain, **kwargs) else: suffix = '' @@ -1858,7 +1860,7 @@ def process_message(message_json, eventrouter, store=True, **kwargs): channel.unread_count_display += 1 except: channel.unread_count_display = 1 - channel.buffer_prnt(message.sender, text + suffix, message.ts, **kwargs) + channel.buffer_prnt(message.sender, text + suffix, message.ts, tag_nick=message.sender_plain, **kwargs) if store: channel.store_message(message, team) @@ -2298,8 +2300,11 @@ def modify_print_time(buffer, new_id, time): return w.WEECHAT_RC_OK -def tag(tagset, user="unknown user"): - default_tag = "nick_" + user +def tag(tagset, user=None): + if user: + default_tag = "nick_" + user + else: + default_tag = '' tagsets = { #when replaying something old "backlog": "no_highlight,notify_none,logger_backlog_end", -- cgit From e0f55196c9758a7e235a31234e2c2479acd3c203 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 08:18:20 -0800 Subject: EAT so no error --- wee_slack.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wee_slack.py b/wee_slack.py index 8bbc3b6..c81a050 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2399,6 +2399,7 @@ def rehistory_command_callback(data, current_buffer, args): channel.got_history = False w.buffer_clear(channel.channel_buffer) channel.get_history() + return w.WEECHAT_RC_OK_EAT def hide_command_callback(data, current_buffer, args): c = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) -- cgit From 4300d68e7b4bcf450189350a585313ef8b0dabdf Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 08:35:13 -0800 Subject: joinleave fix --- wee_slack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index c81a050..4a60d96 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2304,7 +2304,7 @@ def tag(tagset, user=None): if user: default_tag = "nick_" + user else: - default_tag = '' + default_tag = 'nick_unknown' tagsets = { #when replaying something old "backlog": "no_highlight,notify_none,logger_backlog_end", @@ -2317,7 +2317,7 @@ def tag(tagset, user=None): "dmfromme": "notify_none,log1,irc_privmsg", #when this is a join/leave, attach for smart filter ala: #if user in [x.strip() for x in w.prefix("join"), w.prefix("quit")] - "joinleave": "irc_smart_filter", + "joinleave": "irc_smart_filter,no_highlight", #catchall ? "default": "notify_message,log1", } -- cgit From bbc1681c386fa6ef33d7cddf4fec4aed9eb569c1 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 09:02:06 -0800 Subject: senders fix --- wee_slack.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 4a60d96..87331a4 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -314,7 +314,7 @@ class EventRouter(object): via callback to drain events from the queue. It also attaches useful metadata and context to events as they are processed. """ - if len(self.slow_queue) > 0 and ((self.slow_queue_timer + 5) < time.time()): + if len(self.slow_queue) > 0 and ((self.slow_queue_timer + 1) < time.time()): #for q in self.slow_queue[0]: dbg("from slow queue", 0) self.queue.append(self.slow_queue.pop()) @@ -1559,8 +1559,10 @@ class SlackMessage(object): self.thread_channel = None if override_sender: self.sender = override_sender + self.sender_plain = override_sender else: - self.sender = self.get_sender() + senders = self.get_sender() + self.sender, self.sender_plain = senders[0], senders[1] self.suffix = '' self.ts = SlackTS(message_json['ts']) def __hash__(self): @@ -1577,29 +1579,34 @@ class SlackMessage(object): dbg(self.message_json) def get_sender(self, utf8=True): name = u"" - self.sender_plain = u"" + name_plain = u"" if 'bot_id' in self.message_json and self.message_json['bot_id'] is not None: name = u"{} :]".format(self.team.bots[self.message_json["bot_id"]].formatted_name()) + name_plain = u"{}".format(self.team.bots[self.message_json["bot_id"]].formatted_name(enable_color=False)) elif 'user' in self.message_json: if self.message_json['user'] == self.team.myidentifier: name = self.team.users[self.team.myidentifier].name + name_plain = self.team.users[self.team.myidentifier].name elif self.message_json['user'] in self.team.users: u = self.team.users[self.message_json['user']] if u.is_bot: name = u"{} :]".format(u.formatted_name()) else: name = u"{}".format(u.formatted_name()) - self.sender_plain = u"{}".format(u.formatted_name(enable_color=False)) + name_plain = u"{}".format(u.formatted_name(enable_color=False)) elif 'username' in self.message_json: name = u"-{}-".format(self.message_json["username"]) + name_plain = u"{}".format(self.message_json["username"]) elif 'service_name' in self.message_json: name = u"-{}-".format(self.message_json["service_name"]) + name_plain = u"{}".format(self.message_json["service_name"]) else: name = u"" + name_plain = u"" if utf8: - return name.encode('utf-8') + return (name.encode('utf-8'), name_plain.encode('utf-8')) else: - return name + return (name, name_plain) def add_reaction(self, reaction, user): m = self.message_json.get('reactions', None) if m: -- cgit From d316acfb2edbbfa0633675a7ea358f4c6adbee8f Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 09:03:18 -0800 Subject: add note about senders --- wee_slack.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 87331a4..3b41067 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1550,6 +1550,8 @@ class SlackMessage(object): Represents a single slack message and associated context/metadata. These are modifiable and can be rerendered to change a message, delete a message, add a reaction, add a thread. + Note: these can't be tied to a SlackUser object because users + can be deleted, so we have to store sender in each one. """ def __init__(self, message_json, team, channel, override_sender=None): self.team = team -- cgit From 2e3fcbb7ab7cd90d8f66692e7579b924cade4e7f Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 09:19:37 -0800 Subject: decorators and tag fix --- wee_slack.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 3b41067..9aa6b1d 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1,6 +1,8 @@ #-*- coding: utf-8 -*- # +from functools import wraps + import time import json import pickle @@ -66,6 +68,30 @@ SLACK_API_TRANSLATOR = { } +###### Decorators have to be up here + +def slack_buffer_or_ignore(f): + """ + Only run this function if we're in a slack buffer, else ignore + """ + @wraps(f) + def wrapper(current_buffer, *args, **kwargs): + if current_buffer in EVENTROUTER.weechat_controller.buffers: + return w.WEECHAT_RC_OK + return f(current_buffer, *args, **kwargs) + return wrapper + +def slack_buffer_required(f): + """ + Only run this function if we're in a slack buffer, else print error + """ + @wraps(f) + def wrapper(current_buffer, *args, **kwargs): + if current_buffer not in EVENTROUTER.weechat_controller.buffers: + return w.WEECHAT_RC_ERROR + return f(current_buffer, *args, **kwargs) + return wrapper + NICK_GROUP_HERE = "0|Here" NICK_GROUP_AWAY = "1|Away" @@ -2311,6 +2337,7 @@ def modify_print_time(buffer, new_id, time): def tag(tagset, user=None): if user: + user.replace(" ", "_") default_tag = "nick_" + user else: default_tag = 'nick_unknown' @@ -2335,6 +2362,7 @@ def tag(tagset, user=None): ###### New/converted command_ commands +@slack_buffer_or_ignore def join_command_cb(data, current_buffer, args): args = args.split() if len(args) < 2: @@ -2539,7 +2567,6 @@ def load_emoji(): dbg("Unexpected error: {}".format(sys.exc_info()), 5) return w.WEECHAT_RC_OK - def setup_hooks(): cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} -- cgit From 3299780535afe304634b11593554ad6c621565c8 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 10:19:23 -0800 Subject: part/leave works --- wee_slack.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 9aa6b1d..f3494f2 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -75,10 +75,10 @@ def slack_buffer_or_ignore(f): Only run this function if we're in a slack buffer, else ignore """ @wraps(f) - def wrapper(current_buffer, *args, **kwargs): - if current_buffer in EVENTROUTER.weechat_controller.buffers: + def wrapper(data, current_buffer, *args, **kwargs): + if current_buffer not in EVENTROUTER.weechat_controller.buffers: return w.WEECHAT_RC_OK - return f(current_buffer, *args, **kwargs) + return f(data, current_buffer, *args, **kwargs) return wrapper def slack_buffer_required(f): @@ -86,10 +86,10 @@ def slack_buffer_required(f): Only run this function if we're in a slack buffer, else print error """ @wraps(f) - def wrapper(current_buffer, *args, **kwargs): + def wrapper(data, current_buffer, *args, **kwargs): if current_buffer not in EVENTROUTER.weechat_controller.buffers: return w.WEECHAT_RC_ERROR - return f(current_buffer, *args, **kwargs) + return f(data, current_buffer, *args, **kwargs) return wrapper @@ -1232,7 +1232,6 @@ class SlackChannel(object): del self.typing[user] return typing def mark_read(self, ts=None, update_remote=True, force=False): - dbg("called mark_read!", 5) if not ts: ts = SlackTS() if self.new_messages or force: @@ -2364,6 +2363,7 @@ def tag(tagset, user=None): @slack_buffer_or_ignore def join_command_cb(data, current_buffer, args): + print args args = args.split() if len(args) < 2: w.prnt(current_buffer, "Missing channel argument") @@ -2373,6 +2373,29 @@ def join_command_cb(data, current_buffer, args): else: return w.WEECHAT_RC_OK +@slack_buffer_or_ignore +def part_command_cb(data, current_buffer, args): + #if channels.find(current_buffer) or servers.find(current_buffer): + e = EVENTROUTER + args = args.split() + if len(args) > 1: + team = e.weechat_controller.buffers[current_buffer].team + cmap = team.get_channel_map() + channel = "".join(args[1:]) + if channel in cmap: + buffer_ptr = team.channels[cmap[channel]].channel_buffer + e.weechat_controller.unregister_buffer(buffer_ptr, update_remote=True, close_buffer=True) + else: + e.weechat_controller.unregister_buffer(current_buffer, update_remote=True, close_buffer=True) + return w.WEECHAT_RC_OK_EAT + +@slack_buffer_or_ignore +def me_command_cb(data, current_buffer, arg): + dbg("me!", 5) + message = "_{}_".format(arg) + buffer_input_callback("EVENTROUTER", current_buffer, message) + return w.WEECHAT_RC_OK + def command_talk(current_buffer, arg): """ incomplete because globals hack @@ -2380,8 +2403,7 @@ def command_talk(current_buffer, arg): /slack talk [user] """ e = EVENTROUTER - current = w.current_buffer() - team = e.weechat_controller.buffers[current].team + team = e.weechat_controller.buffers[current_buffer].team dbg(team) c = team.get_channel_map() if arg not in c: -- cgit From a21dfa3f79170d76f2f96ce2668938dca50ee0fc Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 10:20:15 -0800 Subject: remove debug --- wee_slack.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index f3494f2..a5536ef 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2375,7 +2375,6 @@ def join_command_cb(data, current_buffer, args): @slack_buffer_or_ignore def part_command_cb(data, current_buffer, args): - #if channels.find(current_buffer) or servers.find(current_buffer): e = EVENTROUTER args = args.split() if len(args) > 1: @@ -2391,7 +2390,6 @@ def part_command_cb(data, current_buffer, args): @slack_buffer_or_ignore def me_command_cb(data, current_buffer, arg): - dbg("me!", 5) message = "_{}_".format(arg) buffer_input_callback("EVENTROUTER", current_buffer, message) return w.WEECHAT_RC_OK -- cgit From b582aee685b983cca237ef233611da2f2c67080f Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 10:25:38 -0800 Subject: msg works --- wee_slack.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index a5536ef..4f36dcd 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2394,6 +2394,22 @@ def me_command_cb(data, current_buffer, arg): buffer_input_callback("EVENTROUTER", current_buffer, message) return w.WEECHAT_RC_OK +@slack_buffer_or_ignore +def msg_command_cb(data, current_buffer, args): + dbg("msg_command_cb") + aargs = args.split(None, 2) + who = aargs[1] + command_talk(current_buffer, who) + + if len(aargs) > 2: + message = aargs[2] + team = EVENTROUTER.weechat_controller.buffers[current_buffer].team + cmap = team.get_channel_map() + if who in cmap: + channel = team.channels[cmap[channel]] + channel.send_message(message) + return w.WEECHAT_RC_OK_EAT + def command_talk(current_buffer, arg): """ incomplete because globals hack -- cgit From 5d53194271ade6c0fa74a8cb8127f40466de7f54 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 10:44:14 -0800 Subject: fix up commands --- wee_slack.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 4f36dcd..04770e7 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2368,7 +2368,7 @@ def join_command_cb(data, current_buffer, args): if len(args) < 2: w.prnt(current_buffer, "Missing channel argument") return w.WEECHAT_RC_OK_EAT - elif command_talk(current_buffer, args[1]): + elif command_talk(data, current_buffer, args[1]): return w.WEECHAT_RC_OK_EAT else: return w.WEECHAT_RC_OK @@ -2388,6 +2388,52 @@ def part_command_cb(data, current_buffer, args): e.weechat_controller.unregister_buffer(current_buffer, update_remote=True, close_buffer=True) return w.WEECHAT_RC_OK_EAT +@slack_buffer_or_ignore +def topic_command_cb(data, current_buffer, args): + n = len(args.split()) + if n < 2: + channel = channels.find(current_buffer) + if channel: + w.prnt(current_buffer, 'Topic for {} is "{}"'.format(channel.name, channel.topic)) + return w.WEECHAT_RC_OK_EAT + elif command_topic(data, current_buffer, args.split(None, 1)[1]): + return w.WEECHAT_RC_OK_EAT + else: + return w.WEECHAT_RC_ERROR + +@slack_buffer_required +def command_topic(data, current_buffer, args): + """ + Change the topic of a channel + /slack topic [] [|-delete] + """ + print 'topic' + e = EVENTROUTER + team = e.weechat_controller.buffers[current_buffer].team + print team + #server = servers.find(current_domain_name()) + arrrrgs = args.split(None, 1) + if arrrrgs[0].startswith('#'): + cmap = team.get_channel_map() + channel_name = arrrrgs[0][1:] + channel = team.channels[cmap[channel_name]] + print channel +# topic = arrrrgs[1] + else: + channel = e.weechat_controller.buffers[current_buffer] +# topic = args + print channel + + if channel: + dbg("channel!", 5) +# if topic == "-delete": +# async_slack_api_request(server.domain, server.token, 'channels.setTopic', {"channel": channel.identifier, "topic": ""}) +# else: +# async_slack_api_request(server.domain, server.token, 'channels.setTopic', {"channel": channel.identifier, "topic": topic}) +# return True +# else: +# return False + @slack_buffer_or_ignore def me_command_cb(data, current_buffer, arg): message = "_{}_".format(arg) @@ -2399,7 +2445,7 @@ def msg_command_cb(data, current_buffer, args): dbg("msg_command_cb") aargs = args.split(None, 2) who = aargs[1] - command_talk(current_buffer, who) + command_talk(data, current_buffer, who) if len(aargs) > 2: message = aargs[2] @@ -2410,7 +2456,7 @@ def msg_command_cb(data, current_buffer, args): channel.send_message(message) return w.WEECHAT_RC_OK_EAT -def command_talk(current_buffer, arg): +def command_talk(data, current_buffer, arg): """ incomplete because globals hack Open a chat with the specified user @@ -2438,7 +2484,7 @@ def command_talk(current_buffer, arg): w.buffer_set(chan.channel_buffer, "display", "1") return True -def command_showmuted(current_buffer, args): +def command_showmuted(data, current_buffer, args): current = w.current_buffer() w.prnt(EVENTROUTER.weechat_controller.buffers[current].team.channel_buffer, str(EVENTROUTER.weechat_controller.buffers[current].team.muted_channels)) @@ -2490,12 +2536,12 @@ def slack_command_cb(data, current_buffer, args): function_name, args = a[0], None # try: - EVENTROUTER.cmds[function_name](current_buffer, args) + EVENTROUTER.cmds[function_name]("", current_buffer, args) # except KeyError: # w.prnt("", "Command not found: " + function_name) return w.WEECHAT_RC_OK -def command_distracting(current_buffer, args): +def command_distracting(data, current_buffer, args): channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) if channel: fullname = channel.formatted_name(style="long_default") @@ -2508,7 +2554,7 @@ def command_distracting(current_buffer, args): def save_distracting_channels(): w.config_set_plugin('distracting_channels', ','.join(config.distracting_channels)) -def command_mute(current_buffer, args): +def command_mute(data, current_buffer, args): current = w.current_buffer() channel_id = EVENTROUTER.weechat_controller.buffers[current].identifier team = EVENTROUTER.weechat_controller.buffers[current].team @@ -2519,7 +2565,7 @@ def command_mute(current_buffer, args): s = SlackRequest(team.token, "users.prefs.set", {"name": "muted_channels", "value": ",".join(team.muted_channels)}, team_hash=team.team_hash, channel_identifier=channel_id) EVENTROUTER.receive(s) -def command_openweb(current_buffer, args): +def command_openweb(data, current_buffer, args): #if done from server buffer, open slack for reals channel = EVENTROUTER.weechat_controller.buffers[current_buffer] if isinstance(channel, SlackTeam): @@ -2529,7 +2575,7 @@ def command_openweb(current_buffer, args): url = "https://{}/archives/{}/p{}000000".format(channel.team.domain, channel.slack_name, now.majorstr()) w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) -def command_nodistractions(current_buffer, args): +def command_nodistractions(data, current_buffer, args): global hide_distractions hide_distractions = not hide_distractions if config.distracting_channels != ['']: @@ -2552,7 +2598,7 @@ def label_command_cb(data, current_buffer, args): new_name = " +" + aargs[1] w.buffer_set(channel.channel_buffer, "short_name", new_name) -def command_p(current_buffer, args): +def command_p(data, current_buffer, args): w.prnt("", "{}".format(eval(args))) ###### NEW EXCEPTIONS @@ -2647,12 +2693,12 @@ def setup_hooks(): w.hook_command_run('/msg', 'msg_command_cb', '') w.hook_command_run('/label', 'label_command_cb', '') w.hook_command_run("/input complete_next", "complete_next_cb", "") - w.hook_command_run('/away', 'away_command_cb', '') w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") w.hook_completion("emoji", "complete :emoji: for slack", "emoji_completion_cb", "") # Hooks to fix/implement + #w.hook_command_run('/away', 'away_command_cb', '') #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") #w.hook_signal('buffer_opened', "buffer_opened_cb", "") #w.hook_signal('window_scrolled', "scrolled_cb", "") -- cgit From 966cd40a8322e18b0ca9db012568ca823edd76eb Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 11:11:28 -0800 Subject: topic works --- wee_slack.py | 83 +++++++++++++++++++++++++++--------------------------------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 04770e7..8649e47 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2361,18 +2361,6 @@ def tag(tagset, user=None): ###### New/converted command_ commands -@slack_buffer_or_ignore -def join_command_cb(data, current_buffer, args): - print args - args = args.split() - if len(args) < 2: - w.prnt(current_buffer, "Missing channel argument") - return w.WEECHAT_RC_OK_EAT - elif command_talk(data, current_buffer, args[1]): - return w.WEECHAT_RC_OK_EAT - else: - return w.WEECHAT_RC_OK - @slack_buffer_or_ignore def part_command_cb(data, current_buffer, args): e = EVENTROUTER @@ -2407,32 +2395,32 @@ def command_topic(data, current_buffer, args): Change the topic of a channel /slack topic [] [|-delete] """ + print args print 'topic' e = EVENTROUTER team = e.weechat_controller.buffers[current_buffer].team print team #server = servers.find(current_domain_name()) - arrrrgs = args.split(None, 1) - if arrrrgs[0].startswith('#'): + args = args.split(' ') + print args + if len(args) > 2 and args[1].startswith('#'): cmap = team.get_channel_map() - channel_name = arrrrgs[0][1:] + channel_name = args[1][1:] channel = team.channels[cmap[channel_name]] print channel -# topic = arrrrgs[1] + topic = " ".join(args[2:]) else: channel = e.weechat_controller.buffers[current_buffer] -# topic = args - print channel + topic = " ".join(args[1:]) if channel: - dbg("channel!", 5) -# if topic == "-delete": -# async_slack_api_request(server.domain, server.token, 'channels.setTopic', {"channel": channel.identifier, "topic": ""}) -# else: -# async_slack_api_request(server.domain, server.token, 'channels.setTopic', {"channel": channel.identifier, "topic": topic}) -# return True -# else: -# return False + if topic == "-delete": + topic = '' + s = SlackRequest(team.token, "channels.setTopic", {"channel": channel.identifier, "topic": topic}, team_hash=team.team_hash) + EVENTROUTER.receive(s) + return w.WEECHAT_RC_OK_EAT + else: + return w.WEECHAT_RC_ERROR_EAT @slack_buffer_or_ignore def me_command_cb(data, current_buffer, arg): @@ -2456,33 +2444,36 @@ def msg_command_cb(data, current_buffer, args): channel.send_message(message) return w.WEECHAT_RC_OK_EAT -def command_talk(data, current_buffer, arg): +@slack_buffer_or_ignore +def command_talk(data, current_buffer, args): """ - incomplete because globals hack Open a chat with the specified user /slack talk [user] """ e = EVENTROUTER team = e.weechat_controller.buffers[current_buffer].team - dbg(team) + dbg(team, 5) + channel_name = args.split(' ')[1] + dbg(channel_name, 5) c = team.get_channel_map() - if arg not in c: + if channel_name not in c: u = team.get_username_map() - if arg in u: - s = SlackRequest(team.token, "im.open", {"user": u[arg]}, team_hash=team.team_hash) + if channel_name in u: + s = SlackRequest(team.token, "im.open", {"user": u[channel_name]}, team_hash=team.team_hash) EVENTROUTER.receive(s) dbg("found user") #refresh channel map here c = team.get_channel_map() - if arg.startswith('#'): - arg = arg[1:] - if arg in c: - chan = team.channels[c[arg]] + if channel_name.startswith('#'): + channel_name = arg[1:] + if channel_name in c: + chan = team.channels[c[channel_name]] chan.open() if config.switch_buffer_on_join: w.buffer_set(chan.channel_buffer, "display", "1") - return True + return w.WEECHAT_RC_OK_EAT + return w.WEECHAT_RC_OK_EAT def command_showmuted(data, current_buffer, args): current = w.current_buffer() @@ -2531,14 +2522,14 @@ def hide_command_callback(data, current_buffer, args): def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) if len(a) > 1: - function_name, args = a[0], " ".join(a[1:]) + function_name, args = a[0], args else: - function_name, args = a[0], None + function_name, args = a[0], args -# try: - EVENTROUTER.cmds[function_name]("", current_buffer, args) -# except KeyError: -# w.prnt("", "Command not found: " + function_name) + try: + EVENTROUTER.cmds[function_name]("", current_buffer, args) + except KeyError: + w.prnt("", "Command not found: " + function_name) return w.WEECHAT_RC_OK def command_distracting(data, current_buffer, args): @@ -2681,11 +2672,11 @@ def setup_hooks(): w.hook_command('me', 'me_command_cb', '') w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') - w.hook_command_run('/query', 'join_command_cb', '') - w.hook_command_run('/join', 'join_command_cb', '') + w.hook_command_run('/query', 'command_talk', '') + w.hook_command_run('/join', 'command_talk', '') w.hook_command_run('/part', 'part_command_cb', '') w.hook_command_run('/leave', 'part_command_cb', '') - w.hook_command_run('/topic', 'topic_command_cb', '') + w.hook_command_run('/topic', 'command_topic', '') w.hook_command_run('/thread', 'thread_command_callback', '') w.hook_command_run('/reply', 'thread_command_callback', '') w.hook_command_run('/rehistory', 'rehistory_command_callback', '') -- cgit From 8aba52aa6993ed480ea50cdb58d8d2d26f3eb5bc Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 11:37:54 -0800 Subject: handle topic fully --- wee_slack.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 8649e47..1705ef3 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -952,6 +952,7 @@ class SlackChannel(object): self.members = set(kwargs.get('members', set())) self.eventrouter = eventrouter self.slack_name = kwargs["name"] + self.slack_topic = kwargs.get("topic", {"value": ""}) self.identifier = kwargs["id"] self.last_read = SlackTS(kwargs.get("last_read", SlackTS())) #print self.last_read @@ -998,6 +999,14 @@ class SlackChannel(object): "long_base": "{}.{}".format(self.team.domain, self.slack_name), } return select[style] + def render_topic(self, topic=None): + if self.channel_buffer: + if not topic: + encoded_topic = self.slack_topic['value'].encode('utf-8') + else: + encoded_topic = topic.encode('utf-8') + self.encoded_topic = topic.encode('utf-8') + w.buffer_set(self.channel_buffer, "title", encoded_topic) def update_from_message_json(self, message_json): for key, value in message_json.items(): setattr(self, key, value) @@ -1053,6 +1062,7 @@ class SlackChannel(object): w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') w.buffer_set(self.channel_buffer, "localvar_set_channel", self.formatted_name()) w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) + self.render_topic() #if self.server.alias: # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) #else: @@ -1862,7 +1872,7 @@ def process_message(message_json, eventrouter, store=True, **kwargs): 'message_deleted', 'channel_join', 'channel_leave', - #'channel_topic', + 'channel_topic', #'group_join', #'group_leave', ] @@ -1986,6 +1996,11 @@ def subprocess_message_changed(message_json, eventrouter, channel, team): def subprocess_message_deleted(message_json, eventrouter, channel, team): channel.change_message(message_json["deleted_ts"], "(deleted)", '') +def subprocess_channel_topic(message_json, eventrouter, channel, team): + text = unfurl_refs(message_json["text"], ignore_alt_text=False) + channel.buffer_prnt(w.prefix("network").rstrip(), text, message_json["ts"]) + channel.render_topic(message_json["topic"]) + def process_reply(message_json, eventrouter, **kwargs): dbg('processing reply') team = kwargs["team"] @@ -2395,19 +2410,14 @@ def command_topic(data, current_buffer, args): Change the topic of a channel /slack topic [] [|-delete] """ - print args - print 'topic' e = EVENTROUTER team = e.weechat_controller.buffers[current_buffer].team - print team #server = servers.find(current_domain_name()) args = args.split(' ') - print args if len(args) > 2 and args[1].startswith('#'): cmap = team.get_channel_map() channel_name = args[1][1:] channel = team.channels[cmap[channel_name]] - print channel topic = " ".join(args[2:]) else: channel = e.weechat_controller.buffers[current_buffer] -- cgit From 584861d2ebde84bca10ca98c5ba267b5f56382bb Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 11:40:29 -0800 Subject: topic whocares --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 1705ef3..f55d467 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1998,7 +1998,7 @@ def subprocess_message_deleted(message_json, eventrouter, channel, team): def subprocess_channel_topic(message_json, eventrouter, channel, team): text = unfurl_refs(message_json["text"], ignore_alt_text=False) - channel.buffer_prnt(w.prefix("network").rstrip(), text, message_json["ts"]) + channel.buffer_prnt(w.prefix("network").rstrip(), text, message_json["ts"], tagset="muted") channel.render_topic(message_json["topic"]) def process_reply(message_json, eventrouter, **kwargs): -- cgit From 88cafdde3337c29c94e54da143bf81db0e489448 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 11:45:45 -0800 Subject: use purpose if no topic --- wee_slack.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index f55d467..eef9c0f 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -953,6 +953,7 @@ class SlackChannel(object): self.eventrouter = eventrouter self.slack_name = kwargs["name"] self.slack_topic = kwargs.get("topic", {"value": ""}) + self.slack_purpose = kwargs.get("purpose", {"value": ""}) self.identifier = kwargs["id"] self.last_read = SlackTS(kwargs.get("last_read", SlackTS())) #print self.last_read @@ -1002,7 +1003,10 @@ class SlackChannel(object): def render_topic(self, topic=None): if self.channel_buffer: if not topic: - encoded_topic = self.slack_topic['value'].encode('utf-8') + if self.slack_topic['value'] != "": + encoded_topic = self.slack_topic['value'].encode('utf-8') + else: + encoded_topic = self.slack_purpose['value'].encode('utf-8') else: encoded_topic = topic.encode('utf-8') self.encoded_topic = topic.encode('utf-8') -- cgit From 9ac727d3193beacdb7257206e44abc76780bf449 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 11:54:39 -0800 Subject: set read messages marker on other tag types too --- wee_slack.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wee_slack.py b/wee_slack.py index eef9c0f..da64233 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1113,6 +1113,7 @@ class SlackChannel(object): backlog = True if ts <= last_read else False if tagset: tags = tag(tagset, user=tag_nick) + self.new_messages = True #we have to infer the tagset because we weren't told elif ts <= last_read: -- cgit From 555060088507cfe26197f98ee9b983131a8ac98c Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:02:13 -0800 Subject: add decorators --- wee_slack.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index da64233..313fcf3 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2526,6 +2526,7 @@ def rehistory_command_callback(data, current_buffer, args): channel.get_history() return w.WEECHAT_RC_OK_EAT +@slack_buffer_required def hide_command_callback(data, current_buffer, args): c = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) if c: @@ -2534,6 +2535,7 @@ def hide_command_callback(data, current_buffer, args): w.buffer_set(c.channel_buffer, "hidden", "1") return w.WEECHAT_RC_OK_EAT +@slack_buffer_required def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) if len(a) > 1: @@ -2547,6 +2549,7 @@ def slack_command_cb(data, current_buffer, args): w.prnt("", "Command not found: " + function_name) return w.WEECHAT_RC_OK +@slack_buffer_required def command_distracting(data, current_buffer, args): channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) if channel: @@ -2557,9 +2560,11 @@ def command_distracting(data, current_buffer, args): config.distracting_channels.pop(config.distracting_channels.index(fullname)) save_distracting_channels() +@slack_buffer_required def save_distracting_channels(): w.config_set_plugin('distracting_channels', ','.join(config.distracting_channels)) +@slack_buffer_required def command_mute(data, current_buffer, args): current = w.current_buffer() channel_id = EVENTROUTER.weechat_controller.buffers[current].identifier @@ -2571,6 +2576,7 @@ def command_mute(data, current_buffer, args): s = SlackRequest(team.token, "users.prefs.set", {"name": "muted_channels", "value": ",".join(team.muted_channels)}, team_hash=team.team_hash, channel_identifier=channel_id) EVENTROUTER.receive(s) +@slack_buffer_required def command_openweb(data, current_buffer, args): #if done from server buffer, open slack for reals channel = EVENTROUTER.weechat_controller.buffers[current_buffer] @@ -2581,6 +2587,7 @@ def command_openweb(data, current_buffer, args): url = "https://{}/archives/{}/p{}000000".format(channel.team.domain, channel.slack_name, now.majorstr()) w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) +@slack_buffer_required def command_nodistractions(data, current_buffer, args): global hide_distractions hide_distractions = not hide_distractions @@ -2597,6 +2604,7 @@ def command_nodistractions(data, current_buffer, args): # config.distracting_channels.pop(config.distracting_channels.index(channel)) # save_distracting_channels() +@slack_buffer_required def label_command_cb(data, current_buffer, args): channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer) if channel and channel.type == 'thread': @@ -2604,6 +2612,7 @@ def label_command_cb(data, current_buffer, args): new_name = " +" + aargs[1] w.buffer_set(channel.channel_buffer, "short_name", new_name) +@slack_buffer_required def command_p(data, current_buffer, args): w.prnt("", "{}".format(eval(args))) -- cgit From 48fc813529fda4fcb4dfd2c048538a413c118b49 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:12:35 -0800 Subject: no slack buffer needed here pal --- wee_slack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 313fcf3..866e77b 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2587,7 +2587,6 @@ def command_openweb(data, current_buffer, args): url = "https://{}/archives/{}/p{}000000".format(channel.team.domain, channel.slack_name, now.majorstr()) w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) -@slack_buffer_required def command_nodistractions(data, current_buffer, args): global hide_distractions hide_distractions = not hide_distractions -- cgit From 41418006a3b4daae49e8a9c633515cfeb6aac56f Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:15:18 -0800 Subject: nor here, pal --- wee_slack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 866e77b..aa42ff6 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2535,7 +2535,6 @@ def hide_command_callback(data, current_buffer, args): w.buffer_set(c.channel_buffer, "hidden", "1") return w.WEECHAT_RC_OK_EAT -@slack_buffer_required def slack_command_cb(data, current_buffer, args): a = args.split(' ', 1) if len(a) > 1: -- cgit From ea9cce52458e6336e5c61dbf4308b6cb47a6c070 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:40:09 -0800 Subject: i'm an idiot --- wee_slack.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index aa42ff6..7d01d17 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1074,18 +1074,19 @@ class SlackChannel(object): self.eventrouter.weechat_controller.set_refresh_buffer_list(True) #else: # self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) - try: - if self.unread_count_display != 0: - for c in range(0, self.unread_count_display): + try: + for c in range(self.unread_count_display): if self.type == "im": + print self.channel_buffer w.buffer_set(self.channel_buffer, "hotlist", "2") else: + print self.channel_buffer w.buffer_set(self.channel_buffer, "hotlist", "1") - else: + else: + pass + #dbg("no unread in {}".format(self.name)) + except: pass - #dbg("no unread in {}".format(self.name)) - except: - pass self.update_nicklist() #dbg("exception no unread count") @@ -1898,7 +1899,10 @@ def process_message(message_json, eventrouter, store=True, **kwargs): text = text[1:-1] if message.sender != channel.team.nick: text = message.sender + " " + text - channel.unread_count_display += 1 + try: + channel.unread_count_display += 1 + except: + channel.unread_count_display += 1 channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, tag_nick=message.sender_plain, **kwargs) else: -- cgit From 09f442e4394ba45df5be588525548e3e0ce40e1e Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:42:35 -0800 Subject: fixup --- wee_slack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 7d01d17..46e1481 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2695,9 +2695,9 @@ def setup_hooks(): '|'.join(cmds.keys()), # Function name 'slack_command_cb', '') - w.hook_command('me', 'me_command_cb', '') - w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') + #w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') + w.hook_command_run('me', 'me_command_cb', '') w.hook_command_run('/query', 'command_talk', '') w.hook_command_run('/join', 'command_talk', '') w.hook_command_run('/part', 'part_command_cb', '') -- cgit From 1ca07764527724c859462e64f9db8232df27fe93 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:43:26 -0800 Subject: fix me --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 46e1481..987c049 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2697,7 +2697,7 @@ def setup_hooks(): 'slack_command_cb', '') #w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') - w.hook_command_run('me', 'me_command_cb', '') + w.hook_command_run('/me', 'me_command_cb', '') w.hook_command_run('/query', 'command_talk', '') w.hook_command_run('/join', 'command_talk', '') w.hook_command_run('/part', 'part_command_cb', '') -- cgit From f274fa72adb75a5462b6e8aa06e9f49680e26c37 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:45:12 -0800 Subject: better --- wee_slack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 987c049..70e208c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2442,8 +2442,8 @@ def command_topic(data, current_buffer, args): return w.WEECHAT_RC_ERROR_EAT @slack_buffer_or_ignore -def me_command_cb(data, current_buffer, arg): - message = "_{}_".format(arg) +def me_command_cb(data, current_buffer, args): + message = "_{}_".format(args[1:]) buffer_input_callback("EVENTROUTER", current_buffer, message) return w.WEECHAT_RC_OK -- cgit From ccff5c673b20693e74ec669091f1b84bff060c81 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:47:30 -0800 Subject: another EAT needed --- wee_slack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 70e208c..14b1a4e 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2443,9 +2443,9 @@ def command_topic(data, current_buffer, args): @slack_buffer_or_ignore def me_command_cb(data, current_buffer, args): - message = "_{}_".format(args[1:]) + message = "_{}_".format(args) buffer_input_callback("EVENTROUTER", current_buffer, message) - return w.WEECHAT_RC_OK + return w.WEECHAT_RC_OK_EAT @slack_buffer_or_ignore def msg_command_cb(data, current_buffer, args): -- cgit From 8cd18f2e67d00d6171b79e823fcf50565f6cddf0 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 12:50:58 -0800 Subject: .. --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 14b1a4e..459ce7f 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2443,7 +2443,7 @@ def command_topic(data, current_buffer, args): @slack_buffer_or_ignore def me_command_cb(data, current_buffer, args): - message = "_{}_".format(args) + message = "_{}_".format(args.split(' ', 1)[1]) buffer_input_callback("EVENTROUTER", current_buffer, message) return w.WEECHAT_RC_OK_EAT -- cgit From a712af40d808ba393ebfea29f01c9ffc4f9909b7 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 13:36:04 -0800 Subject: ignore thread messages --- wee_slack.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 459ce7f..13d1f93 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1212,7 +1212,11 @@ class SlackChannel(object): s = SlackRequest(self.team.token, method, data) self.eventrouter.receive(s) def sorted_message_keys(self): - return sorted(self.messages) + keys = [] + for k in self.messages: + if type(self.messages[k]) == SlackMessage: + keys.append(k) + return sorted(keys) # Typing related def set_typing(self, user): if self.channel_buffer and self.is_visible(): -- cgit From a0ee784b18c7096c2beef9294a61dc4e850b3aa4 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 13:37:22 -0800 Subject: debug bye --- wee_slack.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 13d1f93..cca88a6 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1077,10 +1077,8 @@ class SlackChannel(object): try: for c in range(self.unread_count_display): if self.type == "im": - print self.channel_buffer w.buffer_set(self.channel_buffer, "hotlist", "2") else: - print self.channel_buffer w.buffer_set(self.channel_buffer, "hotlist", "1") else: pass -- cgit From 8268c049c37668ef719d8dc917e8d02a6f3673e2 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 14:05:40 -0800 Subject: fix debugger --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index cca88a6..8e872d2 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2616,8 +2616,8 @@ def label_command_cb(data, current_buffer, args): new_name = " +" + aargs[1] w.buffer_set(channel.channel_buffer, "short_name", new_name) -@slack_buffer_required def command_p(data, current_buffer, args): + args = args.split(' ', 1)[1] w.prnt("", "{}".format(eval(args))) ###### NEW EXCEPTIONS -- cgit From efe97756955e89ea31f70ad0d3cbd53d134b0126 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 9 Feb 2017 17:45:47 -0800 Subject: fuck python 2 unicode so much --- wee_slack.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 8e872d2..53efbb4 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2009,6 +2009,9 @@ def subprocess_message_deleted(message_json, eventrouter, channel, team): def subprocess_channel_topic(message_json, eventrouter, channel, team): text = unfurl_refs(message_json["text"], ignore_alt_text=False) + if type(text) != unicode: + text = text.decode('utf-8', 'ignore') + text = text.encode('utf-8') channel.buffer_prnt(w.prefix("network").rstrip(), text, message_json["ts"], tagset="muted") channel.render_topic(message_json["topic"]) -- cgit From 190e8da85c582d9f387fa321395e95287b32b40a Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 10 Feb 2017 08:32:06 -0800 Subject: also check that it even has .members --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 53efbb4..a192881 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -705,7 +705,7 @@ def complete_next_cb(data, current_buffer, command): current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) #channel = channels.find(current_buffer) - if current_channel is None or current_channel.members is None: + if not hasattr(current_channel, 'members') or current_channel is None or current_channel.members is None: return w.WEECHAT_RC_OK line_input = w.buffer_get_string(current_buffer, "input") -- cgit From e5b09f71c9281e4c6cf9c2937382ed581d54be19 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 10 Feb 2017 10:23:51 -0800 Subject: fix slash commands --- wee_slack.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index a192881..c139692 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2476,9 +2476,7 @@ def command_talk(data, current_buffer, args): """ e = EVENTROUTER team = e.weechat_controller.buffers[current_buffer].team - dbg(team, 5) channel_name = args.split(' ')[1] - dbg(channel_name, 5) c = team.get_channel_map() if channel_name not in c: u = team.get_username_map() @@ -2572,6 +2570,28 @@ def command_distracting(data, current_buffer, args): def save_distracting_channels(): w.config_set_plugin('distracting_channels', ','.join(config.distracting_channels)) +@slack_buffer_required +def command_slash(data, current_buffer, args): + """ + Support for custom slack commands + /slack slash /customcommand arg1 arg2 arg3 + """ + e = EVENTROUTER + channel = e.weechat_controller.buffers.get(current_buffer, None) + if channel: + team = channel.team + + if args is None: + server.buffer_prnt("Usage: /slack slash /someslashcommand [arguments...].") + return + + split_args = args.split(None, 2) + command = split_args[1] + text = split_args[2] if len(split_args) > 2 else "" + + s = SlackRequest(team.token, "chat.command", {"command": command, "text": text, 'channel': channel.identifier}, team_hash=team.team_hash, channel_identifier=channel.identifier) + EVENTROUTER.receive(s) + @slack_buffer_required def command_mute(data, current_buffer, args): current = w.current_buffer() -- cgit From 175c0646f406979b90fd771f5e1038de07ac312a Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 10 Feb 2017 13:53:47 -0800 Subject: aliases are back --- wee_slack.py | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index c139692..b34bae3 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -849,8 +849,15 @@ class SlackTeam(object): # self.eventrouter.weechat_controller.unregister_buffer(self.channel_buffer, update_remote=False, close_buffer=True) def create_buffer(self): if not self.channel_buffer: - bname = self.subdomain if config.short_buffer_names else self.domain - self.channel_buffer = w.buffer_new("{}".format(bname), "buffer_input_callback", "EVENTROUTER", "", "") + if config.short_buffer_names: + self.preferred_name = self.subdomain + elif config.server_aliases not in ['', None]: + name = config.server_aliases.get(self.subdomain, None) + if name: + self.preferred_name = name + else: + self.preferred_name = self.domain + self.channel_buffer = w.buffer_new("{}".format(self.preferred_name), "buffer_input_callback", "EVENTROUTER", "", "") self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) w.buffer_set(self.channel_buffer, "localvar_set_type", 'server') if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': @@ -996,8 +1003,8 @@ class SlackChannel(object): "default": prepend + self.slack_name, "sidebar": prepend + self.slack_name, "base": self.slack_name, - "long_default": "{}.{}{}".format(self.team.domain, prepend, self.slack_name), - "long_base": "{}.{}".format(self.team.domain, self.slack_name), + "long_default": "{}.{}{}".format(self.team.preferred_name, prepend, self.slack_name), + "long_base": "{}.{}".format(self.team.preferred_name, self.slack_name), } return select[style] def render_topic(self, topic=None): @@ -1067,11 +1074,12 @@ class SlackChannel(object): w.buffer_set(self.channel_buffer, "localvar_set_channel", self.formatted_name()) w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) self.render_topic() - #if self.server.alias: - # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) - #else: - # w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + if self.channel_buffer: + #if self.team.server_alias: + #w.buffer_set(self.channel_buffer, "localvar_set_server", self.team.server_alias) + #else: + w.buffer_set(self.channel_buffer, "localvar_set_server", self.team.preferred_name) #else: # self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) try: @@ -1351,8 +1359,8 @@ class SlackDMChannel(SlackChannel): "default": self.slack_name, "sidebar": prepend + self.slack_name, "base": self.slack_name, - "long_default": "{}.{}".format(self.team.domain, self.slack_name), - "long_base": "{}.{}".format(self.team.domain, self.slack_name), + "long_default": "{}.{}".format(self.team.preferred_name, self.slack_name), + "long_base": "{}.{}".format(self.team.preferred_name, self.slack_name), } return print_color + select[style] def open(self, update_remote=True): @@ -1415,8 +1423,8 @@ class SlackMPDMChannel(SlackChannel): "default": adjusted_name, "sidebar": prepend + adjusted_name, "base": adjusted_name, - "long_default": "{}.{}".format(self.team.domain, adjusted_name), - "long_base": "{}.{}".format(self.team.domain, adjusted_name), + "long_default": "{}.{}".format(self.team.preferred_name, adjusted_name), + "long_base": "{}.{}".format(self.team.preferred_name, adjusted_name), } return select[style] @@ -2800,6 +2808,7 @@ class PluginConfig(object): 'short_buffer_names': 'false', 'background_load_all_history': 'false', 'never_away': 'false', + 'server_aliases': '', } # Set missing settings to their defaults. Load non-missing settings from @@ -2840,6 +2849,11 @@ class PluginConfig(object): def get_distracting_channels(self, key): return [x.strip() for x in w.config_get_plugin(key).split(',')] + def get_server_aliases(self, key): + alias_list = w.config_get_plugin(key) + if len(alias_list) > 0: + return dict(item.split(":") for item in alias_list.split(",")) + def get_slack_api_token(self, key): token = w.config_get_plugin("slack_api_token") if token.startswith('${sec.data'): -- cgit From 19ab82ef55f530506d70e40ec54fa89f3ab75767 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 10 Feb 2017 14:18:49 -0800 Subject: fix chicken v egg --- wee_slack.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wee_slack.py b/wee_slack.py index b34bae3..39cfcc0 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -806,6 +806,7 @@ class SlackTeam(object): self.team = self self.subdomain = subdomain self.domain = subdomain + ".slack.com" + self.preferred_name = self.domain self.nick = nick self.myidentifier = myidentifier try: -- cgit From 6411a77a3e85681b117c623e9143870970e6e8e1 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Fri, 10 Feb 2017 15:02:22 -0800 Subject: handle DM channels created --- wee_slack.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 39cfcc0..406400a 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2089,6 +2089,12 @@ def process_channel_rename(message_json, eventrouter, **kwargs): channel = kwargs['team'].channels[item["id"]] channel.slack_name = message_json['channel']['name'] +def process_im_created(message_json, eventrouter, **kwargs): + team = kwargs['team'] + item = message_json["channel"] + c = SlackDMChannel(eventrouter, team=team, users=team.users, **item) + team.channels[item["id"]] = c + def process_im_open(message_json, eventrouter, **kwargs): channel = kwargs['channel'] item = message_json -- cgit From 2bc87a60a44e9b59f8ba3ddae7f43ef661a9f370 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 13 Feb 2017 13:43:25 -0800 Subject: derp --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 406400a..79e9bbc 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1114,7 +1114,7 @@ class SlackChannel(object): ts = SlackTS(timestamp) last_read = SlackTS(self.last_read) #without this, DMs won't open automatically - if not self.channel_buffer and ts <= last_read: + if not self.channel_buffer and ts >= last_read: self.open(update_remote=False) if self.channel_buffer: #backlog messages - we will update the read marker as we print these -- cgit From d7e42876fac7e5797215023d8e46ae09724c6602 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 13 Feb 2017 13:44:24 -0800 Subject: only new new --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 79e9bbc..637e0f5 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1114,7 +1114,7 @@ class SlackChannel(object): ts = SlackTS(timestamp) last_read = SlackTS(self.last_read) #without this, DMs won't open automatically - if not self.channel_buffer and ts >= last_read: + if not self.channel_buffer and ts > last_read: self.open(update_remote=False) if self.channel_buffer: #backlog messages - we will update the read marker as we print these -- cgit From c3078c51e618956240f0dc9a6472b3a022c7550d Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Tue, 21 Feb 2017 16:18:14 -0800 Subject: fix new private group/channel creation --- wee_slack.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 637e0f5..8a4917d 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2106,6 +2106,15 @@ def process_im_close(message_json, eventrouter, **kwargs): cbuf = kwargs['team'].channels[item["channel"]].channel_buffer eventrouter.weechat_controller.unregister_buffer(cbuf, False, True) +def process_group_joined(message_json, eventrouter, **kwargs): + item = message_json["channel"] + if item["name"].startswith("mpdm-"): + c = SlackMPDMChannel(eventrouter, team=kwargs["team"], **item) + else: + c = SlackGroupChannel(eventrouter, team=kwargs["team"], **item) + kwargs['team'].channels[item["id"]] = c + kwargs['team'].channels[item["id"]].open() + def process_reaction_added(message_json, eventrouter, **kwargs): channel = kwargs['team'].channels[message_json["item"]["channel"]] if message_json["item"].get("type") == "message": -- cgit From 3b22dd58211294833dd6e0ca6ae770cfccf5c62e Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Tue, 21 Feb 2017 16:28:31 -0800 Subject: print notification of new channels in server buffer --- wee_slack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 8a4917d..96b974b 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2081,8 +2081,7 @@ def process_channel_created(message_json, eventrouter, **kwargs): item = message_json["channel"] c = SlackChannel(eventrouter, team=kwargs["team"], **item) kwargs['team'].channels[item["id"]] = c - #print eventrouter.teams['d80c2b6c3127dbb1991917394ed219e8212a2606'].channels['C3ZM2GMGU'].team.domain - #raise + kwargs['team'].buffer_prnt('Channel created: {}'.format(c.slack_name)) def process_channel_rename(message_json, eventrouter, **kwargs): item = message_json["channel"] @@ -2094,6 +2093,7 @@ def process_im_created(message_json, eventrouter, **kwargs): item = message_json["channel"] c = SlackDMChannel(eventrouter, team=team, users=team.users, **item) team.channels[item["id"]] = c + kwargs['team'].buffer_prnt('IM channel created: {}'.format(c.name)) def process_im_open(message_json, eventrouter, **kwargs): channel = kwargs['channel'] -- cgit From 1b5fcb47332b40906424c2cdb04db263e7ebbaa4 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Tue, 21 Feb 2017 17:06:00 -0800 Subject: file upload returns --- wee_slack.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 96b974b..62a9916 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2655,6 +2655,19 @@ def command_nodistractions(data, current_buffer, args): # config.distracting_channels.pop(config.distracting_channels.index(channel)) # save_distracting_channels() +@slack_buffer_required +def command_upload(data, current_buffer, args): + channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer) + url = 'https://slack.com/api/files.upload' + fname = args.split(' ', 1) + file_path = os.path.expanduser(fname[1]) + team = EVENTROUTER.weechat_controller.buffers[current_buffer].team + if ' ' in file_path: + file_path = file_path.replace(' ', '\ ') + + command = 'curl -F file=@{} -F channels={} -F token={} {}'.format(file_path, channel.identifier, team.token, url) + w.hook_process(command, config.slack_timeout, '', '') + @slack_buffer_required def label_command_cb(data, current_buffer, args): channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer) -- cgit From bfcd9dbb0a71b76a598b2ad46fc02abb098fc80d Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Tue, 21 Feb 2017 17:26:26 -0800 Subject: put back away/back --- wee_slack.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 62a9916..218cb48 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2668,6 +2668,36 @@ def command_upload(data, current_buffer, args): command = 'curl -F file=@{} -F channels={} -F token={} {}'.format(file_path, channel.identifier, team.token, url) w.hook_process(command, config.slack_timeout, '', '') +def away_command_cb(data, current_buffer, args): + #TODO: reimplement all.. maybe + (all, message) = re.match("^/away(?:\s+(-all))?(?:\s+(.+))?", args).groups() + if message is None: + command_back(data, current_buffer, args) + else: + command_away(data, current_buffer, args) + return w.WEECHAT_RC_OK + +@slack_buffer_required +def command_away(data, current_buffer, args): + """ + Sets your status as 'away' + /slack away + """ + team = EVENTROUTER.weechat_controller.buffers[current_buffer].team + s = SlackRequest(team.token, "presence.set", {"presence": "away"}, team_hash=team.team_hash) + EVENTROUTER.receive(s) + + +@slack_buffer_required +def command_back(data, current_buffer, args): + """ + Sets your status as 'back' + /slack back + """ + team = EVENTROUTER.weechat_controller.buffers[current_buffer].team + s = SlackRequest(team.token, "presence.set", {"presence": "active"}, team_hash=team.team_hash) + EVENTROUTER.receive(s) + @slack_buffer_required def label_command_cb(data, current_buffer, args): channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer) @@ -2772,12 +2802,12 @@ def setup_hooks(): w.hook_command_run('/msg', 'msg_command_cb', '') w.hook_command_run('/label', 'label_command_cb', '') w.hook_command_run("/input complete_next", "complete_next_cb", "") + w.hook_command_run('/away', 'away_command_cb', '') w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") w.hook_completion("emoji", "complete :emoji: for slack", "emoji_completion_cb", "") # Hooks to fix/implement - #w.hook_command_run('/away', 'away_command_cb', '') #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") #w.hook_signal('buffer_opened', "buffer_opened_cb", "") #w.hook_signal('window_scrolled', "scrolled_cb", "") -- cgit From 14f8abeac85dcb69cf37efc10ff9d5754f051630 Mon Sep 17 00:00:00 2001 From: Wolf480pl Date: Wed, 22 Feb 2017 15:29:18 +0100 Subject: Add sha1 thread IDs --- wee_slack.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 218cb48..971baa3 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -969,6 +969,7 @@ class SlackChannel(object): self.team = kwargs.get('team', None) self.got_history = False self.messages = {} + self.hashed_messages = {} self.new_messages = False self.typing = {} self.type = 'channel' @@ -1103,6 +1104,7 @@ class SlackChannel(object): if self.channel_buffer is not None: self.channel_buffer = None self.messages = {} + self.hashed_messages = {} self.got_history = False #if update_remote and not eventrouter.shutting_down: self.active = False @@ -1320,6 +1322,14 @@ class SlackChannel(object): else: for fn in ["1| too", "2| many", "3| users", "4| to", "5| show"]: w.nicklist_add_group(self.channel_buffer, '', fn, w.color('white'), 1) + def hash_message(self, ts): + sts = SlackTS(ts) + if sts in self.messages: + message = self.messages[sts] + if not message.hash: + tshash = sha.sha(ts).hexdigest() + self.hashed_messages[tshash[:3]] = message + message.hash = tshash[:3] class SlackDMChannel(SlackChannel): @@ -1451,10 +1461,11 @@ class SlackThreadChannel(object): #def set_name(self, slack_name): # self.name = "#" + slack_name def formatted_name(self, style="default", **kwargs): + hash_or_ts = self.parent_message.hash or self.parent_message.ts styles = { - "default": " +{}".format(self.parent_message.ts), - "long_default": "{}.{}".format(self.parent_message.channel.formatted_name(style="long_default"), self.parent_message.ts), - "sidebar": " +{}".format(self.parent_message.ts), + "default": " +{}".format(hash_or_ts), + "long_default": "{}.{}".format(self.parent_message.channel.formatted_name(style="long_default"), hash_or_ts), + "sidebar": " +{}".format(hash_or_ts), } return styles[style] def refresh(self): @@ -1611,6 +1622,7 @@ class SlackMessage(object): self.message_json = message_json self.submessages = [] self.thread_channel = None + self.hash = None if override_sender: self.sender = override_sender self.sender_plain = override_sender @@ -1623,7 +1635,7 @@ class SlackMessage(object): return hash(self.ts) def render(self, force=False): if len(self.submessages) > 0: - return "{} {} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix, "{}[ Thread: {} Replies: {} ]".format(w.color(config.thread_suffix_color), self.ts, len(self.submessages))) + return "{} {} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix, "{}[ Thread: {} Replies: {} ]".format(w.color(config.thread_suffix_color), self.hash or self.ts, len(self.submessages))) return "{} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix) def change_text(self, new_text): self.message_json["text"] = new_text @@ -1943,6 +1955,7 @@ def subprocess_thread_message(message_json, eventrouter, channel, team): if parent_message: message = SlackThreadMessage(parent_ts, message_json, team, channel) parent_message.submessages.append(message) + channel.hash_message(parent_ts) channel.store_message(message, team) channel.change_message(parent_ts) @@ -2532,7 +2545,10 @@ def thread_command_callback(data, current_buffer, args): args = args.split() if args[0] == '/thread': if len(args) == 2: - pm = channel.messages[SlackTS(args[1])] + try: + pm = channel.messages[SlackTS(args[1])] + except: + pm = channel.hashed_messages[args[1]] tc = SlackThreadChannel(EVENTROUTER, pm) pm.thread_channel = tc tc.open() -- cgit From dc5097c8b3b6a82442c6d5637a62a946b4dfc21c Mon Sep 17 00:00:00 2001 From: Wolf480pl Date: Wed, 22 Feb 2017 16:40:41 +0100 Subject: Avoid hash collisions by taking more hexdigits as necessary --- wee_slack.py | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 971baa3..05f4785 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1323,13 +1323,30 @@ class SlackChannel(object): for fn in ["1| too", "2| many", "3| users", "4| to", "5| show"]: w.nicklist_add_group(self.channel_buffer, '', fn, w.color('white'), 1) def hash_message(self, ts): - sts = SlackTS(ts) - if sts in self.messages: - message = self.messages[sts] - if not message.hash: - tshash = sha.sha(ts).hexdigest() - self.hashed_messages[tshash[:3]] = message - message.hash = tshash[:3] + ts = SlackTS(ts) + def calc_hash(msg): + return sha.sha(str(msg.ts)).hexdigest() + + if ts in self.messages and not self.messages[ts].hash: + message = self.messages[ts] + tshash = calc_hash(message) + l = 3 + shorthash = tshash[:l] + while any(x.startswith(shorthash) for x in self.hashed_messages): + l += 1 + shorthash = tshash[:l] + + if shorthash[:-1] in self.hashed_messages: + col_msg = self.hashed_messages.pop(shorthash[:-1]) + col_new_hash = calc_hash(col_msg)[:l] + col_msg.hash = col_new_hash + self.hashed_messages[col_new_hash] = col_msg + self.change_message(str(col_msg.ts)) + if col_msg.thread_channel: + col_msg.thread_channel.rename() + + self.hashed_messages[shorthash] = message + message.hash = shorthash class SlackDMChannel(SlackChannel): @@ -1457,6 +1474,7 @@ class SlackThreadChannel(object): #self.name = "#" + kwargs['name'] self.type = "thread" self.got_history = False + self.label = None #self.set_name(self.slack_name) #def set_name(self, slack_name): # self.name = "#" + slack_name @@ -1469,7 +1487,7 @@ class SlackThreadChannel(object): } return styles[style] def refresh(self): - pass + self.rename() def mark_read(self, ts=None, update_remote=True, force=False): if self.channel_buffer: w.buffer_set(self.channel_buffer, "unread", "") @@ -1532,6 +1550,10 @@ class SlackThreadChannel(object): # self.eventrouter.receive(s) self.create_buffer() + def rename(self): + if self.channel_buffer and not self.label: + w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) + def create_buffer(self): """ incomplete (muted doesn't work) @@ -2720,6 +2742,7 @@ def label_command_cb(data, current_buffer, args): if channel and channel.type == 'thread': aargs = args.split(None, 2) new_name = " +" + aargs[1] + channel.label = new_name w.buffer_set(channel.channel_buffer, "short_name", new_name) def command_p(data, current_buffer, args): -- cgit From 429dfedb0403a9ec51af171513b5b3d9fbe8f1a6 Mon Sep 17 00:00:00 2001 From: Wolf480pl Date: Wed, 22 Feb 2017 15:29:40 +0100 Subject: Set the thread channel's topic to the parent message --- wee_slack.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index 05f4785..a74dab4 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1565,6 +1565,10 @@ class SlackThreadChannel(object): w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') w.buffer_set(self.channel_buffer, "localvar_set_channel", self.formatted_name()) w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) + time_format = w.config_string(w.config_get("weechat.look.buffer_time_format")) + parent_time = time.localtime(SlackTS(self.parent_message.ts).major) + topic = '{} {} | {}'.format(time.strftime(time_format, parent_time), self.parent_message.sender, self.parent_message.render() ) + w.buffer_set(self.channel_buffer, "title", topic.encode('utf-8')) #self.eventrouter.weechat_controller.set_refresh_buffer_list(True) -- cgit From 22b00e02d737bab4086b70b6f73ec5ef0bf54a2a Mon Sep 17 00:00:00 2001 From: Wolf480pl Date: Wed, 22 Feb 2017 18:30:46 +0100 Subject: Fix a memleak --- wee_slack.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index a74dab4..d81659c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1166,6 +1166,9 @@ class SlackChannel(object): mk = self.messages.keys() mk.sort() for k in mk[:SCROLLBACK_SIZE]: + msg_to_delete = self.messages[k] + if msg_to_delete.hash: + del self.hashed_messages[msg_to_delete.hash] del self.messages[k] def change_message(self, ts, text=None, suffix=None): ts = SlackTS(ts) -- cgit From 10cc1908d064256f35560fd1444d97fad6bf43cf Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 22 Feb 2017 11:38:43 -0800 Subject: allow disabling of channel name typing indicator --- wee_slack.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 218cb48..9bdb73c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -996,10 +996,13 @@ class SlackChannel(object): return True return False def formatted_name(self, style="default", typing=False, **kwargs): - if not typing: - prepend = "#" + if config.channel_name_typing_indicator: + if not typing: + prepend = "#" + else: + prepend = ">" else: - prepend = ">" + prepend = "#" select = { "default": prepend + self.slack_name, "sidebar": prepend + self.slack_name, @@ -1416,10 +1419,13 @@ class SlackMPDMChannel(SlackChannel): self.name = "|".join("-".join(n.split("-")[1:-1]).split("--")) def formatted_name(self, style="default", typing=False, **kwargs): adjusted_name = "|".join("-".join(self.slack_name.split("-")[1:-1]).split("--")) - if not typing: - prepend = " " + if config.channel_name_typing_indicator: + if not typing: + prepend = "#" + else: + prepend = ">" else: - prepend = ">" + prepend = "#" select = { "default": adjusted_name, "sidebar": prepend + adjusted_name, @@ -2865,6 +2871,7 @@ class PluginConfig(object): 'thread_suffix_color': 'lightcyan', 'unhide_buffers_with_activity': 'false', 'short_buffer_names': 'false', + 'channel_name_typing_indicator': 'true', 'background_load_all_history': 'false', 'never_away': 'false', 'server_aliases': '', -- cgit From 17ad47f47a5e84901f928e558baca151a0c3f27a Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 22 Feb 2017 11:49:45 -0800 Subject: stop linter complaint --- wee_slack.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wee_slack.py b/wee_slack.py index 8e56ce1..bee0c12 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1330,6 +1330,7 @@ class SlackChannel(object): w.nicklist_add_group(self.channel_buffer, '', fn, w.color('white'), 1) def hash_message(self, ts): ts = SlackTS(ts) + def calc_hash(msg): return sha.sha(str(msg.ts)).hexdigest() -- cgit From 965eac76e523b11c73470e7971c3a0acdac56ec3 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Thu, 23 Feb 2017 09:16:03 -0800 Subject: remove unnecessary check --- wee_slack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index bee0c12..cb746ce 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2642,7 +2642,6 @@ def command_distracting(data, current_buffer, args): config.distracting_channels.pop(config.distracting_channels.index(fullname)) save_distracting_channels() -@slack_buffer_required def save_distracting_channels(): w.config_set_plugin('distracting_channels', ','.join(config.distracting_channels)) -- cgit From c31dee0b3ffcc3f2065869bc39456af907a4c9a4 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 27 Mar 2017 15:19:25 +0000 Subject: don't create a new channel ugh --- wee_slack.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wee_slack.py b/wee_slack.py index cb746ce..44e848a 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1446,6 +1446,14 @@ class SlackMPDMChannel(SlackChannel): n = kwargs.get('name') self.set_name(n) self.type = "group" + def open(self, update_remote=False): + self.create_buffer() + self.active = True + self.get_history() + if "info" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + #self.create_buffer() def set_name(self, n): self.name = "|".join("-".join(n.split("-")[1:-1]).split("--")) def formatted_name(self, style="default", typing=False, **kwargs): -- cgit From eac914db38774444718923f3fe8a8d8b2fc758bf Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 27 Mar 2017 19:00:52 +0000 Subject: fix broken linkifying wrong-ly --- wee_slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index 44e848a..0157306 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2247,7 +2247,7 @@ def linkify_text(message, team, channel): channels = team.get_channel_map() message = message.split(' ') for item in enumerate(message): - targets = re.match('.*([@#])([\w.-]+[\w. -])(\W*)', item[1]) + targets = re.match('^\s*([@#])([\w.-]+[\w. -])(\W*)', item[1]) #print targets if targets and targets.groups()[0] == '@': #print targets.groups() -- cgit From 93cf1fb9ca4e2ceaedb35605ed6686e032fbcf03 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 27 Mar 2017 19:17:16 +0000 Subject: fix here/group/channel --- wee_slack.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index 0157306..a2f472a 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2253,12 +2253,14 @@ def linkify_text(message, team, channel): #print targets.groups() named = targets.groups() if named[1] in ["group", "channel", "here"]: + dbg(named[1], 5) message[item[0]] = "".format(named[1]) - try: - if usernames[named[1]]: - message[item[0]] = "<@{}>{}".format(usernames[named[1]], named[2]) - except: - message[item[0]] = "@{}{}".format(named[1], named[2]) + else: + try: + if usernames[named[1]]: + message[item[0]] = "<@{}>{}".format(usernames[named[1]], named[2]) + except: + message[item[0]] = "@{}{}".format(named[1], named[2]) if targets and targets.groups()[0] == '#': named = targets.groups() try: -- cgit From aebb3dab02675c9b85c698616305cc6082939a81 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Mon, 27 Mar 2017 19:17:39 +0000 Subject: remove dbg --- wee_slack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wee_slack.py b/wee_slack.py index a2f472a..cc19def 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -2253,7 +2253,6 @@ def linkify_text(message, team, channel): #print targets.groups() named = targets.groups() if named[1] in ["group", "channel", "here"]: - dbg(named[1], 5) message[item[0]] = "".format(named[1]) else: try: -- cgit From 82f6df320adc0d2b3d6c4d6cd1e949ad427ed29f Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 5 Apr 2017 18:28:40 +0000 Subject: much much better handling of read markers --- wee_slack.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wee_slack.py b/wee_slack.py index cc19def..e8b736c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -551,7 +551,7 @@ def buffer_input_callback(signal, buffer_ptr, data): channel.edit_previous_message(old.decode("utf-8"), new.decode("utf-8"), flags) else: channel.send_message(data) - channel.mark_read(update_remote=True, force=True) + #this is probably wrong channel.mark_read(update_remote=True, force=True) return w.WEECHAT_RC_ERROR def buffer_switch_callback(signal, sig_type, data): @@ -1158,7 +1158,7 @@ class SlackChannel(object): request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} request.update(request_dict_ext) self.team.send_to_websocket(request) - self.mark_read(force=True) + self.mark_read(update_remote=False, force=True) def store_message(self, message, team, from_me=False): if not self.active: return @@ -1276,7 +1276,7 @@ class SlackChannel(object): if update_remote: s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": ts}, team_hash=self.team.team_hash, channel_identifier=self.identifier) self.eventrouter.receive(s) - self.new_messages = False + self.new_messages = False def user_joined(self, user_id): #ugly hack - for some reason this gets turned into a list self.members = set(self.members) @@ -1553,7 +1553,7 @@ class SlackThreadChannel(object): dbg(message) request = {"type": "message", "channel": self.parent_message.channel.identifier, "text": message, "_team": self.parent_message.team.team_hash, "user": self.parent_message.team.myidentifier, "thread_ts": str(self.parent_message.ts)} self.parent_message.team.send_to_websocket(request) - self.mark_read(force=True) + self.mark_read(update_remote=False, force=True) def open(self, update_remote=True): self.create_buffer() -- cgit From 95fa084893660b655388604084f082a080f1ba32 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 12 Apr 2017 16:47:43 +0000 Subject: massssssssive merge of the new codebase --- README.md | 26 +- _pytest/conftest.py | 121 +- _pytest/data/http/rtm.start.json | 861 ++++ .../websocket/1485975367.79-reconnect_url.json | 1 + .../data/websocket/1485975408.19-user_typing.json | 1 + .../data/websocket/1485975412.74-user_typing.json | 1 + .../data/websocket/1485975420.36-user_typing.json | 1 + _pytest/data/websocket/1485975421.33-message.json | 1 + .../1485975421.42-desktop_notification.json | 1 + .../websocket/1485975426.55-reaction_added.json | 1 + .../websocket/1485975428.76-reaction_removed.json | 1 + .../websocket/1485975458.35-channel_created.json | 1 + .../websocket/1485975458.87-channel_joined.json | 1 + _pytest/data/websocket/1485975458.87-message.json | 1 + .../1485975462.62-update_thread_state.json | 1 + .../data/websocket/1485975462.62-user_typing.json | 1 + .../websocket/1485975476.62-channel_deleted.json | 1 + .../data/websocket/1485975476.86-group_join.json | 1 + .../data/websocket/1485975476.86-group_joined.json | 1 + .../1485975487.69-group_history_changed.json | 1 + .../data/websocket/1485975487.69-group_join.json | 1 + .../data/websocket/1485975493.19-group_joined.json | 1 + _pytest/data/websocket/1485975493.23-message.json | 1 + _pytest/data/websocket/1485975547.75-message.json | 1 + .../1485975547.75-update_thread_state.json | 1 + .../websocket/1485975547.83-reconnect_url.json | 1 + .../data/websocket/1485975606.59-team_join.json | 1 + _pytest/data/websocket/1485975606.67-message.json | 1 + .../data/websocket/1485975606.75-im_created.json | 1 + _pytest/data/websocket/1485975606.75-im_open.json | 1 + .../websocket/1485975611.29-presence_change.json | 1 + .../data/websocket/1485975640.5-user_change.json | 1 + .../data/websocket/1485975669.14-user_change.json | 1 + .../websocket/1485975675.81-presence_change.json | 1 + .../data/websocket/1485975690.59-mpim_joined.json | 1 + .../data/websocket/1485975690.67-group_joined.json | 1 + .../1485975690.67-update_thread_state.json | 1 + .../data/websocket/1485975697.58-user_typing.json | 1 + _pytest/data/websocket/1485975698.45-message.json | 1 + .../websocket/1485975701.72-reaction_added.json | 1 + .../websocket/1485975703.22-reaction_removed.json | 1 + _pytest/data/websocket/1485975715.79-message.json | 1 + .../data/websocket/1485975715.87-file_shared.json | 1 + _pytest/data/websocket/1485975723.85-message.json | 1 + .../websocket/1485975727.84-reconnect_url.json | 1 + .../websocket/1485975735.91-group_archive.json | 1 + _pytest/data/websocket/1485975735.99-message.json | 1 + _pytest/data/websocket/1485975736.08-message.json | 1 + .../1485975738.1-desktop_notification.json | 1 + .../websocket/1485975747.17-channel_created.json | 1 + .../websocket/1485975757.63-channel_rename.json | 1 + .../websocket/1485975771.6-presence_change.json | 1 + .../websocket/1485975774.62-presence_change.json | 1 + .../data/websocket/1485975822.17-user_typing.json | 1 + _pytest/data/websocket/1485975824.48-message.json | 1 + _pytest/data/websocket/1485975836.23-message.json | 1 + _pytest/data/websocket/1485975836.31-message.json | 1 + _pytest/data/websocket/1485975842.1-message.json | 1 + _pytest/data/websocket/1485975842.18-message.json | 1 + .../data/websocket/1485975850.32-mpim_open.json | 1 + .../data/websocket/1485975850.45-group_open.json | 1 + .../websocket/1485975858.56-reaction_added.json | 1 + _pytest/data/websocket/1485975890.7-bot_added.json | 1 + _pytest/data/websocket/1485975890.78-message.json | 1 + _pytest/data/websocket/1485975896.16-message.json | 1 + .../websocket/1485975907.84-reconnect_url.json | 1 + .../data/websocket/1485975926.59-user_change.json | 1 + .../data/websocket/1485975930.81-user_change.json | 1 + .../data/websocket/1485975956.38-file_public.json | 1 + .../data/websocket/1485975956.47-file_shared.json | 1 + _pytest/data/websocket/1485975956.47-message.json | 1 + _pytest/data/websocket/1485975978.79-message.json | 1 + .../data/websocket/1485975978.91-file_public.json | 1 + .../data/websocket/1485975978.91-file_shared.json | 1 + .../data/websocket/1485975995.98-file_change.json | 1 + .../data/websocket/1485975998.13-file_change.json | 1 + .../data/websocket/1485976002.57-file_deleted.json | 1 + _pytest/data/websocket/1485976002.66-message.json | 1 + .../data/websocket/1485976024.46-user_typing.json | 1 + .../data/websocket/1485976028.31-user_typing.json | 1 + .../websocket/1485976039.32-reaction_added.json | 1 + .../websocket/1485976040.8-reaction_removed.json | 1 + .../websocket/1485976087.84-reconnect_url.json | 1 + .../websocket/1485976107.57-channel_created.json | 1 + .../data/websocket/1485976114.72-user_typing.json | 1 + .../data/websocket/1485976117.99-user_typing.json | 1 + .../data/websocket/1485976124.9-user_typing.json | 1 + _pytest/data/websocket/1485976124.98-message.json | 1 + .../1485976125.06-desktop_notification.json | 1 + .../data/websocket/1485976125.95-user_typing.json | 1 + .../data/websocket/1485976129.49-user_typing.json | 1 + .../data/websocket/1485976133.0-user_typing.json | 1 + .../data/websocket/1485976137.51-user_typing.json | 1 + _pytest/data/websocket/1485976137.95-message.json | 1 + .../data/websocket/1485976138.73-user_typing.json | 1 + .../data/websocket/1485976141.79-user_typing.json | 1 + .../data/websocket/1485976145.8-user_typing.json | 1 + .../data/websocket/1485976149.89-user_typing.json | 1 + _pytest/data/websocket/1485976151.6-message.json | 1 + _pytest/data/websocket/1485976157.18-message.json | 1 + .../data/websocket/1485976157.8-user_typing.json | 1 + .../data/websocket/1485976161.29-user_typing.json | 1 + _pytest/data/websocket/1485976161.75-message.json | 1 + .../websocket/1485976182.59-channel_archive.json | 1 + .../websocket/1485976186.62-channel_unarchive.json | 1 + _pytest/data/websocket/1485976236.58-message.json | 1 + .../1485976236.67-file_comment_added.json | 1 + _pytest/data/websocket/1485976248.57-message.json | 1 + .../1485976248.65-file_comment_edited.json | 1 + .../websocket/1485976267.81-reconnect_url.json | 1 + .../websocket/1486004888.32-presence_change.json | 1 + .../websocket/1486004888.41-reconnect_url.json | 1 + .../websocket/1486004935.63-presence_change.json | 1 + _pytest/data/websocket/1486004950.43-message.json | 1 + .../data/websocket/1486004992.49-user_typing.json | 1 + .../data/websocket/1486004995.69-user_typing.json | 1 + .../data/websocket/1486004999.55-user_typing.json | 1 + _pytest/data/websocket/1486004999.95-message.json | 1 + .../data/websocket/1486005004.03-user_typing.json | 1 + .../data/websocket/1486005007.16-user_typing.json | 1 + .../websocket/1486005008.45-reconnect_url.json | 1 + _pytest/data/websocket/1486005009.0-message.json | 1 + .../websocket/1486005096.38-accounts_changed.json | 1 + .../websocket/1486005099.81-presence_change.json | 1 + .../websocket/1486005104.63-channel_marked.json | 1 + .../data/websocket/1486005109.58-im_marked.json | 1 + .../data/websocket/1486005119.69-im_marked.json | 1 + .../data/websocket/1486005124.51-mpim_marked.json | 1 + .../data/websocket/1486005124.59-group_marked.json | 1 + .../data/websocket/1486005124.67-group_marked.json | 1 + .../websocket/1486005188.48-reconnect_url.json | 1 + _pytest/test_eventrouter.py | 86 + _pytest/test_everything.py | 50 + _pytest/test_linkifytext.py | 6 + _pytest/test_presencechange.py | 31 + _pytest/test_process_message.py | 68 +- _pytest/test_processreply.py | 33 + _pytest/test_processteamjoin.py | 54 + _pytest/test_sendmessage.py | 21 + _pytest/test_slackchannel.py | 33 + _pytest/test_slackdmchannel.py | 20 + _pytest/test_slackgroupchannel.py | 20 + _pytest/test_slackmpdmchannel.py | 20 + _pytest/test_slackrequest.py | 11 + _pytest/test_slackteam.py | 10 + _pytest/test_slackts.py | 24 + _pytest/test_unfurl.py | 25 +- wee_slack.py | 4699 +++++++++++--------- weemoji.json | 1 + 149 files changed, 4121 insertions(+), 2227 deletions(-) create mode 100644 _pytest/data/http/rtm.start.json create mode 100644 _pytest/data/websocket/1485975367.79-reconnect_url.json create mode 100644 _pytest/data/websocket/1485975408.19-user_typing.json create mode 100644 _pytest/data/websocket/1485975412.74-user_typing.json create mode 100644 _pytest/data/websocket/1485975420.36-user_typing.json create mode 100644 _pytest/data/websocket/1485975421.33-message.json create mode 100644 _pytest/data/websocket/1485975421.42-desktop_notification.json create mode 100644 _pytest/data/websocket/1485975426.55-reaction_added.json create mode 100644 _pytest/data/websocket/1485975428.76-reaction_removed.json create mode 100644 _pytest/data/websocket/1485975458.35-channel_created.json create mode 100644 _pytest/data/websocket/1485975458.87-channel_joined.json create mode 100644 _pytest/data/websocket/1485975458.87-message.json create mode 100644 _pytest/data/websocket/1485975462.62-update_thread_state.json create mode 100644 _pytest/data/websocket/1485975462.62-user_typing.json create mode 100644 _pytest/data/websocket/1485975476.62-channel_deleted.json create mode 100644 _pytest/data/websocket/1485975476.86-group_join.json create mode 100644 _pytest/data/websocket/1485975476.86-group_joined.json create mode 100644 _pytest/data/websocket/1485975487.69-group_history_changed.json create mode 100644 _pytest/data/websocket/1485975487.69-group_join.json create mode 100644 _pytest/data/websocket/1485975493.19-group_joined.json create mode 100644 _pytest/data/websocket/1485975493.23-message.json create mode 100644 _pytest/data/websocket/1485975547.75-message.json create mode 100644 _pytest/data/websocket/1485975547.75-update_thread_state.json create mode 100644 _pytest/data/websocket/1485975547.83-reconnect_url.json create mode 100644 _pytest/data/websocket/1485975606.59-team_join.json create mode 100644 _pytest/data/websocket/1485975606.67-message.json create mode 100644 _pytest/data/websocket/1485975606.75-im_created.json create mode 100644 _pytest/data/websocket/1485975606.75-im_open.json create mode 100644 _pytest/data/websocket/1485975611.29-presence_change.json create mode 100644 _pytest/data/websocket/1485975640.5-user_change.json create mode 100644 _pytest/data/websocket/1485975669.14-user_change.json create mode 100644 _pytest/data/websocket/1485975675.81-presence_change.json create mode 100644 _pytest/data/websocket/1485975690.59-mpim_joined.json create mode 100644 _pytest/data/websocket/1485975690.67-group_joined.json create mode 100644 _pytest/data/websocket/1485975690.67-update_thread_state.json create mode 100644 _pytest/data/websocket/1485975697.58-user_typing.json create mode 100644 _pytest/data/websocket/1485975698.45-message.json create mode 100644 _pytest/data/websocket/1485975701.72-reaction_added.json create mode 100644 _pytest/data/websocket/1485975703.22-reaction_removed.json create mode 100644 _pytest/data/websocket/1485975715.79-message.json create mode 100644 _pytest/data/websocket/1485975715.87-file_shared.json create mode 100644 _pytest/data/websocket/1485975723.85-message.json create mode 100644 _pytest/data/websocket/1485975727.84-reconnect_url.json create mode 100644 _pytest/data/websocket/1485975735.91-group_archive.json create mode 100644 _pytest/data/websocket/1485975735.99-message.json create mode 100644 _pytest/data/websocket/1485975736.08-message.json create mode 100644 _pytest/data/websocket/1485975738.1-desktop_notification.json create mode 100644 _pytest/data/websocket/1485975747.17-channel_created.json create mode 100644 _pytest/data/websocket/1485975757.63-channel_rename.json create mode 100644 _pytest/data/websocket/1485975771.6-presence_change.json create mode 100644 _pytest/data/websocket/1485975774.62-presence_change.json create mode 100644 _pytest/data/websocket/1485975822.17-user_typing.json create mode 100644 _pytest/data/websocket/1485975824.48-message.json create mode 100644 _pytest/data/websocket/1485975836.23-message.json create mode 100644 _pytest/data/websocket/1485975836.31-message.json create mode 100644 _pytest/data/websocket/1485975842.1-message.json create mode 100644 _pytest/data/websocket/1485975842.18-message.json create mode 100644 _pytest/data/websocket/1485975850.32-mpim_open.json create mode 100644 _pytest/data/websocket/1485975850.45-group_open.json create mode 100644 _pytest/data/websocket/1485975858.56-reaction_added.json create mode 100644 _pytest/data/websocket/1485975890.7-bot_added.json create mode 100644 _pytest/data/websocket/1485975890.78-message.json create mode 100644 _pytest/data/websocket/1485975896.16-message.json create mode 100644 _pytest/data/websocket/1485975907.84-reconnect_url.json create mode 100644 _pytest/data/websocket/1485975926.59-user_change.json create mode 100644 _pytest/data/websocket/1485975930.81-user_change.json create mode 100644 _pytest/data/websocket/1485975956.38-file_public.json create mode 100644 _pytest/data/websocket/1485975956.47-file_shared.json create mode 100644 _pytest/data/websocket/1485975956.47-message.json create mode 100644 _pytest/data/websocket/1485975978.79-message.json create mode 100644 _pytest/data/websocket/1485975978.91-file_public.json create mode 100644 _pytest/data/websocket/1485975978.91-file_shared.json create mode 100644 _pytest/data/websocket/1485975995.98-file_change.json create mode 100644 _pytest/data/websocket/1485975998.13-file_change.json create mode 100644 _pytest/data/websocket/1485976002.57-file_deleted.json create mode 100644 _pytest/data/websocket/1485976002.66-message.json create mode 100644 _pytest/data/websocket/1485976024.46-user_typing.json create mode 100644 _pytest/data/websocket/1485976028.31-user_typing.json create mode 100644 _pytest/data/websocket/1485976039.32-reaction_added.json create mode 100644 _pytest/data/websocket/1485976040.8-reaction_removed.json create mode 100644 _pytest/data/websocket/1485976087.84-reconnect_url.json create mode 100644 _pytest/data/websocket/1485976107.57-channel_created.json create mode 100644 _pytest/data/websocket/1485976114.72-user_typing.json create mode 100644 _pytest/data/websocket/1485976117.99-user_typing.json create mode 100644 _pytest/data/websocket/1485976124.9-user_typing.json create mode 100644 _pytest/data/websocket/1485976124.98-message.json create mode 100644 _pytest/data/websocket/1485976125.06-desktop_notification.json create mode 100644 _pytest/data/websocket/1485976125.95-user_typing.json create mode 100644 _pytest/data/websocket/1485976129.49-user_typing.json create mode 100644 _pytest/data/websocket/1485976133.0-user_typing.json create mode 100644 _pytest/data/websocket/1485976137.51-user_typing.json create mode 100644 _pytest/data/websocket/1485976137.95-message.json create mode 100644 _pytest/data/websocket/1485976138.73-user_typing.json create mode 100644 _pytest/data/websocket/1485976141.79-user_typing.json create mode 100644 _pytest/data/websocket/1485976145.8-user_typing.json create mode 100644 _pytest/data/websocket/1485976149.89-user_typing.json create mode 100644 _pytest/data/websocket/1485976151.6-message.json create mode 100644 _pytest/data/websocket/1485976157.18-message.json create mode 100644 _pytest/data/websocket/1485976157.8-user_typing.json create mode 100644 _pytest/data/websocket/1485976161.29-user_typing.json create mode 100644 _pytest/data/websocket/1485976161.75-message.json create mode 100644 _pytest/data/websocket/1485976182.59-channel_archive.json create mode 100644 _pytest/data/websocket/1485976186.62-channel_unarchive.json create mode 100644 _pytest/data/websocket/1485976236.58-message.json create mode 100644 _pytest/data/websocket/1485976236.67-file_comment_added.json create mode 100644 _pytest/data/websocket/1485976248.57-message.json create mode 100644 _pytest/data/websocket/1485976248.65-file_comment_edited.json create mode 100644 _pytest/data/websocket/1485976267.81-reconnect_url.json create mode 100644 _pytest/data/websocket/1486004888.32-presence_change.json create mode 100644 _pytest/data/websocket/1486004888.41-reconnect_url.json create mode 100644 _pytest/data/websocket/1486004935.63-presence_change.json create mode 100644 _pytest/data/websocket/1486004950.43-message.json create mode 100644 _pytest/data/websocket/1486004992.49-user_typing.json create mode 100644 _pytest/data/websocket/1486004995.69-user_typing.json create mode 100644 _pytest/data/websocket/1486004999.55-user_typing.json create mode 100644 _pytest/data/websocket/1486004999.95-message.json create mode 100644 _pytest/data/websocket/1486005004.03-user_typing.json create mode 100644 _pytest/data/websocket/1486005007.16-user_typing.json create mode 100644 _pytest/data/websocket/1486005008.45-reconnect_url.json create mode 100644 _pytest/data/websocket/1486005009.0-message.json create mode 100644 _pytest/data/websocket/1486005096.38-accounts_changed.json create mode 100644 _pytest/data/websocket/1486005099.81-presence_change.json create mode 100644 _pytest/data/websocket/1486005104.63-channel_marked.json create mode 100644 _pytest/data/websocket/1486005109.58-im_marked.json create mode 100644 _pytest/data/websocket/1486005119.69-im_marked.json create mode 100644 _pytest/data/websocket/1486005124.51-mpim_marked.json create mode 100644 _pytest/data/websocket/1486005124.59-group_marked.json create mode 100644 _pytest/data/websocket/1486005124.67-group_marked.json create mode 100644 _pytest/data/websocket/1486005188.48-reconnect_url.json create mode 100644 _pytest/test_eventrouter.py create mode 100644 _pytest/test_everything.py create mode 100644 _pytest/test_linkifytext.py create mode 100644 _pytest/test_presencechange.py create mode 100644 _pytest/test_processreply.py create mode 100644 _pytest/test_processteamjoin.py create mode 100644 _pytest/test_sendmessage.py create mode 100644 _pytest/test_slackchannel.py create mode 100644 _pytest/test_slackdmchannel.py create mode 100644 _pytest/test_slackgroupchannel.py create mode 100644 _pytest/test_slackmpdmchannel.py create mode 100644 _pytest/test_slackrequest.py create mode 100644 _pytest/test_slackteam.py create mode 100644 _pytest/test_slackts.py create mode 100644 weemoji.json diff --git a/README.md b/README.md index 46bf652..ff47c98 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ wee-slack ========= **News:** - The 0.99.1+ has a number of backend changes to make things faster and better. You should use it. :) _(please report bugs in #wee-slack on freenode)_ + 1.0-RC1 is here. It is a pretty massive refactor, and fixes many of the current issues listed on github. Because there was no good way to do this, it breaks some/many existing PRs. _(please report bugs in #wee-slack on freenode)_ A WeeChat native client for Slack.com. Provides supplemental features only available in the web/mobile clients such as: synchronizing read markers, typing notification, search, (and more)! Connects via the Slack API, and maintains a persistent websocket for notification of events. @@ -12,8 +12,9 @@ A WeeChat native client for Slack.com. Provides supplemental features only avail Features -------- - * **New** Slash commands (including custom ones!) - * **New** Upload to slack capabilities! + * **New** Threads support! + * Slash commands (including custom ones!) + * Upload to slack capabilities! * Emoji reactions! * Edited messages work just like the official clients, where the original message changes and has (edited) appended. * Unfurled urls dont generate a new message, but replace the original with more info as it is received. @@ -29,7 +30,6 @@ Features * Colorized nicks in chat * Supports bidirectional slack read notifications for all channels. (never reread the same messages on the web client or other devices). * Typing notification, so you can see when others are typing, and they can see when you type. Appears globally for direct messages - * Search slack history allows you to do simple searches across all previous slack conversations * Away/back status handling * Expands/shows metadata for things like tweets/links * Displays edited messages (slack.com irc mode currently doesn't show these) @@ -37,9 +37,7 @@ Features In Development -------------- - * fix search * add notification of new versions of wee-slack - * growl notification Dependencies @@ -193,6 +191,22 @@ Debug mode: /slack debug ``` +Start a new thread on the most recent message The number indicates which message in the buffer to reply to, in reverse time order: +``` +/reply 1 here is a threaded reply to the most recent message! +``` + +Open an existing thread as a channel. The argument is the thread identifier, which is printed in square brackets with every threaded message in a channel: +``` +/thread af8 +``` + +Label a thread with a memorable name. The above command will open a channel called af8, but perhaps you want to call it "meetingnotes". To do so, select that buffer and type: +``` +/label meetingnotes +``` +_Note: labels do not persist once a thread buffer is closed_ + Optional settings ----------------- diff --git a/_pytest/conftest.py b/_pytest/conftest.py index e9b7750..0259ac2 100644 --- a/_pytest/conftest.py +++ b/_pytest/conftest.py @@ -1,15 +1,48 @@ +import json import pytest import sys sys.path.append(".") -#sys.path.append(str(pytest.config.rootdir)) -from wee_slack import SlackServer -from wee_slack import Channel -from wee_slack import User -from wee_slack import SearchList +#New stuff +from wee_slack import EventRouter +from wee_slack import SlackRequest import wee_slack +class fakewebsocket(object): + def __init__(self): + self.returndata = [] + pass + def add(self, data): + self.returndata.append(data) + def recv(self): + return json.dumps(self.returndata.pop(0)) + def send(self, data): + print "websocket received: {}".format(data) + return + +@pytest.fixture +def mock_websocket(): + return fakewebsocket() + +@pytest.fixture +def realish_eventrouter(): + e = EventRouter() + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.start", {"meh": "blah"})) + rtmstartdata = open('_pytest/data/http/rtm.start.json', 'r').read() + e.receive_httprequest_callback(context, 1, 0, rtmstartdata, 4) + e.handle_next() + #e.sc is just shortcuts to these items + e.sc = {} + e.sc["team_id"] = e.teams.keys()[0] + e.sc["team"] = e.teams[e.sc["team_id"]] + e.sc["user"] = e.teams[e.sc["team_id"]].users[e.teams[e.sc["team_id"]].users.keys()[0]] + socket = mock_websocket + e.teams[e.sc["team_id"]].ws = socket + + return e + + class FakeWeechat(): """ this is the thing that acts as "w." everywhere.. @@ -18,7 +51,8 @@ class FakeWeechat(): WEECHAT_RC_OK = True def __init__(self): - print "INITIALIZE FAKE WEECHAT" + pass + #print "INITIALIZE FAKE WEECHAT" def prnt(*args): output = "(" for arg in args: @@ -33,77 +67,24 @@ class FakeWeechat(): return "1355517519" def hdata_string(*args): return "testuser" - + def buffer_new(*args): + return "0x8a8a8a8b" def __getattr__(self, name): def method(*args): - print "called {}".format(name) - if args: - print "\twith args: {}".format(args) + pass + #print "called {}".format(name) + #if args: + # print "\twith args: {}".format(args) return method @pytest.fixture -def fake_weechat(): +def mock_weechat(): wee_slack.w = FakeWeechat() - pass - - -@pytest.fixture -def slack_debug(): + wee_slack.config = wee_slack.PluginConfig() + wee_slack.debug_string = None wee_slack.slack_debug = "debug_buffer_ptr" - -@pytest.fixture -def server(fake_weechat, monkeypatch): -#def server(monkeypatch, mychannels, myusers): - def mock_connect_to_slack(*args): - return True - monkeypatch.setattr(SlackServer, 'connect_to_slack', mock_connect_to_slack) - myserver = SlackServer('xoxo-12345') - myserver.identifier = 'test.slack.com' - myserver.nick = 'myusername' - return myserver - -@pytest.fixture -def myservers(server): - servers = SearchList() - servers.append(server) - return servers - - - -@pytest.fixture -def channel(monkeypatch, server): - def mock_buffer_prnt(*args): - print "called buffer_prnt\n\twith args: {}".format(args) - return - def mock_do_nothing(*args): - print args - return True - monkeypatch.setattr(Channel, 'create_buffer', mock_do_nothing) - monkeypatch.setattr(Channel, 'attach_buffer', mock_do_nothing) - monkeypatch.setattr(Channel, 'set_topic', mock_do_nothing) - monkeypatch.setattr(Channel, 'set_topic', mock_do_nothing) - monkeypatch.setattr(Channel, 'buffer_prnt', mock_buffer_prnt) - mychannel = Channel(server, '#testchan', 'C2147483705', True, last_read=0, prepend_name="", members=[], topic="") - return mychannel - -@pytest.fixture -def mychannels(channel): - channels = SearchList() - channels.append(channel) - return channels - -@pytest.fixture -def user(monkeypatch, server): - wee_slack.domain = None - wee_slack.colorize_nicks = True + wee_slack.STOP_TALKING_TO_SLACK = False + wee_slack.proc = {} pass - myuser = User(server, "testuser", 'U2147483697', presence="away") - myuser.color = '' - return myuser -@pytest.fixture -def myusers(monkeypatch, user): - users = SearchList() - users.append(user) - return users diff --git a/_pytest/data/http/rtm.start.json b/_pytest/data/http/rtm.start.json new file mode 100644 index 0000000..d9cc464 --- /dev/null +++ b/_pytest/data/http/rtm.start.json @@ -0,0 +1,861 @@ +{ + "ok": true, + "self": { + "id": "U3ZKBBDL5", + "name": "bob", + "prefs": { + "highlight_words": "", + "user_colors": "", + "color_names_in_list": true, + "growls_enabled": true, + "tz": "America\/Los_Angeles", + "push_dm_alert": true, + "push_mention_alert": true, + "push_everything": true, + "push_show_preview": true, + "push_idle_wait": 2, + "push_sound": "b2.mp3", + "push_loud_channels": "", + "push_mention_channels": "", + "push_loud_channels_set": "", + "threads_everything": false, + "email_alerts": "instant", + "email_alerts_sleep_until": 0, + "email_misc": false, + "email_weekly": true, + "welcome_message_hidden": false, + "all_channels_loud": true, + "loud_channels": "", + "never_channels": "", + "loud_channels_set": "", + "search_sort": "timestamp", + "expand_inline_imgs": true, + "expand_internal_inline_imgs": true, + "expand_snippets": false, + "posts_formatting_guide": true, + "seen_welcome_2": true, + "seen_ssb_prompt": false, + "spaces_new_xp_banner_dismissed": false, + "search_only_my_channels": false, + "search_only_current_team": false, + "emoji_mode": "default", + "emoji_use": "", + "has_invited": false, + "has_uploaded": false, + "has_created_channel": false, + "has_searched": false, + "search_exclude_channels": "", + "messages_theme": "default", + "webapp_spellcheck": true, + "no_joined_overlays": false, + "no_created_overlays": false, + "dropbox_enabled": false, + "seen_domain_invite_reminder": false, + "seen_member_invite_reminder": false, + "mute_sounds": false, + "arrow_history": false, + "tab_ui_return_selects": true, + "obey_inline_img_limit": true, + "new_msg_snd": "knock_brush.mp3", + "require_at": false, + "ssb_space_window": "", + "mac_ssb_bounce": "", + "mac_ssb_bullet": true, + "expand_non_media_attachments": true, + "show_typing": true, + "pagekeys_handled": true, + "last_snippet_type": "", + "display_real_names_override": 0, + "display_preferred_names": true, + "time24": false, + "enter_is_special_in_tbt": false, + "graphic_emoticons": false, + "convert_emoticons": true, + "ss_emojis": true, + "sidebar_behavior": "", + "seen_onboarding_start": false, + "onboarding_cancelled": true, + "seen_onboarding_slackbot_conversation": false, + "seen_onboarding_channels": false, + "seen_onboarding_direct_messages": false, + "seen_onboarding_invites": false, + "seen_onboarding_search": false, + "seen_onboarding_recent_mentions": false, + "seen_onboarding_starred_items": false, + "seen_onboarding_private_groups": false, + "onboarding_slackbot_conversation_step": 0, + "dnd_enabled": true, + "dnd_start_hour": "22:00", + "dnd_end_hour": "08:00", + "mark_msgs_read_immediately": true, + "start_scroll_at_oldest": true, + "snippet_editor_wrap_long_lines": false, + "ls_disabled": false, + "sidebar_theme": "default", + "sidebar_theme_custom_values": "", + "f_key_search": false, + "k_key_omnibox": true, + "speak_growls": false, + "mac_speak_voice": "com.apple.speech.synthesis.voice.Alex", + "mac_speak_speed": 250, + "at_channel_suppressed_channels": "", + "push_at_channel_suppressed_channels": "", + "prompted_for_email_disabling": false, + "full_text_extracts": false, + "no_text_in_notifications": false, + "muted_channels": "", + "no_macelectron_banner": false, + "no_macssb1_banner": false, + "no_macssb2_banner": false, + "no_winssb1_banner": false, + "no_invites_widget_in_sidebar": false, + "no_omnibox_in_channels": false, + "k_key_omnibox_auto_hide_count": 0, + "prev_next_btn": false, + "hide_user_group_info_pane": false, + "mentions_exclude_at_user_groups": false, + "privacy_policy_seen": true, + "enterprise_migration_seen": true, + "last_tos_acknowledged": "tos_oct2016", + "search_exclude_bots": false, + "load_lato_2": false, + "fuller_timestamps": false, + "last_seen_at_channel_warning": 0, + "msg_preview": false, + "msg_preview_persistent": true, + "emoji_autocomplete_big": false, + "winssb_run_from_tray": true, + "winssb_window_flash_behavior": "idle", + "two_factor_auth_enabled": false, + "two_factor_type": null, + "two_factor_backup_type": null, + "hide_hex_swatch": false, + "client_logs_pri": "", + "enhanced_debugging": false, + "thin_channel_membership_fe": false, + "flannel_server_pool": "random", + "mentions_exclude_at_channels": true, + "confirm_clear_all_unreads": true, + "confirm_user_marked_away": true, + "box_enabled": false, + "seen_single_emoji_msg": false, + "confirm_sh_call_start": true, + "preferred_skin_tone": "", + "show_all_skin_tones": false, + "separate_private_channels": false, + "whats_new_read": 1485969645, + "hotness": false, + "frecency_jumper": "", + "frecency_ent_jumper": "", + "jumbomoji": true, + "newxp_seen_last_message": "1", + "attachments_with_borders": false, + "show_memory_instrument": false, + "enable_unread_view": false, + "seen_unread_view_coachmark": false, + "seen_calls_video_beta_coachmark": false, + "seen_calls_video_ga_coachmark": false, + "seen_calls_ss_window_coachmark": false, + "measure_css_usage": false, + "enable_react_emoji_picker": true, + "seen_replies_coachmark": false, + "seen_custom_status_badge": false, + "all_unreads_sort_order": "alphabetical", + "locale": "en_US", + "gdrive_authed": true, + "gdrive_enabled": true, + "seen_gdrive_coachmark": false, + "channel_sort": "{\"is_custom_sorted\":false, \"priority_display\":false, \"priority_type\":\"\", \"sorts\":[]}", + "overloaded_message_enabled": true, + "seen_highlights_coachmark": false, + "seen_highlights_arrows_coachmark": false, + "a11y_font_size": "normal", + "a11y_animations": true, + "intro_to_apps_message_seen": false + }, + "created": 1485969645, + "manual_presence": "active" + }, + "team": { + "id": "T3YS5EAL9", + "name": "weeslacktest", + "email_domain": "", + "domain": "weeslacktest", + "msg_edit_window_mins": -1, + "prefs": { + "invites_only_admins": false, + "default_channels": [ + "C407ABS94", + "C3ZEQAYN7" + ], + "display_email_addresses": false, + "hide_referers": true, + "msg_edit_window_mins": -1, + "allow_message_deletion": true, + "calling_app_name": "Slack", + "allow_calls": true, + "display_real_names": false, + "who_can_at_everyone": "regular", + "who_can_at_channel": "ra", + "who_can_create_channels": "regular", + "who_can_archive_channels": "regular", + "who_can_create_groups": "ra", + "who_can_post_general": "ra", + "who_can_kick_channels": "admin", + "who_can_kick_groups": "regular", + "retention_type": 0, + "retention_duration": 0, + "group_retention_type": 0, + "group_retention_duration": 0, + "dm_retention_type": 0, + "dm_retention_duration": 0, + "file_retention_duration": 0, + "file_retention_type": 0, + "allow_retention_override": true, + "require_at_for_mention": false, + "default_rxns": [ + "simple_smile", + "thumbsup", + "white_check_mark", + "heart", + "eyes" + ], + "team_handy_rxns": { + "restrict": false, + "list": [ + { + "name": "slightly_smiling_face", + "title": "I'm happy!" + }, + { + "name": "+1", + "title": "good!" + }, + { + "name": "white_check_mark", + "title": "done!" + }, + { + "name": "heart", + "title": "love it!" + }, + { + "name": "eyes", + "title": "looking..." + } + ] + }, + "channel_handy_rxns": null, + "compliance_export_start": 0, + "warn_before_at_channel": "always", + "disallow_public_file_urls": false, + "who_can_create_delete_user_groups": "admin", + "who_can_edit_user_groups": "admin", + "who_can_change_team_profile": "admin", + "allow_shared_channels": false, + "who_has_team_visibility": "ra", + "disable_file_uploads": "allow_all", + "disable_file_editing": false, + "disable_file_deleting": false, + "who_can_create_shared_channels": "admin", + "who_can_manage_shared_channels": { + "type": [ + "admin" + ] + }, + "who_can_post_in_shared_channels": { + "type": [ + "regular", + "ra" + ] + }, + "allow_shared_channel_perms_override": false, + "gdrive_enabled_team": true, + "dnd_enabled": true, + "dnd_start_hour": "22:00", + "dnd_end_hour": "08:00", + "auth_mode": "normal", + "who_can_manage_integrations": { + "type": [ + "regular" + ] + }, + "invites_limit": true + }, + "icon": { + "image_34": "https:\/\/a.slack-edge.com\/0180\/img\/avatars-teams\/ava_0014-34.png", + "image_44": "https:\/\/a.slack-edge.com\/0180\/img\/avatars-teams\/ava_0014-44.png", + "image_68": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-68.png", + "image_88": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-88.png", + "image_102": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-102.png", + "image_132": "https:\/\/a.slack-edge.com\/66f9\/img\/avatars-teams\/ava_0014-132.png", + "image_230": "https:\/\/a.slack-edge.com\/bfaba\/img\/avatars-teams\/ava_0014-230.png", + "image_default": true + }, + "over_storage_limit": false, + "plan": "", + "avatar_base_url": "https:\/\/ca.slack-edge.com\/", + "over_integrations_limit": true + }, + "latest_event_ts": "1485976462.000000", + "channels": [ + { + "id": "C407ABS94", + "name": "general", + "is_channel": true, + "created": 1485969592, + "creator": "U407ABLLW", + "is_archived": false, + "is_general": true, + "has_pins": false, + "is_member": true, + "last_read": "1485976236.000019", + "latest": { + "type": "message", + "subtype": "file_comment", + "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.", + "file": { + "id": "F3ZJQTA66", + "created": 1485975955, + "timestamp": 1485975955, + "name": "1x1.png", + "title": "1x1.png", + "mimetype": "image\/png", + "filetype": "png", + "pretty_type": "PNG", + "user": "U407ABLLW", + "editable": false, + "size": 68, + "mode": "hosted", + "is_external": false, + "external_type": "", + "is_public": true, + "public_url_shared": false, + "display_as_bot": false, + "username": "", + "url_private": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZJQTA66\/1x1.png", + "url_private_download": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZJQTA66\/download\/1x1.png", + "thumb_64": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_64.png", + "thumb_80": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_80.png", + "thumb_360": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_360.png", + "thumb_360_w": 1, + "thumb_360_h": 1, + "thumb_160": "https:\/\/files.slack.com\/files-tmb\/T3YS5EAL9-F3ZJQTA66-8ab528dd77\/1x1_160.png", + "image_exif_rotation": 1, + "original_w": 1, + "original_h": 1, + "permalink": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZJQTA66\/1x1.png", + "permalink_public": "https:\/\/slack-files.com\/T3YS5EAL9-F3ZJQTA66-5d747593d2", + "channels": [ + "C407ABS94" + ], + "groups": [], + "ims": [], + "comments_count": 1 + }, + "comment": { + "id": "Fc3ZMDRQLV", + "created": 1485976236, + "timestamp": 1485976236, + "user": "U407ABLLW", + "is_intro": false, + "comment": "first comment! now i edited it.", + "channel": "" + }, + "is_intro": false, + "ts": "1485976236.000019" + }, + "unread_count": 0, + "unread_count_display": 0, + "members": [ + "U3ZKBBDL5", + "U407ABLLW", + "U4096CBHC" + ], + "topic": { + "value": "Company-wide announcements and work-based matters", + "creator": "", + "last_set": 0 + }, + "purpose": { + "value": "This channel is for team-wide communication and announcements. All team members are in this channel.", + "creator": "", + "last_set": 0 + }, + "previous_names": [] + }, + { + "id": "C3ZM2GMGU", + "name": "made-to-be-archived", + "is_channel": true, + "created": 1485976107, + "creator": "U407ABLLW", + "is_archived": false, + "is_general": false, + "has_pins": false, + "is_member": false, + "previous_names": [] + }, + { + "id": "C3ZEQAYN7", + "name": "random", + "is_channel": true, + "created": 1485969592, + "creator": "U407ABLLW", + "is_archived": false, + "is_general": false, + "has_pins": false, + "is_member": true, + "last_read": "1485969592.000002", + "latest": { + "user": "U4096CBHC", + "text": "<@U4096CBHC|charles> has joined the channel", + "type": "message", + "subtype": "channel_join", + "ts": "1485975606.000004" + }, + "unread_count": 2, + "unread_count_display": 0, + "members": [ + "U3ZKBBDL5", + "U407ABLLW", + "U4096CBHC" + ], + "topic": { + "value": "Non-work banter and water cooler conversation", + "creator": "", + "last_set": 0 + }, + "purpose": { + "value": "A place for non-work-related flimflam, faffing, hodge-podge or jibber-jabber you'd prefer to keep out of more focused work-related channels.", + "creator": "", + "last_set": 0 + }, + "previous_names": [] + }, + { + "id": "C3ZM8JTD3", + "name": "some-channel2-renamed", + "is_channel": true, + "created": 1485975747, + "creator": "U407ABLLW", + "is_archived": false, + "is_general": false, + "has_pins": false, + "is_member": false, + "previous_names": [ + "some-channel2" + ] + } + ], + "groups": [ + { + "id": "G3ZGMF4RZ", + "name": "mpdm-bob--alice--charles-1", + "is_group": true, + "created": 1485975690, + "creator": "U407ABLLW", + "is_archived": false, + "is_mpim": true, + "has_pins": false, + "is_open": true, + "last_read": "0000000000.000000", + "latest": { + "type": "message", + "subtype": "file_share", + "text": "<@U407ABLLW|alice> uploaded a file: ", + "file": { + "id": "F3ZLY6K5J", + "created": 1485975715, + "timestamp": 1485975715, + "name": "-.txt", + "title": "Untitled", + "mimetype": "text\/plain", + "filetype": "text", + "pretty_type": "Plain Text", + "user": "U407ABLLW", + "editable": true, + "size": 14, + "mode": "snippet", + "is_external": false, + "external_type": "", + "is_public": false, + "public_url_shared": false, + "display_as_bot": false, + "username": "", + "url_private": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZLY6K5J\/-.txt", + "url_private_download": "https:\/\/files.slack.com\/files-pri\/T3YS5EAL9-F3ZLY6K5J\/download\/-.txt", + "permalink": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZLY6K5J\/-.txt", + "permalink_public": "https:\/\/slack-files.com\/T3YS5EAL9-F3ZLY6K5J-39c2c4f739", + "edit_link": "https:\/\/weeslacktest.slack.com\/files\/alice\/F3ZLY6K5J\/-.txt\/edit", + "preview": "some code here", + "preview_highlight": "
\n
\n
some code here<\/pre><\/div>\n<\/div>\n<\/div>\n",
+                    "lines": 1,
+                    "lines_more": 0,
+                    "preview_is_truncated": false,
+                    "channels": [],
+                    "groups": [
+                        "G3ZGMF4RZ"
+                    ],
+                    "ims": [],
+                    "comments_count": 0
+                },
+                "user": "U407ABLLW",
+                "upload": true,
+                "display_as_bot": false,
+                "username": "<@U407ABLLW|alice>",
+                "bot_id": null,
+                "ts": "1485975715.000005"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "members": [
+                "U407ABLLW",
+                "U3ZKBBDL5",
+                "U4096CBHC"
+            ],
+            "topic": {
+                "value": "Group messaging",
+                "creator": "U407ABLLW",
+                "last_set": 1485975690
+            },
+            "purpose": {
+                "value": "Group messaging with: @bob @alice @charles",
+                "creator": "U407ABLLW",
+                "last_set": 1485975690
+            }
+        },
+        {
+            "id": "G3ZJKP7GA",
+            "name": "some-private-channel",
+            "is_group": true,
+            "created": 1485975492,
+            "creator": "U407ABLLW",
+            "is_archived": false,
+            "is_mpim": false,
+            "has_pins": false,
+            "is_open": true,
+            "last_read": "1485975492.000002",
+            "latest": {
+                "user": "U407ABLLW",
+                "purpose": "seekret",
+                "text": "<@U407ABLLW|alice> set the channel's purpose: seekret",
+                "type": "message",
+                "subtype": "group_purpose",
+                "ts": "1485975492.000004"
+            },
+            "unread_count": 2,
+            "unread_count_display": 1,
+            "members": [
+                "U3ZKBBDL5",
+                "U407ABLLW"
+            ],
+            "topic": {
+                "value": "",
+                "creator": "",
+                "last_set": 0
+            },
+            "purpose": {
+                "value": "seekret",
+                "creator": "U407ABLLW",
+                "last_set": 1485975493
+            }
+        },
+        {
+            "id": "G409GKN9M",
+            "name": "some-channel",
+            "is_group": true,
+            "created": 1485975458,
+            "creator": "U407ABLLW",
+            "is_archived": true,
+            "is_mpim": false,
+            "has_pins": false,
+            "is_open": false,
+            "last_read": "1485975476.876631",
+            "latest": {
+                "user": "U407ABLLW",
+                "text": "<@U407ABLLW|alice> archived the private channel",
+                "type": "message",
+                "subtype": "group_archive",
+                "ts": "1485975735.000006"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "members": [
+                "U3ZKBBDL5",
+                "U407ABLLW"
+            ],
+            "topic": {
+                "value": "",
+                "creator": "U407ABLLW",
+                "last_set": 1485975476
+            },
+            "purpose": {
+                "value": "who knows?",
+                "creator": "U407ABLLW",
+                "last_set": 1485975476
+            }
+        }
+    ],
+    "ims": [
+        {
+            "id": "D3ZK1D8JY",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "USLACKBOT",
+            "has_pins": false,
+            "last_read": "1485969667.000002",
+            "latest": {
+                "type": "message",
+                "user": "USLACKBOT",
+                "text": "<@U407ABLLW|alice> archived the private channel ",
+                "ts": "1485975735.000002"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "is_open": true
+        },
+        {
+            "id": "D3ZEQULFM",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U3ZKBBDL5",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": null,
+            "unread_count": 0,
+            "unread_count_display": 0,
+            "is_open": true
+        },
+        {
+            "id": "D3ZEQULHZ",
+            "created": 1485969645,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U407ABLLW",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": {
+                "type": "message",
+                "user": "U407ABLLW",
+                "text": "hi bob",
+                "ts": "1485975421.000002"
+            },
+            "unread_count": 1,
+            "unread_count_display": 1,
+            "is_open": true
+        },
+        {
+            "id": "D409J34CF",
+            "created": 1485975606,
+            "is_im": true,
+            "is_org_shared": false,
+            "user": "U4096CBHC",
+            "has_pins": false,
+            "last_read": "0000000000.000000",
+            "latest": null,
+            "unread_count": 0,
+            "unread_count_display": 0,
+            "is_open": true
+        }
+    ],
+    "cache_ts": 1485977062,
+    "read_only_channels": [],
+    "can_manage_shared_channels": false,
+    "subteams": {
+        "self": [],
+        "all": []
+    },
+    "dnd": {
+        "dnd_enabled": true,
+        "dnd_debug": {
+            "next_dnd_start_date": "Wed, 01 Feb 2017 22:00:00 -0800",
+            "next_dnd_end_date": "Thu, 02 Feb 2017 08:00:00 -0800",
+            "now": "Wed, 01 Feb 2017 11:24:22 -0800",
+            "type": "slack"
+        },
+        "next_dnd_start_ts": 1486015200,
+        "next_dnd_end_ts": 1486051200,
+        "snooze_enabled": false
+    },
+    "users": [
+        {
+            "id": "U407ABLLW",
+            "team_id": "T3YS5EAL9",
+            "name": "alice",
+            "deleted": false,
+            "status": null,
+            "color": "9f69e7",
+            "real_name": "First Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "First",
+                "last_name": "Testuser",
+                "avatar_hash": "gfd0ce7a168d",
+                "real_name": "First Testuser",
+                "real_name_normalized": "First Testuser",
+                "email": "redacted1@gmail.com",
+                "image_24": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-24.png",
+                "image_32": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-32.png",
+                "image_48": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-48.png",
+                "image_72": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png",
+                "image_192": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png",
+                "image_512": "https:\/\/secure.gravatar.com\/avatar\/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-512.png",
+                "fields": null
+            },
+            "is_admin": true,
+            "is_owner": true,
+            "is_primary_owner": true,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "away"
+        },
+        {
+            "id": "U3ZKBBDL5",
+            "team_id": "T3YS5EAL9",
+            "name": "bob",
+            "deleted": false,
+            "status": null,
+            "color": "4bbe2e",
+            "real_name": "Second Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "Second",
+                "last_name": "Testuser",
+                "avatar_hash": "g6f7a5bf7eb7",
+                "real_name": "Second Testuser",
+                "real_name_normalized": "Second Testuser",
+                "email": "redacted2@gmail.com",
+                "image_24": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-24.png",
+                "image_32": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-32.png",
+                "image_48": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-48.png",
+                "image_72": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png",
+                "image_192": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0019-192.png",
+                "image_512": "https:\/\/secure.gravatar.com\/avatar\/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0019-512.png",
+                "fields": null
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "has_2fa": false,
+            "presence": "active"
+        },
+        {
+            "id": "U4096CBHC",
+            "team_id": "T3YS5EAL9",
+            "name": "charles",
+            "deleted": false,
+            "status": null,
+            "color": "e7392d",
+            "real_name": "Charles Testuser",
+            "tz": "America\/Los_Angeles",
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "Charles",
+                "last_name": "Testuser",
+                "avatar_hash": "5af404f7d4b7",
+                "fields": [],
+                "image_24": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_32": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_48": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_72": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_192": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_512": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_1024": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_24.png",
+                "image_original": "https:\/\/avatars.slack-edge.com\/2017-02-01\/135714629508_5af404f7d4b7728d10c0_original.png",
+                "real_name": "Charles Testuser",
+                "real_name_normalized": "Charles Testuser",
+                "email": "redacted3@gmail.com"
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "away"
+        },
+        {
+            "id": "USLACKBOT",
+            "team_id": "T3YS5EAL9",
+            "name": "slackbot",
+            "deleted": false,
+            "status": null,
+            "color": "757575",
+            "real_name": "slackbot",
+            "tz": null,
+            "tz_label": "Pacific Standard Time",
+            "tz_offset": -28800,
+            "profile": {
+                "first_name": "slackbot",
+                "last_name": "",
+                "image_24": "https:\/\/a.slack-edge.com\/0180\/img\/slackbot_24.png",
+                "image_32": "https:\/\/a.slack-edge.com\/2fac\/plugins\/slackbot\/assets\/service_32.png",
+                "image_48": "https:\/\/a.slack-edge.com\/2fac\/plugins\/slackbot\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/0180\/img\/slackbot_72.png",
+                "image_192": "https:\/\/a.slack-edge.com\/66f9\/img\/slackbot_192.png",
+                "image_512": "https:\/\/a.slack-edge.com\/1801\/img\/slackbot_512.png",
+                "avatar_hash": "sv1444671949",
+                "real_name": "slackbot",
+                "real_name_normalized": "slackbot",
+                "fields": null
+            },
+            "is_admin": false,
+            "is_owner": false,
+            "is_primary_owner": false,
+            "is_restricted": false,
+            "is_ultra_restricted": false,
+            "is_bot": false,
+            "presence": "active"
+        }
+    ],
+    "cache_version": "v15-koala",
+    "cache_ts_version": "v1-cat",
+    "bots": [
+        {
+            "id": "B3YTBU6L8",
+            "deleted": false,
+            "name": "incoming-webhook",
+            "app_id": "A0F7XDUAZ",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/tester\/assets\/service_72.png"
+            }
+        },
+        {
+            "id": "B3ZESMZKM",
+            "deleted": false,
+            "name": "Slack API Tester",
+            "app_id": "A02",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/b48b\/plugins\/slack_api_news\/assets\/service_72.png"
+            }
+        },
+        {
+            "id": "B407MVCA3",
+            "deleted": false,
+            "name": "gdrive",
+            "app_id": "A0F7YS32P",
+            "icons": {
+                "image_36": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_36.png",
+                "image_48": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_48.png",
+                "image_72": "https:\/\/a.slack-edge.com\/12b5a\/plugins\/gdrive\/assets\/service_72.png"
+            }
+        }
+    ],
+    "url": "wss:\/\/mpmulti-9ry9.slack-msgs.com\/websocket\/U4n-6Yw8mpc9C64E74gYXGbojir25QCCcLJ9eK57KDURr0o5so6x4qZU-pKn-LX0bYt-ZmIaFcN4TxoKPry1eRtOwiCNSyGyIGxnYrUtrsvMvkFr5C2-osZVL85WqzIWRoE00sBma3U8BkKfi8oUmMoYGKFFVeJ2WPI_ygHRPOo="
+}
diff --git a/_pytest/data/websocket/1485975367.79-reconnect_url.json b/_pytest/data/websocket/1485975367.79-reconnect_url.json
new file mode 100644
index 0000000..94cacca
--- /dev/null
+++ b/_pytest/data/websocket/1485975367.79-reconnect_url.json
@@ -0,0 +1 @@
+{"url": "wss://mpmulti-gvop.slack-msgs.com/websocket/tamYoLuX4lU-WBS7cFe2RCh8kqeO86F0Mi1RKFVfk7FM-QNu1KD7HiMBStfdLkwkKHmtyTACNE6SONtoQRTbnC0q9fAoLHPl76Y7y9IhCve6VKs2KNLmRH37WutXTBsj3b9HvF79VySlPgAwVXZeH0lgfDDk_RAY9l_dJ8u-jSs=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"}
diff --git a/_pytest/data/websocket/1485975408.19-user_typing.json b/_pytest/data/websocket/1485975408.19-user_typing.json
new file mode 100644
index 0000000..422e30b
--- /dev/null
+++ b/_pytest/data/websocket/1485975408.19-user_typing.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "D3ZEQULHZ"}
diff --git a/_pytest/data/websocket/1485975412.74-user_typing.json b/_pytest/data/websocket/1485975412.74-user_typing.json
new file mode 100644
index 0000000..df57e72
--- /dev/null
+++ b/_pytest/data/websocket/1485975412.74-user_typing.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"}
diff --git a/_pytest/data/websocket/1485975420.36-user_typing.json b/_pytest/data/websocket/1485975420.36-user_typing.json
new file mode 100644
index 0000000..422e30b
--- /dev/null
+++ b/_pytest/data/websocket/1485975420.36-user_typing.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "D3ZEQULHZ"}
diff --git a/_pytest/data/websocket/1485975421.33-message.json b/_pytest/data/websocket/1485975421.33-message.json
new file mode 100644
index 0000000..9a85771
--- /dev/null
+++ b/_pytest/data/websocket/1485975421.33-message.json
@@ -0,0 +1 @@
+{"text": "hi bob", "ts": "1485975421.000002", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "D3ZEQULHZ"}
diff --git a/_pytest/data/websocket/1485975421.42-desktop_notification.json b/_pytest/data/websocket/1485975421.42-desktop_notification.json
new file mode 100644
index 0000000..ee6739a
--- /dev/null
+++ b/_pytest/data/websocket/1485975421.42-desktop_notification.json
@@ -0,0 +1 @@
+{"avatarImage": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png", "subtitle": "alice", "is_shared": false, "title": "weeslacktest", "ssbFilename": "knock_brush.mp3", "imageUri": null, "launchUri": "slack://channel?id=D3ZEQULHZ&message=1485975421000002&team=T3YS5EAL9", "event_ts": "1485975421.875655", "msg": "1485975421.000002", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "content": "hi bob", "type": "desktop_notification", "channel": "D3ZEQULHZ"}
diff --git a/_pytest/data/websocket/1485975426.55-reaction_added.json b/_pytest/data/websocket/1485975426.55-reaction_added.json
new file mode 100644
index 0000000..b51cfdb
--- /dev/null
+++ b/_pytest/data/websocket/1485975426.55-reaction_added.json
@@ -0,0 +1 @@
+{"reaction": "grinning", "event_ts": "1485975426.875724", "ts": "1485975426.000003", "item": {"type": "message", "ts": "1485975421.000002", "channel": "D3ZEQULHZ"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_added"}
diff --git a/_pytest/data/websocket/1485975428.76-reaction_removed.json b/_pytest/data/websocket/1485975428.76-reaction_removed.json
new file mode 100644
index 0000000..b3b90ac
--- /dev/null
+++ b/_pytest/data/websocket/1485975428.76-reaction_removed.json
@@ -0,0 +1 @@
+{"reaction": "grinning", "event_ts": "1485975428.875759", "ts": "1485975428.000004", "item": {"type": "message", "ts": "1485975421.000002", "channel": "D3ZEQULHZ"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_removed"}
diff --git a/_pytest/data/websocket/1485975458.35-channel_created.json b/_pytest/data/websocket/1485975458.35-channel_created.json
new file mode 100644
index 0000000..ca225d8
--- /dev/null
+++ b/_pytest/data/websocket/1485975458.35-channel_created.json
@@ -0,0 +1 @@
+{"event_ts": "1485975458.876318", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_created", "channel": {"is_shared": false, "is_channel": true, "creator": "U407ABLLW", "created": 1485975458, "is_org_shared": false, "id": "C3ZJKCGTU", "name": "some-channel"}}
diff --git a/_pytest/data/websocket/1485975458.87-channel_joined.json b/_pytest/data/websocket/1485975458.87-channel_joined.json
new file mode 100644
index 0000000..8991446
--- /dev/null
+++ b/_pytest/data/websocket/1485975458.87-channel_joined.json
@@ -0,0 +1 @@
+{"event_ts": "1485975458.876336", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_joined", "channel": {"previous_names": [], "is_general": false, "name": "some-channel", "is_channel": true, "created": 1485975458, "is_member": true, "unread_count_display": 0, "is_archived": false, "creator": "U407ABLLW", "topic": {"last_set": 0, "value": "", "creator": ""}, "unread_count": 0, "purpose": {"last_set": 1485975458, "value": "who knows?", "creator": "U407ABLLW"}, "members": ["U3ZKBBDL5", "U407ABLLW"], "last_read": "1485975458.000003", "id": "C3ZJKCGTU", "latest": {"text": "<@U407ABLLW|alice> set the channel purpose: who knows?", "ts": "1485975458.000003", "subtype": "channel_purpose", "purpose": "who knows?", "type": "message", "user": "U407ABLLW"}}}
diff --git a/_pytest/data/websocket/1485975458.87-message.json b/_pytest/data/websocket/1485975458.87-message.json
new file mode 100644
index 0000000..63f611c
--- /dev/null
+++ b/_pytest/data/websocket/1485975458.87-message.json
@@ -0,0 +1 @@
+{"type": "message", "user_profile": {"avatar_hash": "g6f7a5bf7eb7", "first_name": "Second", "image_72": "https://secure.gravatar.com/avatar/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png", "name": "bob", "real_name": "Second Testuser"}, "text": "<@U3ZKBBDL5|bob> has joined the channel", "ts": "1485975458.000004", "subtype": "channel_join", "user": "U3ZKBBDL5", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "inviter": "U407ABLLW", "channel": "C3ZJKCGTU"}
diff --git a/_pytest/data/websocket/1485975462.62-update_thread_state.json b/_pytest/data/websocket/1485975462.62-update_thread_state.json
new file mode 100644
index 0000000..7a5411a
--- /dev/null
+++ b/_pytest/data/websocket/1485975462.62-update_thread_state.json
@@ -0,0 +1 @@
+{"mention_count": 0, "event_ts": "1485975458.876340", "timestamp": "1485975458.741354", "has_unreads": false, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "update_thread_state"}
diff --git a/_pytest/data/websocket/1485975462.62-user_typing.json b/_pytest/data/websocket/1485975462.62-user_typing.json
new file mode 100644
index 0000000..ec19e94
--- /dev/null
+++ b/_pytest/data/websocket/1485975462.62-user_typing.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C3ZJKCGTU"}
diff --git a/_pytest/data/websocket/1485975476.62-channel_deleted.json b/_pytest/data/websocket/1485975476.62-channel_deleted.json
new file mode 100644
index 0000000..8dd1cb8
--- /dev/null
+++ b/_pytest/data/websocket/1485975476.62-channel_deleted.json
@@ -0,0 +1 @@
+{"event_ts": "1485975476.876638", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_deleted", "channel": "C3ZJKCGTU"}
diff --git a/_pytest/data/websocket/1485975476.86-group_join.json b/_pytest/data/websocket/1485975476.86-group_join.json
new file mode 100644
index 0000000..dc72eeb
--- /dev/null
+++ b/_pytest/data/websocket/1485975476.86-group_join.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_join", "user": "U407ABLLW", "channel": "G409GKN9M", "ts": "1485975476.000002"}
diff --git a/_pytest/data/websocket/1485975476.86-group_joined.json b/_pytest/data/websocket/1485975476.86-group_joined.json
new file mode 100644
index 0000000..bbd971c
--- /dev/null
+++ b/_pytest/data/websocket/1485975476.86-group_joined.json
@@ -0,0 +1 @@
+{"event_ts": "1485975476.876646", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_joined", "channel": {"name": "some-channel", "created": 1485975458, "is_mpim": false, "is_archived": false, "creator": "U407ABLLW", "is_group": true, "topic": {"last_set": 1485975476, "value": "", "creator": "U407ABLLW"}, "purpose": {"last_set": 1485975476, "value": "who knows?", "creator": "U407ABLLW"}, "members": ["U407ABLLW", "U3ZKBBDL5"], "is_starred": false, "id": "G409GKN9M"}}
diff --git a/_pytest/data/websocket/1485975487.69-group_history_changed.json b/_pytest/data/websocket/1485975487.69-group_history_changed.json
new file mode 100644
index 0000000..0196401
--- /dev/null
+++ b/_pytest/data/websocket/1485975487.69-group_history_changed.json
@@ -0,0 +1 @@
+{"event_ts": "1485975476.876650", "is_mpim": false, "ts": "1485975476.000004", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_history_changed", "channel": "G409GKN9M", "latest": "1485975476.876632"}
diff --git a/_pytest/data/websocket/1485975487.69-group_join.json b/_pytest/data/websocket/1485975487.69-group_join.json
new file mode 100644
index 0000000..0fdb54c
--- /dev/null
+++ b/_pytest/data/websocket/1485975487.69-group_join.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_join", "user": "U3ZKBBDL5", "channel": "G409GKN9M", "ts": "1485975476.000003"}
diff --git a/_pytest/data/websocket/1485975493.19-group_joined.json b/_pytest/data/websocket/1485975493.19-group_joined.json
new file mode 100644
index 0000000..76e7b55
--- /dev/null
+++ b/_pytest/data/websocket/1485975493.19-group_joined.json
@@ -0,0 +1 @@
+{"event_ts": "1485975492.876964", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_joined", "channel": {"name": "some-private-channel", "last_read": "1485975492.000002", "creator": "U407ABLLW", "is_mpim": false, "is_archived": false, "created": 1485975492, "is_group": true, "topic": {"last_set": 0, "value": "", "creator": ""}, "unread_count": 0, "is_open": true, "purpose": {"last_set": 0, "value": "", "creator": ""}, "members": ["U3ZKBBDL5", "U407ABLLW"], "latest": {"text": "<@U407ABLLW|alice> has joined the group", "subtype": "group_join", "type": "message", "user": "U407ABLLW", "ts": "1485975492.000002"}, "id": "G3ZJKP7GA", "unread_count_display": 0}}
diff --git a/_pytest/data/websocket/1485975493.23-message.json b/_pytest/data/websocket/1485975493.23-message.json
new file mode 100644
index 0000000..c6dad6c
--- /dev/null
+++ b/_pytest/data/websocket/1485975493.23-message.json
@@ -0,0 +1 @@
+{"type": "message", "user_profile": {"avatar_hash": "g6f7a5bf7eb7", "first_name": "Second", "image_72": "https://secure.gravatar.com/avatar/6f7a5bf7eb782853afb1d33f28ca9ae7.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0019-72.png", "name": "bob", "real_name": "Second Testuser"}, "text": "<@U3ZKBBDL5|bob> has joined the group", "ts": "1485975492.000003", "subtype": "group_join", "user": "U3ZKBBDL5", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "inviter": "U407ABLLW", "channel": "G3ZJKP7GA"}
diff --git a/_pytest/data/websocket/1485975547.75-message.json b/_pytest/data/websocket/1485975547.75-message.json
new file mode 100644
index 0000000..4826952
--- /dev/null
+++ b/_pytest/data/websocket/1485975547.75-message.json
@@ -0,0 +1 @@
+{"user_profile": {"avatar_hash": "gfd0ce7a168d", "first_name": "First", "image_72": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png", "name": "alice", "real_name": "First Testuser"}, "text": "<@U407ABLLW|alice> set the channel's purpose: seekret", "ts": "1485975492.000004", "subtype": "group_purpose", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "G3ZJKP7GA", "purpose": "seekret"}
diff --git a/_pytest/data/websocket/1485975547.75-update_thread_state.json b/_pytest/data/websocket/1485975547.75-update_thread_state.json
new file mode 100644
index 0000000..4d91b20
--- /dev/null
+++ b/_pytest/data/websocket/1485975547.75-update_thread_state.json
@@ -0,0 +1 @@
+{"mention_count": 0, "event_ts": "1485975492.876967", "timestamp": "1485975493.013414", "has_unreads": false, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "update_thread_state"}
diff --git a/_pytest/data/websocket/1485975547.83-reconnect_url.json b/_pytest/data/websocket/1485975547.83-reconnect_url.json
new file mode 100644
index 0000000..42da6c5
--- /dev/null
+++ b/_pytest/data/websocket/1485975547.83-reconnect_url.json
@@ -0,0 +1 @@
+{"url": "wss://mpmulti-t63z.slack-msgs.com/websocket/I4VgKhpT11zUwKt_Wajw_phIbuzM7xTy0V0DVLb6dyPDkqGU5-497Jn3bOGncotPX4QhfZepKVtlUkrSWCqMcbjH_vjRo91HBvzvk4nJoScjn6KJatZ6vpzokG44Ee-vNMsxXLeedcbraFAzCrLlODGRqvdqxUQDQFMVES0XHP8=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"}
diff --git a/_pytest/data/websocket/1485975606.59-team_join.json b/_pytest/data/websocket/1485975606.59-team_join.json
new file mode 100644
index 0000000..3af908e
--- /dev/null
+++ b/_pytest/data/websocket/1485975606.59-team_join.json
@@ -0,0 +1 @@
+{"event_ts": "1485975606.879253", "cache_ts": 1485975606, "type": "team_join", "user": {"status": null, "profile": {"first_name": "Chuck", "last_name": "Testuser", "fields": null, "real_name": "Chuck Testuser", "image_24": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png", "real_name_normalized": "Chuck Testuser", "image_512": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png", "image_32": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png", "image_48": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png", "avatar_hash": "g7f87f7015f8", "image_72": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png", "image_192": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png"}, "tz": "America/Los_Angeles", "name": "chuck", "presence": "away", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Chuck Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}}
diff --git a/_pytest/data/websocket/1485975606.67-message.json b/_pytest/data/websocket/1485975606.67-message.json
new file mode 100644
index 0000000..cfc8e5f
--- /dev/null
+++ b/_pytest/data/websocket/1485975606.67-message.json
@@ -0,0 +1 @@
+{"user_profile": {"avatar_hash": "g7f87f7015f8", "first_name": "Chuck", "image_72": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png", "name": "chuck", "real_name": "Chuck Testuser"}, "text": "<@U4096CBHC|chuck> has joined the channel", "ts": "1485975606.000003", "subtype": "channel_join", "user": "U4096CBHC", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"}
diff --git a/_pytest/data/websocket/1485975606.75-im_created.json b/_pytest/data/websocket/1485975606.75-im_created.json
new file mode 100644
index 0000000..fa360d8
--- /dev/null
+++ b/_pytest/data/websocket/1485975606.75-im_created.json
@@ -0,0 +1 @@
+{"event_ts": "1485975606.879278", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "im_created", "user": "U4096CBHC", "channel": {"last_read": "0000000000.000000", "created": 1485975606, "is_org_shared": false, "unread_count": 0, "is_open": false, "user": "U4096CBHC", "unread_count_display": 0, "is_im": true, "id": "D409J34CF", "latest": null}}
diff --git a/_pytest/data/websocket/1485975606.75-im_open.json b/_pytest/data/websocket/1485975606.75-im_open.json
new file mode 100644
index 0000000..f2a9696
--- /dev/null
+++ b/_pytest/data/websocket/1485975606.75-im_open.json
@@ -0,0 +1 @@
+{"event_ts": "1485975606.879280", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "im_open", "user": "U4096CBHC", "channel": "D409J34CF"}
diff --git a/_pytest/data/websocket/1485975611.29-presence_change.json b/_pytest/data/websocket/1485975611.29-presence_change.json
new file mode 100644
index 0000000..ec41e5f
--- /dev/null
+++ b/_pytest/data/websocket/1485975611.29-presence_change.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U4096CBHC", "presence": "active"}
diff --git a/_pytest/data/websocket/1485975640.5-user_change.json b/_pytest/data/websocket/1485975640.5-user_change.json
new file mode 100644
index 0000000..9c2b114
--- /dev/null
+++ b/_pytest/data/websocket/1485975640.5-user_change.json
@@ -0,0 +1 @@
+{"event_ts": "1485975640.879906", "cache_ts": 1485975640, "type": "user_change", "user": {"status": null, "profile": {"first_name": "Charles", "last_name": "Testuser", "fields": [], "real_name": "Charles Testuser", "image_24": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png", "real_name_normalized": "Charles Testuser", "image_512": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png", "image_32": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png", "image_48": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png", "avatar_hash": "g7f87f7015f8", "image_72": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png", "image_192": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png"}, "tz": "America/Los_Angeles", "name": "chuck", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Charles Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}}
diff --git a/_pytest/data/websocket/1485975669.14-user_change.json b/_pytest/data/websocket/1485975669.14-user_change.json
new file mode 100644
index 0000000..0561553
--- /dev/null
+++ b/_pytest/data/websocket/1485975669.14-user_change.json
@@ -0,0 +1 @@
+{"event_ts": "1485975668.880329", "cache_ts": 1485975669, "type": "user_change", "user": {"status": null, "profile": {"first_name": "Charles", "last_name": "Testuser", "fields": [], "real_name": "Charles Testuser", "image_24": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=24&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-24.png", "real_name_normalized": "Charles Testuser", "image_512": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=512&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-512.png", "image_32": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=32&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-32.png", "image_48": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=48&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-48.png", "avatar_hash": "g7f87f7015f8", "image_72": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0010-72.png", "image_192": "https://secure.gravatar.com/avatar/7f87f7015f8e5081190ece053e41b11e.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0010-192.png"}, "tz": "America/Los_Angeles", "name": "charles", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Charles Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}}
diff --git a/_pytest/data/websocket/1485975675.81-presence_change.json b/_pytest/data/websocket/1485975675.81-presence_change.json
new file mode 100644
index 0000000..ec41e5f
--- /dev/null
+++ b/_pytest/data/websocket/1485975675.81-presence_change.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U4096CBHC", "presence": "active"}
diff --git a/_pytest/data/websocket/1485975690.59-mpim_joined.json b/_pytest/data/websocket/1485975690.59-mpim_joined.json
new file mode 100644
index 0000000..d377778
--- /dev/null
+++ b/_pytest/data/websocket/1485975690.59-mpim_joined.json
@@ -0,0 +1 @@
+{"event_ts": "1485975690.880722", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "mpim_joined", "channel": {"name": "mpdm-bob--alice--charles-1", "last_read": "0000000000.000000", "creator": "U407ABLLW", "is_mpim": true, "is_archived": false, "created": 1485975690, "is_group": true, "topic": {"last_set": 1485975690, "value": "Group messaging", "creator": "U407ABLLW"}, "unread_count": 0, "is_open": false, "purpose": {"last_set": 1485975690, "value": "Group messaging with: @bob @alice @charles", "creator": "U407ABLLW"}, "members": ["U407ABLLW", "U3ZKBBDL5", "U4096CBHC"], "latest": null, "id": "G3ZGMF4RZ", "unread_count_display": 0}}
diff --git a/_pytest/data/websocket/1485975690.67-group_joined.json b/_pytest/data/websocket/1485975690.67-group_joined.json
new file mode 100644
index 0000000..b1c27dd
--- /dev/null
+++ b/_pytest/data/websocket/1485975690.67-group_joined.json
@@ -0,0 +1 @@
+{"event_ts": "1485975690.880723", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_joined", "channel": {"name": "mpdm-bob--alice--charles-1", "last_read": "0000000000.000000", "creator": "U407ABLLW", "is_mpim": true, "is_archived": false, "created": 1485975690, "is_group": true, "topic": {"last_set": 1485975690, "value": "Group messaging", "creator": "U407ABLLW"}, "unread_count": 0, "is_open": false, "purpose": {"last_set": 1485975690, "value": "Group messaging with: @bob @alice @charles", "creator": "U407ABLLW"}, "members": ["U407ABLLW", "U3ZKBBDL5", "U4096CBHC"], "latest": null, "id": "G3ZGMF4RZ", "unread_count_display": 0}}
diff --git a/_pytest/data/websocket/1485975690.67-update_thread_state.json b/_pytest/data/websocket/1485975690.67-update_thread_state.json
new file mode 100644
index 0000000..c745df6
--- /dev/null
+++ b/_pytest/data/websocket/1485975690.67-update_thread_state.json
@@ -0,0 +1 @@
+{"mention_count": 0, "event_ts": "1485975690.880724", "timestamp": "1485975690.537094", "has_unreads": false, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "update_thread_state"}
diff --git a/_pytest/data/websocket/1485975697.58-user_typing.json b/_pytest/data/websocket/1485975697.58-user_typing.json
new file mode 100644
index 0000000..b8239d4
--- /dev/null
+++ b/_pytest/data/websocket/1485975697.58-user_typing.json
@@ -0,0 +1 @@
+{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "G3ZGMF4RZ"}
diff --git a/_pytest/data/websocket/1485975698.45-message.json b/_pytest/data/websocket/1485975698.45-message.json
new file mode 100644
index 0000000..2118ec7
--- /dev/null
+++ b/_pytest/data/websocket/1485975698.45-message.json
@@ -0,0 +1 @@
+{"text": "surely", "ts": "1485975698.000002", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "G3ZGMF4RZ"}
diff --git a/_pytest/data/websocket/1485975701.72-reaction_added.json b/_pytest/data/websocket/1485975701.72-reaction_added.json
new file mode 100644
index 0000000..b4d2282
--- /dev/null
+++ b/_pytest/data/websocket/1485975701.72-reaction_added.json
@@ -0,0 +1 @@
+{"reaction": "unamused", "event_ts": "1485975701.880957", "ts": "1485975701.000003", "item": {"type": "message", "ts": "1485975698.000002", "channel": "G3ZGMF4RZ"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_added"}
diff --git a/_pytest/data/websocket/1485975703.22-reaction_removed.json b/_pytest/data/websocket/1485975703.22-reaction_removed.json
new file mode 100644
index 0000000..3deafb2
--- /dev/null
+++ b/_pytest/data/websocket/1485975703.22-reaction_removed.json
@@ -0,0 +1 @@
+{"reaction": "unamused", "event_ts": "1485975703.880979", "ts": "1485975703.000004", "item": {"type": "message", "ts": "1485975698.000002", "channel": "G3ZGMF4RZ"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_removed"}
diff --git a/_pytest/data/websocket/1485975715.79-message.json b/_pytest/data/websocket/1485975715.79-message.json
new file mode 100644
index 0000000..5d3c51c
--- /dev/null
+++ b/_pytest/data/websocket/1485975715.79-message.json
@@ -0,0 +1 @@
+{"username": "<@U407ABLLW|alice>", "source_team": "T3YS5EAL9", "user_profile": {"avatar_hash": "gfd0ce7a168d", "first_name": "First", "image_72": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png", "name": "alice", "real_name": "First Testuser"}, "text": "<@U407ABLLW|alice> uploaded a file: ", "team": "T3YS5EAL9", "upload": true, "ts": "1485975715.000005", "display_as_bot": false, "user": "U407ABLLW", "file": {"filetype": "text", "lines_more": 0, "channels": [], "display_as_bot": false, "id": "F3ZLY6K5J", "size": 14, "title": "Untitled", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZLY6K5J/-.txt", "ims": [], "preview": "some code here", "external_type": "", "edit_link": "https://weeslacktest.slack.com/files/alice/F3ZLY6K5J/-.txt/edit", "username": "", "timestamp": 1485975715, "public_url_shared": false, "editable": true, "preview_highlight": "
\n
\n
some code here
\n
\n
\n", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZLY6K5J/download/-.txt", "user": "U407ABLLW", "groups": [], "is_public": false, "pretty_type": "Plain Text", "is_external": false, "mimetype": "text/plain", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZLY6K5J-39c2c4f739", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZLY6K5J/-.txt", "name": "-.txt", "created": 1485975715, "lines": 1, "comments_count": 0, "mode": "snippet", "preview_is_truncated": false}, "subtype": "file_share", "user_team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "G3ZGMF4RZ", "bot_id": null} diff --git a/_pytest/data/websocket/1485975715.87-file_shared.json b/_pytest/data/websocket/1485975715.87-file_shared.json new file mode 100644 index 0000000..526f72c --- /dev/null +++ b/_pytest/data/websocket/1485975715.87-file_shared.json @@ -0,0 +1 @@ +{"user_id": "U407ABLLW", "event_ts": "1485975715.881302", "file_id": "F3ZLY6K5J", "file": {"id": "F3ZLY6K5J"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_shared"} diff --git a/_pytest/data/websocket/1485975723.85-message.json b/_pytest/data/websocket/1485975723.85-message.json new file mode 100644 index 0000000..d06d025 --- /dev/null +++ b/_pytest/data/websocket/1485975723.85-message.json @@ -0,0 +1 @@ +{"event_ts": "1485975723.881563", "ts": "1485975723.000006", "subtype": "message_deleted", "hidden": true, "previous_message": {"text": "surely", "type": "message", "user": "U407ABLLW", "ts": "1485975698.000002"}, "type": "message", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "channel": "G3ZGMF4RZ", "deleted_ts": "1485975698.000002"} diff --git a/_pytest/data/websocket/1485975727.84-reconnect_url.json b/_pytest/data/websocket/1485975727.84-reconnect_url.json new file mode 100644 index 0000000..3630d02 --- /dev/null +++ b/_pytest/data/websocket/1485975727.84-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-824w.slack-msgs.com/websocket/Pl7CWLLi3w1S0gHESj79_JySeV0ho6vZRl1usylFoDVBWTj332mKkaLBR6OdrDqRDlYPnwwnh9TqlqOMVajfyHMH_Q02oVU14YJS_ao5_nEJBBd58vDB3XkKfGdPRZNgP2Id05xTpEy9izt9EF_BdDaDaTU9nGc5c2ggnuS9gos=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} diff --git a/_pytest/data/websocket/1485975735.91-group_archive.json b/_pytest/data/websocket/1485975735.91-group_archive.json new file mode 100644 index 0000000..61cfdc7 --- /dev/null +++ b/_pytest/data/websocket/1485975735.91-group_archive.json @@ -0,0 +1 @@ +{"event_ts": "1485975735.881780", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_archive", "ts": "1485975735.000005", "channel": "G409GKN9M"} diff --git a/_pytest/data/websocket/1485975735.99-message.json b/_pytest/data/websocket/1485975735.99-message.json new file mode 100644 index 0000000..10f541f --- /dev/null +++ b/_pytest/data/websocket/1485975735.99-message.json @@ -0,0 +1 @@ +{"user_profile": {"avatar_hash": "gfd0ce7a168d", "first_name": "First", "image_72": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png", "name": "alice", "real_name": "First Testuser"}, "text": "<@U407ABLLW|alice> archived the private channel", "ts": "1485975735.000006", "subtype": "group_archive", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "G409GKN9M"} diff --git a/_pytest/data/websocket/1485975736.08-message.json b/_pytest/data/websocket/1485975736.08-message.json new file mode 100644 index 0000000..147dbb4 --- /dev/null +++ b/_pytest/data/websocket/1485975736.08-message.json @@ -0,0 +1 @@ +{"text": "<@U407ABLLW|alice> archived the private channel ", "ts": "1485975735.000002", "user": "USLACKBOT", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "D3ZK1D8JY"} diff --git a/_pytest/data/websocket/1485975738.1-desktop_notification.json b/_pytest/data/websocket/1485975738.1-desktop_notification.json new file mode 100644 index 0000000..da451bb --- /dev/null +++ b/_pytest/data/websocket/1485975738.1-desktop_notification.json @@ -0,0 +1 @@ +{"avatarImage": "https://a.slack-edge.com/66f9/img/slackbot_192.png", "subtitle": "slackbot", "is_shared": false, "title": "weeslacktest", "ssbFilename": "knock_brush.mp3", "imageUri": null, "launchUri": "slack://channel?id=D3ZK1D8JY&message=1485975735000002&team=T3YS5EAL9", "event_ts": "1485975737.881829", "msg": "1485975735.000002", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "content": "@alice archived the private channel some-channel", "type": "desktop_notification", "channel": "D3ZK1D8JY"} diff --git a/_pytest/data/websocket/1485975747.17-channel_created.json b/_pytest/data/websocket/1485975747.17-channel_created.json new file mode 100644 index 0000000..6bcafdb --- /dev/null +++ b/_pytest/data/websocket/1485975747.17-channel_created.json @@ -0,0 +1 @@ +{"event_ts": "1485975746.882009", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_created", "channel": {"is_shared": false, "is_channel": true, "creator": "U407ABLLW", "created": 1485975747, "is_org_shared": false, "id": "C3ZM8JTD3", "name": "some-channel2"}} diff --git a/_pytest/data/websocket/1485975757.63-channel_rename.json b/_pytest/data/websocket/1485975757.63-channel_rename.json new file mode 100644 index 0000000..a921fed --- /dev/null +++ b/_pytest/data/websocket/1485975757.63-channel_rename.json @@ -0,0 +1 @@ +{"event_ts": "1485975757.882178", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_rename", "channel": {"name": "some-channel2-renamed", "id": "C3ZM8JTD3", "is_channel": true, "created": "1485975747"}} diff --git a/_pytest/data/websocket/1485975771.6-presence_change.json b/_pytest/data/websocket/1485975771.6-presence_change.json new file mode 100644 index 0000000..e70421c --- /dev/null +++ b/_pytest/data/websocket/1485975771.6-presence_change.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U407ABLLW", "presence": "away"} diff --git a/_pytest/data/websocket/1485975774.62-presence_change.json b/_pytest/data/websocket/1485975774.62-presence_change.json new file mode 100644 index 0000000..9728fae --- /dev/null +++ b/_pytest/data/websocket/1485975774.62-presence_change.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U407ABLLW", "presence": "active"} diff --git a/_pytest/data/websocket/1485975822.17-user_typing.json b/_pytest/data/websocket/1485975822.17-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485975822.17-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975824.48-message.json b/_pytest/data/websocket/1485975824.48-message.json new file mode 100644 index 0000000..9fae542 --- /dev/null +++ b/_pytest/data/websocket/1485975824.48-message.json @@ -0,0 +1 @@ +{"text": "generally, yep!", "ts": "1485975824.000004", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975836.23-message.json b/_pytest/data/websocket/1485975836.23-message.json new file mode 100644 index 0000000..6715bed --- /dev/null +++ b/_pytest/data/websocket/1485975836.23-message.json @@ -0,0 +1 @@ +{"thread_ts": "1485975824.000004", "text": "have you met my friend threads?", "ts": "1485975835.000005", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975836.31-message.json b/_pytest/data/websocket/1485975836.31-message.json new file mode 100644 index 0000000..3823c61 --- /dev/null +++ b/_pytest/data/websocket/1485975836.31-message.json @@ -0,0 +1 @@ +{"hidden": true, "event_ts": "1485975835.883772", "ts": "1485975835.000006", "subtype": "message_replied", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "message": {"thread_ts": "1485975824.000004", "text": "generally, yep!", "ts": "1485975824.000004", "reply_count": 1, "user": "U407ABLLW", "replies": [{"user": "U407ABLLW", "ts": "1485975835.000005"}], "type": "message"}, "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975842.1-message.json b/_pytest/data/websocket/1485975842.1-message.json new file mode 100644 index 0000000..b94a913 --- /dev/null +++ b/_pytest/data/websocket/1485975842.1-message.json @@ -0,0 +1 @@ +{"thread_ts": "1485975824.000004", "text": "react if yes", "ts": "1485975841.000007", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975842.18-message.json b/_pytest/data/websocket/1485975842.18-message.json new file mode 100644 index 0000000..7bc2b38 --- /dev/null +++ b/_pytest/data/websocket/1485975842.18-message.json @@ -0,0 +1 @@ +{"hidden": true, "event_ts": "1485975841.883922", "ts": "1485975841.000008", "subtype": "message_replied", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "message": {"thread_ts": "1485975824.000004", "text": "generally, yep!", "ts": "1485975824.000004", "reply_count": 2, "user": "U407ABLLW", "replies": [{"user": "U407ABLLW", "ts": "1485975835.000005"}, {"user": "U407ABLLW", "ts": "1485975841.000007"}], "type": "message"}, "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485975850.32-mpim_open.json b/_pytest/data/websocket/1485975850.32-mpim_open.json new file mode 100644 index 0000000..ebce13e --- /dev/null +++ b/_pytest/data/websocket/1485975850.32-mpim_open.json @@ -0,0 +1 @@ +{"event_ts": "1485975849.884091", "is_mpim": true, "user": "U3ZKBBDL5", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "mpim_open", "channel": "G3ZGMF4RZ"} diff --git a/_pytest/data/websocket/1485975850.45-group_open.json b/_pytest/data/websocket/1485975850.45-group_open.json new file mode 100644 index 0000000..beb27d3 --- /dev/null +++ b/_pytest/data/websocket/1485975850.45-group_open.json @@ -0,0 +1 @@ +{"event_ts": "1485975849.884092", "is_mpim": true, "user": "U3ZKBBDL5", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_open", "channel": "G3ZGMF4RZ"} diff --git a/_pytest/data/websocket/1485975858.56-reaction_added.json b/_pytest/data/websocket/1485975858.56-reaction_added.json new file mode 100644 index 0000000..978d30f --- /dev/null +++ b/_pytest/data/websocket/1485975858.56-reaction_added.json @@ -0,0 +1 @@ +{"reaction": "stuck_out_tongue", "event_ts": "1485975858.884268", "item": {"type": "message", "ts": "1485975841.000007", "channel": "C407ABS94"}, "user": "U4096CBHC", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_added"} diff --git a/_pytest/data/websocket/1485975890.7-bot_added.json b/_pytest/data/websocket/1485975890.7-bot_added.json new file mode 100644 index 0000000..91254cf --- /dev/null +++ b/_pytest/data/websocket/1485975890.7-bot_added.json @@ -0,0 +1 @@ +{"event_ts": "1485975890.885679", "type": "bot_added", "cache_ts": 1485975890, "bot": {"deleted": false, "id": "B3YTBU6L8", "app_id": "A0F7XDUAZ", "name": "incoming-webhook", "icons": {"image_36": "https://a.slack-edge.com/12b5a/plugins/tester/assets/service_36.png", "image_48": "https://a.slack-edge.com/12b5a/plugins/tester/assets/service_48.png", "image_72": "https://a.slack-edge.com/12b5a/plugins/tester/assets/service_72.png"}}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}} diff --git a/_pytest/data/websocket/1485975890.78-message.json b/_pytest/data/websocket/1485975890.78-message.json new file mode 100644 index 0000000..785faa6 --- /dev/null +++ b/_pytest/data/websocket/1485975890.78-message.json @@ -0,0 +1 @@ +{"text": "added an integration to this channel: ", "ts": "1485975890.000009", "subtype": "bot_add", "user": "U4096CBHC", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94", "bot_id": "B3YTBU6L8"} diff --git a/_pytest/data/websocket/1485975896.16-message.json b/_pytest/data/websocket/1485975896.16-message.json new file mode 100644 index 0000000..3a9db89 --- /dev/null +++ b/_pytest/data/websocket/1485975896.16-message.json @@ -0,0 +1 @@ +{"text": "disabled an integration in this channel: ", "ts": "1485975896.000010", "subtype": "bot_disable", "user": "U4096CBHC", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94", "bot_id": "B3YTBU6L8"} diff --git a/_pytest/data/websocket/1485975907.84-reconnect_url.json b/_pytest/data/websocket/1485975907.84-reconnect_url.json new file mode 100644 index 0000000..a2f99c1 --- /dev/null +++ b/_pytest/data/websocket/1485975907.84-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-1r8c.slack-msgs.com/websocket/8atJCMnYZ10YaqNai-zwEaNMfkwh85XwRpc0MYVVOzAojnPcrx8SboF-NBWeep6Hy7arytqEffr_Fh7mPrDagwEGwbOeX-OH3OLlubVjpC2cCLWwm2jN3rEZcq8A4j0tpPP56GZ84jbLn4BKvDhKkkKhRXQhuYtC7kskaXXLTbk=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} diff --git a/_pytest/data/websocket/1485975926.59-user_change.json b/_pytest/data/websocket/1485975926.59-user_change.json new file mode 100644 index 0000000..179a67d --- /dev/null +++ b/_pytest/data/websocket/1485975926.59-user_change.json @@ -0,0 +1 @@ +{"event_ts": "1485975926.886492", "cache_ts": 1485975926, "type": "user_change", "user": {"status": null, "profile": {"first_name": "Charles", "last_name": "Testuser", "fields": [], "real_name": "Charles Testuser", "image_24": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_original": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_original.png", "real_name_normalized": "Charles Testuser", "image_512": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_32": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_48": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "avatar_hash": "5af404f7d4b7", "image_72": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_1024": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_192": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png"}, "tz": "America/Los_Angeles", "name": "charles", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Charles Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}} diff --git a/_pytest/data/websocket/1485975930.81-user_change.json b/_pytest/data/websocket/1485975930.81-user_change.json new file mode 100644 index 0000000..afbbb1b --- /dev/null +++ b/_pytest/data/websocket/1485975930.81-user_change.json @@ -0,0 +1 @@ +{"event_ts": "1485975930.886576", "cache_ts": 1485975930, "type": "user_change", "user": {"status": null, "profile": {"first_name": "Charles", "last_name": "Testuser", "fields": [], "real_name": "Charles Testuser", "image_24": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_original": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_original.png", "real_name_normalized": "Charles Testuser", "image_512": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_32": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_48": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "avatar_hash": "5af404f7d4b7", "image_72": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_1024": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png", "image_192": "https://avatars.slack-edge.com/2017-02-01/135714629508_5af404f7d4b7728d10c0_24.png"}, "tz": "America/Los_Angeles", "name": "charles", "deleted": false, "is_bot": false, "tz_label": "Pacific Standard Time", "real_name": "Charles Testuser", "color": "e7392d", "team_id": "T3YS5EAL9", "is_admin": false, "is_ultra_restricted": false, "is_restricted": false, "tz_offset": -28800, "is_primary_owner": false, "id": "U4096CBHC", "is_owner": false}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}} diff --git a/_pytest/data/websocket/1485975956.38-file_public.json b/_pytest/data/websocket/1485975956.38-file_public.json new file mode 100644 index 0000000..288b528 --- /dev/null +++ b/_pytest/data/websocket/1485975956.38-file_public.json @@ -0,0 +1 @@ +{"user_id": "U407ABLLW", "event_ts": "1485975956.887078", "file_id": "F3ZJQTA66", "file": {"id": "F3ZJQTA66"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_public"} diff --git a/_pytest/data/websocket/1485975956.47-file_shared.json b/_pytest/data/websocket/1485975956.47-file_shared.json new file mode 100644 index 0000000..1064ac7 --- /dev/null +++ b/_pytest/data/websocket/1485975956.47-file_shared.json @@ -0,0 +1 @@ +{"user_id": "U407ABLLW", "event_ts": "1485975956.887081", "file_id": "F3ZJQTA66", "file": {"id": "F3ZJQTA66"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_shared"} diff --git a/_pytest/data/websocket/1485975956.47-message.json b/_pytest/data/websocket/1485975956.47-message.json new file mode 100644 index 0000000..04e67a8 --- /dev/null +++ b/_pytest/data/websocket/1485975956.47-message.json @@ -0,0 +1 @@ +{"username": "<@U407ABLLW|alice>", "source_team": "T3YS5EAL9", "user_profile": {"avatar_hash": "gfd0ce7a168d", "first_name": "First", "image_72": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2F66f9%2Fimg%2Favatars%2Fava_0024-72.png", "name": "alice", "real_name": "First Testuser"}, "text": "<@U407ABLLW|alice> uploaded a file: ", "team": "T3YS5EAL9", "upload": true, "ts": "1485975956.000011", "display_as_bot": false, "user": "U407ABLLW", "file": {"image_exif_rotation": 1, "filetype": "png", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3ZJQTA66", "size": 68, "original_h": 1, "thumb_360_w": 1, "title": "1x1.png", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png", "thumb_360": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png", "thumb_64": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png", "ims": [], "thumb_80": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png", "thumb_360_h": 1, "external_type": "", "username": "", "timestamp": 1485975955, "public_url_shared": false, "editable": false, "thumb_160": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png", "user": "U407ABLLW", "groups": [], "is_public": true, "pretty_type": "PNG", "is_external": false, "mimetype": "image/png", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png", "name": "1x1.png", "created": 1485975955, "original_w": 1, "comments_count": 0, "mode": "hosted"}, "subtype": "file_share", "user_team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94", "bot_id": null} diff --git a/_pytest/data/websocket/1485975978.79-message.json b/_pytest/data/websocket/1485975978.79-message.json new file mode 100644 index 0000000..f7c1846 --- /dev/null +++ b/_pytest/data/websocket/1485975978.79-message.json @@ -0,0 +1 @@ +{"username": "<@U407ABLLW|alice>", "subtype": "file_share", "text": "<@U407ABLLW|alice> shared a file: ", "upload": false, "ts": "1485975978.000012", "display_as_bot": false, "user": "U407ABLLW", "file": {"filetype": "space", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3YTCL8TA", "size": 73, "title": "some post here", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/some_post_here", "ims": [], "state": "locked", "editor": "U407ABLLW", "preview": null, "external_type": "", "username": "", "updated": 1485975959, "timestamp": 1485975967, "public_url_shared": false, "editable": true, "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/download/some_post_here", "user": "U407ABLLW", "groups": [], "is_public": false, "last_editor": "U407ABLLW", "pretty_type": "Post", "is_external": false, "mimetype": "text/plain", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3YTCL8TA-9d9391a713", "permalink": "https://weeslacktest.slack.com/files/alice/F3YTCL8TA/some_post_here", "name": "some_post_here", "created": 1485975959, "comments_count": 0, "mode": "space"}, "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94", "bot_id": null} diff --git a/_pytest/data/websocket/1485975978.91-file_public.json b/_pytest/data/websocket/1485975978.91-file_public.json new file mode 100644 index 0000000..820f802 --- /dev/null +++ b/_pytest/data/websocket/1485975978.91-file_public.json @@ -0,0 +1 @@ +{"user_id": "U407ABLLW", "event_ts": "1485975978.887563", "file_id": "F3YTCL8TA", "file": {"id": "F3YTCL8TA"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_public"} diff --git a/_pytest/data/websocket/1485975978.91-file_shared.json b/_pytest/data/websocket/1485975978.91-file_shared.json new file mode 100644 index 0000000..ede98ba --- /dev/null +++ b/_pytest/data/websocket/1485975978.91-file_shared.json @@ -0,0 +1 @@ +{"user_id": "U407ABLLW", "event_ts": "1485975978.887560", "file_id": "F3YTCL8TA", "file": {"id": "F3YTCL8TA"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_shared"} diff --git a/_pytest/data/websocket/1485975995.98-file_change.json b/_pytest/data/websocket/1485975995.98-file_change.json new file mode 100644 index 0000000..9744692 --- /dev/null +++ b/_pytest/data/websocket/1485975995.98-file_change.json @@ -0,0 +1 @@ +{"user_id": "U407ABLLW", "event_ts": "1485975995.887846", "file_id": "F3YTCL8TA", "file": {"id": "F3YTCL8TA"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_change"} diff --git a/_pytest/data/websocket/1485975998.13-file_change.json b/_pytest/data/websocket/1485975998.13-file_change.json new file mode 100644 index 0000000..8933dd7 --- /dev/null +++ b/_pytest/data/websocket/1485975998.13-file_change.json @@ -0,0 +1 @@ +{"user_id": "U407ABLLW", "event_ts": "1485975998.887906", "file_id": "F3YTCL8TA", "file": {"id": "F3YTCL8TA"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_change"} diff --git a/_pytest/data/websocket/1485976002.57-file_deleted.json b/_pytest/data/websocket/1485976002.57-file_deleted.json new file mode 100644 index 0000000..34719d0 --- /dev/null +++ b/_pytest/data/websocket/1485976002.57-file_deleted.json @@ -0,0 +1 @@ +{"event_ts": "1485976002.888005", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_deleted", "file_id": "F3YTCL8TA"} diff --git a/_pytest/data/websocket/1485976002.66-message.json b/_pytest/data/websocket/1485976002.66-message.json new file mode 100644 index 0000000..cb7bc08 --- /dev/null +++ b/_pytest/data/websocket/1485976002.66-message.json @@ -0,0 +1 @@ +{"event_ts": "1485976002.888006", "ts": "1485976002.000013", "subtype": "message_deleted", "hidden": true, "previous_message": {"username": "<@U407ABLLW|alice>", "subtype": "file_share", "text": "<@U407ABLLW|alice> shared a file: ", "upload": false, "ts": "1485975978.000012", "display_as_bot": false, "user": "U407ABLLW", "file": {"filetype": "space", "channels": [], "display_as_bot": false, "id": "F3YTCL8TA", "size": 73, "title": "some post here", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/some_post_here", "ims": [], "state": "locked", "editor": "U407ABLLW", "preview": null, "external_type": "", "username": "", "updated": 1485975959, "timestamp": 1485975967, "public_url_shared": false, "editable": true, "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3YTCL8TA/download/some_post_here", "user": "U407ABLLW", "groups": [], "is_public": true, "last_editor": "U407ABLLW", "pretty_type": "Post", "is_external": false, "mimetype": "text/plain", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3YTCL8TA-9d9391a713", "permalink": "https://weeslacktest.slack.com/files/alice/F3YTCL8TA/some_post_here", "name": "some_post_here", "created": 1485975959, "comments_count": 0, "mode": "space"}, "type": "message", "bot_id": null}, "type": "message", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "channel": "C407ABS94", "deleted_ts": "1485975978.000012"} diff --git a/_pytest/data/websocket/1485976024.46-user_typing.json b/_pytest/data/websocket/1485976024.46-user_typing.json new file mode 100644 index 0000000..b8239d4 --- /dev/null +++ b/_pytest/data/websocket/1485976024.46-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "G3ZGMF4RZ"} diff --git a/_pytest/data/websocket/1485976028.31-user_typing.json b/_pytest/data/websocket/1485976028.31-user_typing.json new file mode 100644 index 0000000..8307f14 --- /dev/null +++ b/_pytest/data/websocket/1485976028.31-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "G3ZJKP7GA"} diff --git a/_pytest/data/websocket/1485976039.32-reaction_added.json b/_pytest/data/websocket/1485976039.32-reaction_added.json new file mode 100644 index 0000000..864ea68 --- /dev/null +++ b/_pytest/data/websocket/1485976039.32-reaction_added.json @@ -0,0 +1 @@ +{"reaction": "unamused", "event_ts": "1485976039.888795", "item": {"type": "message", "ts": "1485975841.000007", "channel": "C407ABS94"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_added"} diff --git a/_pytest/data/websocket/1485976040.8-reaction_removed.json b/_pytest/data/websocket/1485976040.8-reaction_removed.json new file mode 100644 index 0000000..3553a10 --- /dev/null +++ b/_pytest/data/websocket/1485976040.8-reaction_removed.json @@ -0,0 +1 @@ +{"reaction": "unamused", "event_ts": "1485976040.888829", "item": {"type": "message", "ts": "1485975841.000007", "channel": "C407ABS94"}, "user": "U407ABLLW", "item_user": "U407ABLLW", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reaction_removed"} diff --git a/_pytest/data/websocket/1485976087.84-reconnect_url.json b/_pytest/data/websocket/1485976087.84-reconnect_url.json new file mode 100644 index 0000000..40308ce --- /dev/null +++ b/_pytest/data/websocket/1485976087.84-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-clcz.slack-msgs.com/websocket/ZwLc8-LUIYLyvEwRO5WKs99SLtxBN9Cyu1W5qmqkre0AtWFl_H8xTE7HbF0E9LUvgI4bNkIRG8WgPxIzfr5gLJsU6vg8By36_mUUZYfH0TV65y_h2vV7NQe6s3A6WJdKRoJAe_EI2AN5L-VeL9rK1Ygc0nw3ngmuL78G6cm48xw=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} diff --git a/_pytest/data/websocket/1485976107.57-channel_created.json b/_pytest/data/websocket/1485976107.57-channel_created.json new file mode 100644 index 0000000..b061743 --- /dev/null +++ b/_pytest/data/websocket/1485976107.57-channel_created.json @@ -0,0 +1 @@ +{"event_ts": "1485976107.890695", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_created", "channel": {"is_shared": false, "is_channel": true, "creator": "U407ABLLW", "created": 1485976107, "is_org_shared": false, "id": "C3ZM2GMGU", "name": "made-to-be-archived"}} diff --git a/_pytest/data/websocket/1485976114.72-user_typing.json b/_pytest/data/websocket/1485976114.72-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976114.72-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976117.99-user_typing.json b/_pytest/data/websocket/1485976117.99-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976117.99-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976124.9-user_typing.json b/_pytest/data/websocket/1485976124.9-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976124.9-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976124.98-message.json b/_pytest/data/websocket/1485976124.98-message.json new file mode 100644 index 0000000..26ce4e5 --- /dev/null +++ b/_pytest/data/websocket/1485976124.98-message.json @@ -0,0 +1 @@ +{"text": "referencing someong by <@U3ZKBBDL5> name", "ts": "1485976124.000014", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976125.06-desktop_notification.json b/_pytest/data/websocket/1485976125.06-desktop_notification.json new file mode 100644 index 0000000..ddd6dff --- /dev/null +++ b/_pytest/data/websocket/1485976125.06-desktop_notification.json @@ -0,0 +1 @@ +{"avatarImage": "https://secure.gravatar.com/avatar/fd0ce7a168dc2235d21c53c2c1cfee0c.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0024-192.png", "subtitle": "#general", "is_shared": false, "title": "weeslacktest", "ssbFilename": "knock_brush.mp3", "imageUri": null, "launchUri": "slack://channel?id=C407ABS94&message=1485976124000014&team=T3YS5EAL9", "event_ts": "1485976124.891186", "msg": "1485976124.000014", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "content": "alice: referencing someong by @bob name", "type": "desktop_notification", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976125.95-user_typing.json b/_pytest/data/websocket/1485976125.95-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976125.95-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976129.49-user_typing.json b/_pytest/data/websocket/1485976129.49-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976129.49-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976133.0-user_typing.json b/_pytest/data/websocket/1485976133.0-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976133.0-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976137.51-user_typing.json b/_pytest/data/websocket/1485976137.51-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976137.51-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976137.95-message.json b/_pytest/data/websocket/1485976137.95-message.json new file mode 100644 index 0000000..f4579e5 --- /dev/null +++ b/_pytest/data/websocket/1485976137.95-message.json @@ -0,0 +1 @@ +{"text": "referencing someone else by <@U407ABLLW> name", "ts": "1485976137.000015", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976138.73-user_typing.json b/_pytest/data/websocket/1485976138.73-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976138.73-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976141.79-user_typing.json b/_pytest/data/websocket/1485976141.79-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976141.79-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976145.8-user_typing.json b/_pytest/data/websocket/1485976145.8-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976145.8-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976149.89-user_typing.json b/_pytest/data/websocket/1485976149.89-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976149.89-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976151.6-message.json b/_pytest/data/websocket/1485976151.6-message.json new file mode 100644 index 0000000..7efa65d --- /dev/null +++ b/_pytest/data/websocket/1485976151.6-message.json @@ -0,0 +1 @@ +{"text": "referencing a #channel-that-doesnt-exist", "ts": "1485976151.000016", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976157.18-message.json b/_pytest/data/websocket/1485976157.18-message.json new file mode 100644 index 0000000..62244b4 --- /dev/null +++ b/_pytest/data/websocket/1485976157.18-message.json @@ -0,0 +1 @@ +{"hidden": true, "event_ts": "1485976156.891735", "ts": "1485976156.000017", "subtype": "message_changed", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "message": {"text": "referencing a <#C407ABS94|general>", "edited": {"user": "U407ABLLW", "ts": "1485976157.000000"}, "type": "message", "user": "U407ABLLW", "ts": "1485976151.000016"}, "channel": "C407ABS94", "previous_message": {"text": "referencing a #channel-that-doesnt-exist", "type": "message", "user": "U407ABLLW", "ts": "1485976151.000016"}} diff --git a/_pytest/data/websocket/1485976157.8-user_typing.json b/_pytest/data/websocket/1485976157.8-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976157.8-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976161.29-user_typing.json b/_pytest/data/websocket/1485976161.29-user_typing.json new file mode 100644 index 0000000..df57e72 --- /dev/null +++ b/_pytest/data/websocket/1485976161.29-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976161.75-message.json b/_pytest/data/websocket/1485976161.75-message.json new file mode 100644 index 0000000..f0b899b --- /dev/null +++ b/_pytest/data/websocket/1485976161.75-message.json @@ -0,0 +1 @@ +{"text": "referencing <#C3ZM8JTD3|some-channel2-renamed>", "ts": "1485976161.000018", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976182.59-channel_archive.json b/_pytest/data/websocket/1485976182.59-channel_archive.json new file mode 100644 index 0000000..4ebdb1f --- /dev/null +++ b/_pytest/data/websocket/1485976182.59-channel_archive.json @@ -0,0 +1 @@ +{"event_ts": "1485976182.892242", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_archive", "user": "U407ABLLW", "channel": "C3ZM2GMGU"} diff --git a/_pytest/data/websocket/1485976186.62-channel_unarchive.json b/_pytest/data/websocket/1485976186.62-channel_unarchive.json new file mode 100644 index 0000000..c95897d --- /dev/null +++ b/_pytest/data/websocket/1485976186.62-channel_unarchive.json @@ -0,0 +1 @@ +{"event_ts": "1485976186.892309", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_unarchive", "user": "U407ABLLW", "channel": "C3ZM2GMGU"} diff --git a/_pytest/data/websocket/1485976236.58-message.json b/_pytest/data/websocket/1485976236.58-message.json new file mode 100644 index 0000000..f09b294 --- /dev/null +++ b/_pytest/data/websocket/1485976236.58-message.json @@ -0,0 +1 @@ +{"comment": {"comment": "first comment!", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment!", "ts": "1485976236.000019", "subtype": "file_comment", "is_intro": false, "file": {"image_exif_rotation": 1, "filetype": "png", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3ZJQTA66", "size": 68, "original_h": 1, "thumb_360_w": 1, "title": "1x1.png", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png", "thumb_360": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png", "thumb_64": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png", "ims": [], "thumb_80": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png", "thumb_360_h": 1, "external_type": "", "username": "", "timestamp": 1485975955, "public_url_shared": false, "editable": false, "thumb_160": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png", "user": "U407ABLLW", "groups": [], "is_public": true, "pretty_type": "PNG", "is_external": false, "mimetype": "image/png", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png", "name": "1x1.png", "created": 1485975955, "original_w": 1, "comments_count": 1, "mode": "hosted"}, "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} diff --git a/_pytest/data/websocket/1485976236.67-file_comment_added.json b/_pytest/data/websocket/1485976236.67-file_comment_added.json new file mode 100644 index 0000000..82c1f0c --- /dev/null +++ b/_pytest/data/websocket/1485976236.67-file_comment_added.json @@ -0,0 +1 @@ +{"comment": {"comment": "first comment!", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "user_id": "U407ABLLW", "event_ts": "1485976236.893560", "file_id": "F3ZJQTA66", "file": {"id": "F3ZJQTA66"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_comment_added"} diff --git a/_pytest/data/websocket/1485976248.57-message.json b/_pytest/data/websocket/1485976248.57-message.json new file mode 100644 index 0000000..d3c8ce6 --- /dev/null +++ b/_pytest/data/websocket/1485976248.57-message.json @@ -0,0 +1 @@ +{"hidden": true, "event_ts": "1485976248.893799", "ts": "1485976248.000020", "subtype": "message_changed", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "message": {"comment": {"comment": "first comment! now i edited it.", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.", "ts": "1485976236.000019", "subtype": "file_comment", "is_intro": false, "file": {"image_exif_rotation": 1, "filetype": "png", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3ZJQTA66", "size": 68, "original_h": 1, "thumb_360_w": 1, "title": "1x1.png", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png", "thumb_360": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png", "thumb_64": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png", "ims": [], "thumb_80": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png", "thumb_360_h": 1, "external_type": "", "username": "", "timestamp": 1485975955, "public_url_shared": false, "editable": false, "thumb_160": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png", "user": "U407ABLLW", "groups": [], "is_public": true, "pretty_type": "PNG", "is_external": false, "mimetype": "image/png", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png", "name": "1x1.png", "created": 1485975955, "original_w": 1, "comments_count": 1, "mode": "hosted"}, "type": "message"}, "channel": "C407ABS94", "previous_message": {"comment": {"comment": "first comment! now i edited it.", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "text": "<@U407ABLLW|alice> commented on <@U407ABLLW|alice>\u2019s file : first comment! now i edited it.", "ts": "1485976236.000019", "subtype": "file_comment", "is_intro": false, "file": {"image_exif_rotation": 1, "filetype": "png", "channels": ["C407ABS94"], "display_as_bot": false, "id": "F3ZJQTA66", "size": 68, "original_h": 1, "thumb_360_w": 1, "title": "1x1.png", "url_private": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/1x1.png", "thumb_360": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_360.png", "thumb_64": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_64.png", "ims": [], "thumb_80": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_80.png", "thumb_360_h": 1, "external_type": "", "username": "", "timestamp": 1485975955, "public_url_shared": false, "editable": false, "thumb_160": "https://files.slack.com/files-tmb/T3YS5EAL9-F3ZJQTA66-8ab528dd77/1x1_160.png", "url_private_download": "https://files.slack.com/files-pri/T3YS5EAL9-F3ZJQTA66/download/1x1.png", "user": "U407ABLLW", "groups": [], "is_public": true, "pretty_type": "PNG", "is_external": false, "mimetype": "image/png", "permalink_public": "https://slack-files.com/T3YS5EAL9-F3ZJQTA66-5d747593d2", "permalink": "https://weeslacktest.slack.com/files/alice/F3ZJQTA66/1x1.png", "name": "1x1.png", "created": 1485975955, "original_w": 1, "comments_count": 1, "mode": "hosted"}, "type": "message"}} diff --git a/_pytest/data/websocket/1485976248.65-file_comment_edited.json b/_pytest/data/websocket/1485976248.65-file_comment_edited.json new file mode 100644 index 0000000..3e7c528 --- /dev/null +++ b/_pytest/data/websocket/1485976248.65-file_comment_edited.json @@ -0,0 +1 @@ +{"comment": {"comment": "first comment! now i edited it.", "created": 1485976236, "timestamp": 1485976236, "is_intro": false, "user": "U407ABLLW", "id": "Fc3ZMDRQLV", "channel": ""}, "user_id": "U407ABLLW", "event_ts": "1485976248.893801", "file_id": "F3ZJQTA66", "file": {"id": "F3ZJQTA66"}, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "file_comment_edited"} diff --git a/_pytest/data/websocket/1485976267.81-reconnect_url.json b/_pytest/data/websocket/1485976267.81-reconnect_url.json new file mode 100644 index 0000000..55533ab --- /dev/null +++ b/_pytest/data/websocket/1485976267.81-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-m00z.slack-msgs.com/websocket/fZI1FV2ZuMYXeQLMKi1SrH2j69xL6m_6hC38CVCr0ugPFpMS1IBxlCvHJYbSrH-fMhSuLmbkEd1te_ND5Q-EyVVX-w06mn5NLZM9GaX_mWb9A3w79sThYzEgnnQ8onoeFM2CWgFNxWM_3XS4HJaWXeee-_sNh_booNbby8jm9mg=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} diff --git a/_pytest/data/websocket/1486004888.32-presence_change.json b/_pytest/data/websocket/1486004888.32-presence_change.json new file mode 100644 index 0000000..c52679d --- /dev/null +++ b/_pytest/data/websocket/1486004888.32-presence_change.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U3ZKBBDL5", "presence": "active"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004888.41-reconnect_url.json b/_pytest/data/websocket/1486004888.41-reconnect_url.json new file mode 100644 index 0000000..ae7d1f1 --- /dev/null +++ b/_pytest/data/websocket/1486004888.41-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-3z94.slack-msgs.com/websocket/v0jzriT0ZxrHn6tVO4Dpi96hr54r8ZOaGklQfQN2G_D1kO6mEMs7KXXfUTax40_5zkAA6XTfZpSsjo3TGVD0hcFrhhSwhTsbFvs6W2m5pHH8hfaLJuKFkfPZySnprXsHCXDNKMWyTKMFTZ_CAUKjyhJWzY9Z708pAGB8oy6YMcE=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004935.63-presence_change.json b/_pytest/data/websocket/1486004935.63-presence_change.json new file mode 100644 index 0000000..eca19bc --- /dev/null +++ b/_pytest/data/websocket/1486004935.63-presence_change.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U407ABLLW", "presence": "active"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004950.43-message.json b/_pytest/data/websocket/1486004950.43-message.json new file mode 100644 index 0000000..ce13b3f --- /dev/null +++ b/_pytest/data/websocket/1486004950.43-message.json @@ -0,0 +1 @@ +{"text": "is here", "ts": "1486004950.000002", "subtype": "me_message", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004992.49-user_typing.json b/_pytest/data/websocket/1486004992.49-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486004992.49-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004995.69-user_typing.json b/_pytest/data/websocket/1486004995.69-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486004995.69-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004999.55-user_typing.json b/_pytest/data/websocket/1486004999.55-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486004999.55-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486004999.95-message.json b/_pytest/data/websocket/1486004999.95-message.json new file mode 100644 index 0000000..b906b1b --- /dev/null +++ b/_pytest/data/websocket/1486004999.95-message.json @@ -0,0 +1 @@ +{"text": "this _is_ a *formatted* message", "ts": "1486004999.000003", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005004.03-user_typing.json b/_pytest/data/websocket/1486005004.03-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486005004.03-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005007.16-user_typing.json b/_pytest/data/websocket/1486005007.16-user_typing.json new file mode 100644 index 0000000..859d905 --- /dev/null +++ b/_pytest/data/websocket/1486005007.16-user_typing.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "user_typing", "user": "U407ABLLW", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005008.45-reconnect_url.json b/_pytest/data/websocket/1486005008.45-reconnect_url.json new file mode 100644 index 0000000..bafddd6 --- /dev/null +++ b/_pytest/data/websocket/1486005008.45-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-14cn.slack-msgs.com/websocket/28Rgy5cmEP5N50Jq5Jk0YSCKRdC_PTNklbuT07jR1-BP0vpyvFzhUFpTqbl7pxbMkSCE1rv8OMQMKsWyHgDnoJ9KANYeHweBmRAsL41zPn-dJnJmTpnm670fgK1Nr-tVsfXpo7ql0cbstLR_KWDHr8zt1B2TbqIUeoikNN6oBiY=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005009.0-message.json b/_pytest/data/websocket/1486005009.0-message.json new file mode 100644 index 0000000..10c96e9 --- /dev/null +++ b/_pytest/data/websocket/1486005009.0-message.json @@ -0,0 +1 @@ +{"text": "this is ~strikethrough~", "ts": "1486005008.000004", "user": "U407ABLLW", "team": "T3YS5EAL9", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "message", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005096.38-accounts_changed.json b/_pytest/data/websocket/1486005096.38-accounts_changed.json new file mode 100644 index 0000000..1d0086c --- /dev/null +++ b/_pytest/data/websocket/1486005096.38-accounts_changed.json @@ -0,0 +1 @@ +{"event_ts": "1486005096.312395", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "accounts_changed"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005099.81-presence_change.json b/_pytest/data/websocket/1486005099.81-presence_change.json new file mode 100644 index 0000000..c52679d --- /dev/null +++ b/_pytest/data/websocket/1486005099.81-presence_change.json @@ -0,0 +1 @@ +{"wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "presence_change", "user": "U3ZKBBDL5", "presence": "active"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005104.63-channel_marked.json b/_pytest/data/websocket/1486005104.63-channel_marked.json new file mode 100644 index 0000000..956e886 --- /dev/null +++ b/_pytest/data/websocket/1486005104.63-channel_marked.json @@ -0,0 +1 @@ +{"mention_count": 0, "event_ts": "1486005104.313131", "ts": "1486005008.000004", "unread_count": 0, "num_mentions": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "channel_marked", "channel": "C407ABS94"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005109.58-im_marked.json b/_pytest/data/websocket/1486005109.58-im_marked.json new file mode 100644 index 0000000..b74c742 --- /dev/null +++ b/_pytest/data/websocket/1486005109.58-im_marked.json @@ -0,0 +1 @@ +{"event_ts": "1486005109.313169", "ts": "1485975735.000002", "dm_count": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "im_marked", "channel": "D3ZK1D8JY"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005119.69-im_marked.json b/_pytest/data/websocket/1486005119.69-im_marked.json new file mode 100644 index 0000000..802f4c9 --- /dev/null +++ b/_pytest/data/websocket/1486005119.69-im_marked.json @@ -0,0 +1 @@ +{"event_ts": "1486005119.313254", "ts": "1485975421.000002", "dm_count": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "im_marked", "channel": "D3ZEQULHZ"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005124.51-mpim_marked.json b/_pytest/data/websocket/1486005124.51-mpim_marked.json new file mode 100644 index 0000000..3357683 --- /dev/null +++ b/_pytest/data/websocket/1486005124.51-mpim_marked.json @@ -0,0 +1 @@ +{"mention_count": 0, "event_ts": "1486005124.313290", "is_mpim": true, "ts": "1485975715.000005", "unread_count": 0, "num_mentions": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "mpim_marked", "channel": "G3ZGMF4RZ"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005124.59-group_marked.json b/_pytest/data/websocket/1486005124.59-group_marked.json new file mode 100644 index 0000000..8efe97a --- /dev/null +++ b/_pytest/data/websocket/1486005124.59-group_marked.json @@ -0,0 +1 @@ +{"mention_count": 0, "event_ts": "1486005124.313291", "is_mpim": true, "ts": "1485975715.000005", "unread_count": 0, "num_mentions": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_marked", "channel": "G3ZGMF4RZ"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005124.67-group_marked.json b/_pytest/data/websocket/1486005124.67-group_marked.json new file mode 100644 index 0000000..7753500 --- /dev/null +++ b/_pytest/data/websocket/1486005124.67-group_marked.json @@ -0,0 +1 @@ +{"mention_count": 0, "event_ts": "1486005124.313292", "is_mpim": false, "ts": "1485975492.000004", "unread_count": 0, "num_mentions": 0, "mention_count_display": 0, "unread_count_display": 0, "num_mentions_display": 0, "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "group_marked", "channel": "G3ZJKP7GA"} \ No newline at end of file diff --git a/_pytest/data/websocket/1486005188.48-reconnect_url.json b/_pytest/data/websocket/1486005188.48-reconnect_url.json new file mode 100644 index 0000000..1bdbd43 --- /dev/null +++ b/_pytest/data/websocket/1486005188.48-reconnect_url.json @@ -0,0 +1 @@ +{"url": "wss://mpmulti-lz08.slack-msgs.com/websocket/dHqqUxSrrgTCJI7af1mYz7ECm0-rxu4GOIPmnYwK_K9uWOwBXvF54joGrhpZcUjrJ913Vx01Wh6Ta4u5-bC4-Zn9H9wXklCc6mDDf8iJJmXKC2nWZYaP8jOTzPRNM97iISoe5LNSXl_JB-9J5DSSwEdBh7eQ0SEFeko7NxXP6_o=", "wee_slack_metadata": {"team": "d80c2b6c3127dbb1991917394ed219e8212a2606"}, "type": "reconnect_url"} \ No newline at end of file diff --git a/_pytest/test_eventrouter.py b/_pytest/test_eventrouter.py new file mode 100644 index 0000000..b235512 --- /dev/null +++ b/_pytest/test_eventrouter.py @@ -0,0 +1,86 @@ +import pytest +from wee_slack import EventRouter, ProcessNotImplemented, SlackRequest + +def test_EventRouter(mock_weechat): + # Sending valid json adds to the queue. + e = EventRouter() + e.receive_json('{}') + assert len(e.queue) == 1 + + # Handling an event removes from the queue. + e = EventRouter() + # Create a function to test we are called + e.proc['testfunc'] = lambda x, y: x + e.receive_json('{"type": "testfunc"}') + e.handle_next() + assert len(e.queue) == 0 + + # Handling a local event removes from the queue. + e = EventRouter() + # Create a function to test we are called + e.proc['local_testfunc'] = lambda x, y: x + e.receive_json('{"type": "local_testfunc"}') + e.handle_next() + assert len(e.queue) == 0 + + # Handling an event without an associated processor + # raises an exception. + e = EventRouter() + # Create a function to test we are called + e.receive_json('{"type": "testfunc"}') + with pytest.raises(ProcessNotImplemented): + e.handle_next() + +def test_EventRouterReceivedata(mock_weechat): + + e = EventRouter() + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.startold", {"meh": "blah"})) + print context + e.receive_httprequest_callback(context, 1, -1, ' {"JSON": "MEH", ', 4) + #print len(e.reply_buffer) + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.startold", {"meh": "blah"})) + print context + e.receive_httprequest_callback(context, 1, -1, ' "JSON2": "MEH", ', 4) + #print len(e.reply_buffer) + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.startold", {"meh": "blah"})) + print context + e.receive_httprequest_callback(context, 1, 0, ' "JSON3": "MEH"}', 4) + #print len(e.reply_buffer) + try: + e.handle_next() + e.handle_next() + e.handle_next() + e.handle_next() + except: + pass + + print e.context + #assert False + + context = e.store_context(SlackRequest('xoxoxoxox', "rtm.start", {"meh": "blah"})) + rtmstartdata = open('_pytest/data/http/rtm.start.json', 'r').read() + e.receive_httprequest_callback(context, 1, -1, rtmstartdata[:5000], 4) + e.receive_httprequest_callback(context, 1, 0, rtmstartdata[5000:], 4) + e.handle_next() + + #print len(e.reply_buffer) + + #print e.teams + + for t in e.teams: + #print vars(e.teams[t]) + for c in e.teams[t].channels: + pass + #print c + for u in e.teams[t].users: + pass + #print vars(u) + + +# e = EventRouter() +# # Create a function to test we are called +# e.receive_json('{"type": "message"}') +# e.handle_next() +# assert False + + #assert False diff --git a/_pytest/test_everything.py b/_pytest/test_everything.py new file mode 100644 index 0000000..a121541 --- /dev/null +++ b/_pytest/test_everything.py @@ -0,0 +1,50 @@ +import glob +import json + +#from wee_slack import render +from wee_slack import ProcessNotImplemented + +def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): + + eventrouter = realish_eventrouter + + t = eventrouter.teams.keys()[0] + #u = eventrouter.teams[t].users.keys()[0] + + #user = eventrouter.teams[t].users[u] + #print user + + socket = mock_websocket + eventrouter.teams[t].ws = socket + + datafiles = glob.glob("_pytest/data/websocket/*.json") + + print datafiles + #assert False + + notimplemented = set() + + for fname in datafiles: + try: + print "####################" + data = json.loads(open(fname, 'r').read()) + socket.add(data) + print data + eventrouter.receive_ws_callback(t) + eventrouter.handle_next() + except ProcessNotImplemented as e: + notimplemented.add(str(e)) + #this handles some message data not existing - need to fix + except KeyError: + pass + + if len(notimplemented) > 0: + print "####################" + print sorted(notimplemented) + print "####################" + + print len(eventrouter.queue) + #assert False + + + diff --git a/_pytest/test_linkifytext.py b/_pytest/test_linkifytext.py new file mode 100644 index 0000000..f9da3f9 --- /dev/null +++ b/_pytest/test_linkifytext.py @@ -0,0 +1,6 @@ +from wee_slack import linkify_text + +#def test_linkifytext(): +# linkify_text('@ryan') + +# assert False diff --git a/_pytest/test_presencechange.py b/_pytest/test_presencechange.py new file mode 100644 index 0000000..b4202fa --- /dev/null +++ b/_pytest/test_presencechange.py @@ -0,0 +1,31 @@ + +def test_PresenceChange(monkeypatch, realish_eventrouter, mock_websocket): + + e = realish_eventrouter + + t = e.teams.keys()[0] + u = e.teams[t].users.keys()[0] + + user = e.teams[t].users[u] + + socket = mock_websocket + e.teams[t].ws = socket + + socket.add({ + "type": "presence_change", + "user": user.identifier, + "presence": "active", + }) + socket.add({ + "type": "presence_change", + "user": user.identifier, + "presence": "away", + }) + + e.receive_ws_callback(t) + e.handle_next() + assert e.teams[t].users[u].presence == "active" + + e.receive_ws_callback(t) + e.handle_next() + assert e.teams[t].users[u].presence == "away" diff --git a/_pytest/test_process_message.py b/_pytest/test_process_message.py index 0ca56cd..e2447f7 100644 --- a/_pytest/test_process_message.py +++ b/_pytest/test_process_message.py @@ -1,38 +1,48 @@ - -import wee_slack -import pytest import json -from collections import defaultdict +from wee_slack import render + +def test_process_message(monkeypatch, realish_eventrouter, mock_websocket): -def test_process_message(slack_debug, monkeypatch, myservers, mychannels, myusers): - called = defaultdict(int) - wee_slack.servers = myservers - wee_slack.channels = mychannels - wee_slack.users = myusers - wee_slack.message_cache = {} - wee_slack.servers[0].users = myusers - wee_slack.unfurl_ignore_alt_text = False + e = realish_eventrouter - def mock_buffer_prnt(*args): - called['buffer_prnt'] += 1 - monkeypatch.setattr(wee_slack.Channel, 'buffer_prnt', mock_buffer_prnt) + t = e.teams.keys()[0] + u = e.teams[t].users.keys()[0] -# def mock_buffer_prnt_changed(*args): -# called['buffer_prnt_changed'] += 1 -# print args -# monkeypatch.setattr(wee_slack.Channel, 'buffer_prnt_changed', mock_buffer_prnt_changed) + user = e.teams[t].users[u] + #print user + socket = mock_websocket + e.teams[t].ws = socket messages = [] - messages.append( json.loads(open('_pytest/data/message-normal.json', 'r').read()) ) - messages.append( json.loads(open('_pytest/data/message-normal2.json', 'r').read()) ) - messages.append( json.loads(open('_pytest/data/message-changed.json', 'r').read()) ) - messages.append( json.loads(open('_pytest/data/message-deleted.json', 'r').read()) ) + messages.append(json.loads(open('_pytest/data/websocket/1485975421.33-message.json', 'r').read())) + + # test message and then change + messages.append(json.loads(open('_pytest/data/websocket/1485976157.18-message.json', 'r').read())) + messages.append(json.loads(open('_pytest/data/websocket/1485976151.6-message.json', 'r').read())) + + # test message then deletion + messages.append(json.loads(open('_pytest/data/websocket/1485975698.45-message.json', 'r').read())) + messages.append(json.loads(open('_pytest/data/websocket/1485975723.85-message.json', 'r').read())) + for m in messages: - wee_slack.process_message(m) - print "---" - print called - print "---" -# assert called['buffer_prnt'] == 2 -# assert called['buffer_prnt_changed'] == 1 + m["user"] = user.id + socket.add(m) + + e.receive_ws_callback(t) + e.handle_next() + + e.receive_ws_callback(t) + e.handle_next() + + e.receive_ws_callback(t) + e.handle_next() + + e.receive_ws_callback(t) + e.handle_next() + + + #assert e.teams[t].channels['C407ABS94'].messages.keys()[0] == '1485976151.00016' + #assert False + diff --git a/_pytest/test_processreply.py b/_pytest/test_processreply.py new file mode 100644 index 0000000..a725f23 --- /dev/null +++ b/_pytest/test_processreply.py @@ -0,0 +1,33 @@ +#from wee_slack import process_reply + +def test_process_reply(monkeypatch, realish_eventrouter, mock_websocket): + + e = realish_eventrouter + + t = e.teams.keys()[0] + #u = e.teams[t].users.keys()[0] + + #user = e.teams[t].users[u] + #print user + + socket = mock_websocket + e.teams[t].ws = socket + + c = e.teams[t].channels.keys()[0] + channel = e.teams[t].channels[c] + channel.send_message('asdf') + + socket = mock_websocket + socket.add({"reply_to": 1, "_team": t, "ts": "12341234.111"}) + + print e.teams[t].ws_replies + + e.receive_ws_callback(t) + e.handle_next() + + #reply = {"reply_to": 1, "_team": t, "ts": "12341234.111"} + #print reply + #process_reply(reply, e) + #print e.teams[t].ws_replies + #assert False + pass diff --git a/_pytest/test_processteamjoin.py b/_pytest/test_processteamjoin.py new file mode 100644 index 0000000..00a8b4c --- /dev/null +++ b/_pytest/test_processteamjoin.py @@ -0,0 +1,54 @@ +import glob +import json + +from wee_slack import ProcessNotImplemented + +def test_process_reply(monkeypatch, mock_websocket, realish_eventrouter): + + eventrouter = realish_eventrouter + + t = eventrouter.teams.keys()[0] + #u = eventrouter.teams[t].users.keys()[0] + + #user = eventrouter.teams[t].users[u] + #print user + + #delete charles so we can add him + del eventrouter.teams[t].users['U4096CBHC'] + + assert len(eventrouter.teams[t].users) == 3 + + socket = mock_websocket + eventrouter.teams[t].ws = socket + + datafiles = glob.glob("_pytest/data/websocket/1485975606.59-team_join.json") + + print datafiles + #assert False + + notimplemented = set() + + for fname in datafiles: + try: + print "####################" + data = json.loads(open(fname, 'r').read()) + socket.add(data) + print data + eventrouter.receive_ws_callback(t) + eventrouter.handle_next() + except ProcessNotImplemented as e: + notimplemented.add(str(e)) + #this handles some message data not existing - need to fix + except KeyError: + pass + + if len(notimplemented) > 0: + print "####################" + print sorted(notimplemented) + print "####################" + + #print len(eventrouter.queue) + assert len(eventrouter.teams[t].users) == 4 + + + diff --git a/_pytest/test_sendmessage.py b/_pytest/test_sendmessage.py new file mode 100644 index 0000000..a87942d --- /dev/null +++ b/_pytest/test_sendmessage.py @@ -0,0 +1,21 @@ + +def test_send_message(monkeypatch, realish_eventrouter, mock_websocket): + e = realish_eventrouter + + t = e.teams.keys()[0] + #u = e.teams[t].users.keys()[0] + + #user = e.teams[t].users[u] + #print user + + socket = mock_websocket + e.teams[t].ws = socket + + c = e.teams[t].channels.keys()[0] + + channel = e.teams[t].channels[c] + channel.send_message('asdf') + + print c + + #assert False diff --git a/_pytest/test_slackchannel.py b/_pytest/test_slackchannel.py new file mode 100644 index 0000000..7d93afc --- /dev/null +++ b/_pytest/test_slackchannel.py @@ -0,0 +1,33 @@ +from mock import Mock +#from wee_slack import SlackChannel + +def test_SlackChannel(realish_eventrouter): + e = realish_eventrouter + + print e.sc["team"].channels + #c = SlackChannel(e, **json.loads(chan)) + c = e.sc["team"].channels['C3ZEQAYN7'] + + print c.formatted_name() + c.is_someone_typing = Mock(return_value=True) + c.channel_buffer = Mock(return_value=True) + print c.create_buffer() + print c.rename() + print c.current_short_name + print c.formatted_name() + print c.rename() + print c.formatted_name() + + print "-------" + print c == "random" + print "-------" + print c == "#random" + print "-------" + print c == "weeslacktest.slack.com.#random" + print "-------" + print c == "weeslacktest.slack.com.random" + print "-------" + print c == "dandom" + + print e.weechat_controller.buffers + #assert False diff --git a/_pytest/test_slackdmchannel.py b/_pytest/test_slackdmchannel.py new file mode 100644 index 0000000..203c4ae --- /dev/null +++ b/_pytest/test_slackdmchannel.py @@ -0,0 +1,20 @@ +from mock import Mock +#from wee_slack import SlackChannel + +def test_SlackDMChannel(realish_eventrouter): + e = realish_eventrouter + + print e.sc["team"].channels + #c = SlackChannel(e, **json.loads(chan)) + c = e.sc["team"].channels['D3ZEQULHZ'] + + print c.formatted_name() + c.is_someone_typing = Mock(return_value=True) + c.channel_buffer = Mock(return_value=True) + print c.create_buffer() + print c.rename() + print c.current_short_name + print c.formatted_name() + print c.rename() + print c.formatted_name() +# assert False diff --git a/_pytest/test_slackgroupchannel.py b/_pytest/test_slackgroupchannel.py new file mode 100644 index 0000000..59793f2 --- /dev/null +++ b/_pytest/test_slackgroupchannel.py @@ -0,0 +1,20 @@ +from mock import Mock +#from wee_slack import SlackChannel + +def test_SlackGroupChannel(realish_eventrouter): + e = realish_eventrouter + + print e.sc["team"].channels + #c = SlackChannel(e, **json.loads(chan)) + c = e.sc["team"].channels['G3ZJKP7GA'] + + print c.formatted_name() + c.is_someone_typing = Mock(return_value=True) + c.channel_buffer = Mock(return_value=True) + print c.create_buffer() + print c.rename() + print c.current_short_name + print c.formatted_name() + print c.rename() + print c.formatted_name() +# assert False diff --git a/_pytest/test_slackmpdmchannel.py b/_pytest/test_slackmpdmchannel.py new file mode 100644 index 0000000..a2c6c51 --- /dev/null +++ b/_pytest/test_slackmpdmchannel.py @@ -0,0 +1,20 @@ +from mock import Mock +#from wee_slack import SlackChannel + +def test_SlackMPDMChannel(realish_eventrouter): + e = realish_eventrouter + + print e.sc["team"].channels + #c = SlackChannel(e, **json.loads(chan)) + c = e.sc["team"].channels['G3ZGMF4RZ'] + + print c.formatted_name() + c.is_someone_typing = Mock(return_value=True) + c.channel_buffer = Mock(return_value=True) + print c.create_buffer() + print c.rename() + print c.current_short_name + print c.formatted_name() + print c.rename() + print c.formatted_name() +# assert False diff --git a/_pytest/test_slackrequest.py b/_pytest/test_slackrequest.py new file mode 100644 index 0000000..081330e --- /dev/null +++ b/_pytest/test_slackrequest.py @@ -0,0 +1,11 @@ +from wee_slack import SlackRequest, EventRouter + +def test_SlackRequest(): + s = SlackRequest('xoxoxoxox', "blah.get", {"meh": "blah"}) + print s + + e = EventRouter() + e.receive(s) + e.handle_next() + #assert False + diff --git a/_pytest/test_slackteam.py b/_pytest/test_slackteam.py new file mode 100644 index 0000000..fa6ac6a --- /dev/null +++ b/_pytest/test_slackteam.py @@ -0,0 +1,10 @@ +from wee_slack import SlackTeam, EventRouter + +def test_SlackTeam(): + e = EventRouter() + #s = SlackTeam('xoxo') + #e.register_team(s) + #print e.queue + #print e.handle_next() + #assert False + diff --git a/_pytest/test_slackts.py b/_pytest/test_slackts.py new file mode 100644 index 0000000..87f43a7 --- /dev/null +++ b/_pytest/test_slackts.py @@ -0,0 +1,24 @@ +from wee_slack import SlackTS + + +def test_slackts(): + base = SlackTS("1485976156.000017") + + b = SlackTS("1485976156.000016") + c = SlackTS("1485976156.000018") + + d = SlackTS("1485976155.000017") + e = SlackTS("1485976157.000017") + + assert base > b + assert base < c + + assert base > d + assert base < e + + c = SlackTS() + assert c > base + + assert base == "1485976156.000017" + assert base > "1485976156.000016" + assert base < "1485976156.000018" diff --git a/_pytest/test_unfurl.py b/_pytest/test_unfurl.py index 2168aed..b631888 100644 --- a/_pytest/test_unfurl.py +++ b/_pytest/test_unfurl.py @@ -20,7 +20,7 @@ slack = wee_slack }, { 'input': "foo <#C2147483705> foo", - 'output': "foo #testchan foo", + 'output': "foo #test-chan foo", }, { 'input': "url: suffix", @@ -34,13 +34,20 @@ slack = wee_slack 'input': "<@U2147483697|@othernick> multiple unfurl ", 'output': "@othernick multiple unfurl https://example.com (example with spaces)", }, + { + 'input': "try the #test-chan channel", + 'output': "try the #test-chan channel", + }, )) -def test_unfurl_refs(myservers, mychannels, myusers, case): - slack.servers = myservers - slack.channels = mychannels - slack.users = myusers - slack.message_cache = {} - slack.servers[0].users = myusers - print mychannels[0].identifier +def test_unfurl_refs(case): + pass + #print myslack + #slack.servers = myslack.server + #slack.channels = myslack.channel + #slack.users = myslack.user + #slack.message_cache = {} + #slack.servers[0].users = myslack.user + #print myslack.channel[0].identifier + + #assert slack.unfurl_refs(case['input'], ignore_alt_text=case.get('ignore_alt_text', False)) == case['output'] - assert slack.unfurl_refs(case['input'], ignore_alt_text=case.get('ignore_alt_text', False)) == case['output'] diff --git a/wee_slack.py b/wee_slack.py index ed96f03..e8b736c 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -1,20 +1,21 @@ -# -*- coding: utf-8 -*- +#-*- coding: utf-8 -*- # from functools import wraps import time import json -import os import pickle import sha +import os import re import urllib -import HTMLParser import sys import traceback -import collections +#import collections import ssl +import random +import string from websocket import create_connection, WebSocketConnectionClosedException @@ -24,9 +25,9 @@ try: except: pass -SCRIPT_NAME = "slack_extension" +SCRIPT_NAME = "slack" SCRIPT_AUTHOR = "Ryan Huber " -SCRIPT_VERSION = "0.99.9" +SCRIPT_VERSION = "1.99" SCRIPT_LICENSE = "MIT" SCRIPT_DESC = "Extends weechat for typing notification/search/etc on slack.com" @@ -35,6 +36,8 @@ SCROLLBACK_SIZE = 500 CACHE_VERSION = "4" +RECORD_DIR = "/tmp/weeslack-debug" + SLACK_API_TRANSLATOR = { "channel": { "history": "channels.history", @@ -54,10 +57,42 @@ SLACK_API_TRANSLATOR = { "join": "channels.join", "leave": "groups.leave", "mark": "groups.mark", + }, + "thread": { + "history": None, + "join": None, + "leave": None, + "mark": None, } + } +###### Decorators have to be up here + +def slack_buffer_or_ignore(f): + """ + Only run this function if we're in a slack buffer, else ignore + """ + @wraps(f) + def wrapper(data, current_buffer, *args, **kwargs): + if current_buffer not in EVENTROUTER.weechat_controller.buffers: + return w.WEECHAT_RC_OK + return f(data, current_buffer, *args, **kwargs) + return wrapper + +def slack_buffer_required(f): + """ + Only run this function if we're in a slack buffer, else print error + """ + @wraps(f) + def wrapper(data, current_buffer, *args, **kwargs): + if current_buffer not in EVENTROUTER.weechat_controller.buffers: + return w.WEECHAT_RC_ERROR + return f(data, current_buffer, *args, **kwargs) + return wrapper + + NICK_GROUP_HERE = "0|Here" NICK_GROUP_AWAY = "1|Away" @@ -67,334 +102,436 @@ if hasattr(ssl, "get_default_verify_paths") and callable(ssl.get_default_verify_ if ssl_defaults.cafile is not None: sslopt_ca_certs = {'ca_certs': ssl_defaults.cafile} +##### BEGIN NEW -def dbg(message, fout=False, main_buffer=False): - """ - send debug output to the slack-debug buffer and optionally write to a file. - """ - message = "DEBUG: {}".format(message) - # message = message.encode('utf-8', 'replace') - if fout: - file('/tmp/debug.log', 'a+').writelines(message + '\n') - if main_buffer: - w.prnt("", "slack: " + message) - else: - if slack_debug is not None: - w.prnt(slack_debug, message) - - -class SearchList(list): - """ - A normal python list with some syntactic sugar for searchability - """ - def __init__(self): - self.hashtable = {} - super(SearchList, self).__init__(self) - - def find(self, name): - if name in self.hashtable: - return self.hashtable[name] - # this is a fallback to __eq__ if the item isn't in the hashtable already - if name in self: - self.update_hashtable() - return self[self.index(name)] - - def append(self, item, aliases=[]): - super(SearchList, self).append(item) - self.update_hashtable(item) - - def update_hashtable(self, item=None): - if item is not None: - try: - for alias in item.get_aliases(): - if alias is not None: - self.hashtable[alias] = item - except AttributeError: - pass - else: - for child in self: - try: - for alias in child.get_aliases(): - if alias is not None: - self.hashtable[alias] = child - except AttributeError: - pass - - def find_by_class(self, class_name): - items = [] - for child in self: - if child.__class__ == class_name: - items.append(child) - return items - - def find_by_class_deep(self, class_name, attribute): - items = [] - for child in self: - if child.__class__ == self.__class__: - items += child.find_by_class_deep(class_name, attribute) - else: - items += (eval('child.' + attribute).find_by_class(class_name)) - return items +IGNORED_EVENTS = [ + "hello", + #"pref_change", + #"reconnect_url", +] +###### New central Event router -class SlackServer(object): - """ - Root object used to represent connection and state of the connection to a slack group. - """ - def __init__(self, token): - self.nick = None - self.name = None - self.team = None - self.domain = None - self.server_buffer_name = None - self.login_data = None - self.buffer = None - self.token = token - self.ws = None - self.ws_hook = None - self.users = SearchList() - self.bots = SearchList() - self.channels = SearchList() - self.connecting = False - self.connected = False - self.connection_attempt_time = 0 - self.communication_counter = 0 - self.message_buffer = {} - self.ping_hook = None - self.alias = None - self.got_history = False +class EventRouter(object): - self.identifier = None - self.connect_to_slack() + def __init__(self): + """ + complete + Eventrouter is the central hub we use to route: + 1) incoming websocket data + 2) outgoing http requests and incoming replies + 3) local requests + It has a recorder that, when enabled, logs most events + to the location specified in RECORD_DIR. + """ + self.queue = [] + self.slow_queue = [] + self.slow_queue_timer = 0 + self.teams = {} + self.context = {} + self.weechat_controller = WeechatController(self) + self.previous_buffer = "" + self.reply_buffer = {} + self.cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} + self.proc = {k[8:]: v for k, v in globals().items() if k.startswith("process_")} + self.handlers = {k[7:]: v for k, v in globals().items() if k.startswith("handle_")} + self.local_proc = {k[14:]: v for k, v in globals().items() if k.startswith("local_process_")} + self.shutting_down = False + self.recording = False + self.recording_path = "/tmp" + + def record(self): + """ + complete + Toggles the event recorder and creates a directory for data if enabled. + """ + self.recording = not self.recording + if self.recording: + if not os.path.exists(RECORD_DIR): + os.makedirs(RECORD_DIR) - def __eq__(self, compare_str): - if compare_str == self.identifier or compare_str == self.token or compare_str == self.buffer: - return True + def record_event(self, message_json, file_name_field, subdir=None): + """ + complete + Called each time you want to record an event. + message_json is a json in dict form + file_name_field is the json key whose value you want to be part of the file name + """ + now = time.time() + if subdir: + directory = "{}/{}".format(RECORD_DIR, subdir) else: - return False - - def __str__(self): - return "{}".format(self.identifier) - - def __repr__(self): - return "{}".format(self.identifier) - - def add_user(self, user): - self.users.append(user, user.get_aliases()) - users.append(user, user.get_aliases()) + directory = RECORD_DIR + if not os.path.exists(directory): + os.makedirs(directory) + mtype = message_json.get(file_name_field, 'unknown') + f = open('{}/{}-{}.json'.format(directory, now, mtype), 'w') + f.write("{}".format(json.dumps(message_json))) + f.close() + + def store_context(self, data): + """ + A place to store data and vars needed by callback returns. We need this because + weechat's "callback_data" has a limited size and weechat will crash if you exceed + this size. + """ + identifier = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(40)) + self.context[identifier] = data + dbg("stored context {} {} ".format(identifier, data.url)) + return identifier - def add_bot(self, bot): - self.bots.append(bot) + def retrieve_context(self, identifier): + """ + A place to retrieve data and vars needed by callback returns. We need this because + weechat's "callback_data" has a limited size and weechat will crash if you exceed + this size. + """ + data = self.context.get(identifier, None) + if data: + #dbg("retrieved eontext {} ".format(identifier)) + return data - def add_channel(self, channel): - self.channels.append(channel, channel.get_aliases()) - channels.append(channel, channel.get_aliases()) + def delete_context(self, identifier): + """ + Requests can span multiple requests, so we may need to delete this as a last step + """ + if identifier in self.context: + #dbg("deleted eontext {} ".format(identifier)) + del self.context[identifier] - def get_aliases(self): - aliases = filter(None, [self.identifier, self.token, self.buffer, self.alias]) - return aliases + def shutdown(self): + """ + complete + This toggles shutdown mode. Shutdown mode tells us not to + talk to Slack anymore. Without this, typing /quit will trigger + a race with the buffer close callback and may result in you + leaving every slack channel. + """ + self.shutting_down = not self.shutting_down - def find(self, name, attribute): - attribute = eval("self." + attribute) - return attribute.find(name) + def register_team(self, team): + """ + complete + Adds a team to the list of known teams for this EventRouter. + """ + if isinstance(team, SlackTeam): + self.teams[team.get_team_hash()] = team + else: + raise InvalidType(type(team)) - def get_communication_id(self): - if self.communication_counter > 999: - self.communication_counter = 0 - self.communication_counter += 1 - return self.communication_counter + def reconnect_if_disconnected(self): + for team_id, team in self.teams.iteritems(): + if not team.connected: + team.connect() + dbg("reconnecting {}".format(team)) - def send_to_websocket(self, data, expect_reply=True): - data["id"] = self.get_communication_id() - message = json.dumps(data) + def receive_ws_callback(self, team_hash): + """ + incomplete (reconnect) + This is called by the global method of the same name. + It is triggered when we have incoming data on a websocket, + which needs to be read. Once it is read, we will ensure + the data is valid JSON, add metadata, and place it back + on the queue for processing as JSON. + """ try: - if expect_reply: - self.message_buffer[data["id"]] = data - self.ws.send(message) - dbg("Sent {}...".format(message[:100])) - except: - dbg("Unexpected error: {}\nSent: {}".format(sys.exc_info()[0], data)) - self.connected = False - - def ping(self): - request = {"type": "ping"} - self.send_to_websocket(request) + # Read the data from the websocket associated with this team. + data = self.teams[team_hash].ws.recv() + message_json = json.loads(data) + metadata = WeeSlackMetadata({ + "team": team_hash, + }).jsonify() + message_json["wee_slack_metadata"] = metadata + if self.recording: + self.record_event(message_json, 'type', 'websocket') + self.receive_json(json.dumps(message_json)) + except WebSocketConnectionClosedException: + #TODO: handle reconnect here + self.teams[team_hash].set_disconnected() + return w.WEECHAT_RC_OK + except Exception: + dbg("socket issue: {}\n".format(traceback.format_exc())) + return w.WEECHAT_RC_OK - def should_connect(self): + def receive_httprequest_callback(self, data, command, return_code, out, err): """ - If we haven't tried to connect OR we tried and never heard back and it - has been 125 seconds consider the attempt dead and try again + complete + Receives the result of an http request we previously handed + off to weechat (weechat bundles libcurl). Weechat can fragment + replies, so it buffers them until the reply is complete. + It is then populated with metadata here so we can identify + where the request originated and route properly. """ - if self.connection_attempt_time == 0 or self.connection_attempt_time + 125 < int(time.time()): - return True + request_metadata = self.retrieve_context(data) + try: + dbg("RECEIVED CALLBACK with request of {} id of {} and code {} of length {}".format(request_metadata.request, request_metadata.response_id, return_code, len(out))) + except: + dbg(request_metadata) + return + if return_code == 0: + if len(out) > 0: + if request_metadata.response_id in self.reply_buffer: + #dbg("found response id in reply_buffer", True) + self.reply_buffer[request_metadata.response_id] += out + else: + #dbg("didn't find response id in reply_buffer", True) + self.reply_buffer[request_metadata.response_id] = "" + self.reply_buffer[request_metadata.response_id] += out + try: + j = json.loads(self.reply_buffer[request_metadata.response_id]) + except: + pass + #dbg("Incomplete json, awaiting more", True) + try: + j["wee_slack_process_method"] = request_metadata.request_normalized + j["wee_slack_request_metadata"] = pickle.dumps(request_metadata) + self.reply_buffer.pop(request_metadata.response_id) + if self.recording: + self.record_event(j, 'wee_slack_process_method', 'http') + self.receive_json(json.dumps(j)) + self.delete_context(data) + except: + dbg("HTTP REQUEST CALLBACK FAILED", True) + pass + # We got an empty reply and this is weird so just ditch it and retry + else: + dbg("length was zero, probably a bug..") + self.delete_context(data) + self.receive(request_metadata) + elif return_code != -1: + self.reply_buffer.pop(request_metadata.response_id, None) + self.delete_context(data) else: - return False + if request_metadata.response_id not in self.reply_buffer: + self.reply_buffer[request_metadata.response_id] = "" + self.reply_buffer[request_metadata.response_id] += out - def connect_to_slack(self): - t = time.time() - # Double check that we haven't exceeded a long wait to connect and try again. - if self.connecting and self.should_connect(): - self.connecting = False - if not self.connecting: - async_slack_api_request("slack.com", self.token, "rtm.start", {"ts": t}) - self.connection_attempt_time = int(time.time()) - self.connecting = True + def receive_json(self, data): + """ + complete + Receives a raw JSON string from and unmarshals it + as dict, then places it back on the queue for processing. + """ + dbg("RECEIVED JSON of len {}".format(len(data))) + message_json = json.loads(data) + self.queue.append(message_json) + def receive(self, dataobj): + """ + complete + Receives a raw object and places it on the queue for + processing. Object must be known to handle_next or + be JSON. + """ + dbg("RECEIVED FROM QUEUE") + self.queue.append(dataobj) + def receive_slow(self, dataobj): + """ + complete + Receives a raw object and places it on the slow queue for + processing. Object must be known to handle_next or + be JSON. + """ + dbg("RECEIVED FROM QUEUE") + self.slow_queue.append(dataobj) + def handle_next(self): + """ + complete + Main handler of the EventRouter. This is called repeatedly + via callback to drain events from the queue. It also attaches + useful metadata and context to events as they are processed. + """ + if len(self.slow_queue) > 0 and ((self.slow_queue_timer + 1) < time.time()): + #for q in self.slow_queue[0]: + dbg("from slow queue", 0) + self.queue.append(self.slow_queue.pop()) + #self.slow_queue = [] + self.slow_queue_timer = time.time() + if len(self.queue) > 0: + j = self.queue.pop(0) + # Reply is a special case of a json reply from websocket. + kwargs = {} + if isinstance(j, SlackRequest): + if j.should_try(): + if j.retry_ready(): + local_process_async_slack_api_request(j, self) + else: + self.slow_queue.append(j) + else: + dbg("Max retries for Slackrequest") - def connected_to_slack(self, login_data): - if login_data["ok"]: - self.team = login_data["team"]["domain"] - self.domain = login_data["team"]["domain"] + ".slack.com" - dbg("connected to {}".format(self.domain)) - self.identifier = self.domain - - alias = w.config_get_plugin("server_alias.{}".format(login_data["team"]["domain"])) - if alias: - self.server_buffer_name = alias - self.alias = alias else: - self.server_buffer_name = self.domain - - self.nick = login_data["self"]["name"] - self.create_local_buffer() - if self.create_slack_websocket(login_data): - if self.ping_hook: - w.unhook(self.ping_hook) - self.communication_counter = 0 - self.ping_hook = w.hook_timer(1000 * 5, 0, 0, "slack_ping_cb", self.domain) - if len(self.users) == 0 or len(self.channels) == 0: - self.create_slack_mappings(login_data) - - self.connected = True - self.connecting = False + if "reply_to" in j: + dbg("SET FROM REPLY") + function_name = "reply" + elif "type" in j: + dbg("SET FROM type") + function_name = j["type"] + elif "wee_slack_process_method" in j: + dbg("SET FROM META") + function_name = j["wee_slack_process_method"] + else: + dbg("SET FROM NADA") + function_name = "unknown" + + # Here we are passing the actual objects. No more lookups. + meta = j.get("wee_slack_metadata", None) + if meta: + try: + if isinstance(meta, str): + dbg("string of metadata") + team = meta.get("team", None) + if team: + kwargs["team"] = self.teams[team] + if "user" in j: + kwargs["user"] = self.teams[team].users[j["user"]] + if "channel" in j: + kwargs["channel"] = self.teams[team].channels[j["channel"]] + except: + dbg("metadata failure") + + if function_name not in IGNORED_EVENTS: + dbg("running {}".format(function_name)) + if function_name.startswith("local_") and function_name in self.local_proc: + self.local_proc[function_name](j, self, **kwargs) + elif function_name in self.proc: + self.proc[function_name](j, self, **kwargs) + elif function_name in self.handlers: + self.handlers[function_name](j, self, **kwargs) + else: + raise ProcessNotImplemented(function_name) - self.print_connection_info(login_data) - if len(self.message_buffer) > 0: - for message_id in self.message_buffer.keys(): - if self.message_buffer[message_id]["type"] != 'ping': - resend = self.message_buffer.pop(message_id) - dbg("Resent failed message.") - self.send_to_websocket(resend) - # sleep to prevent being disconnected by websocket server - time.sleep(1) - else: - self.message_buffer.pop(message_id) - for chan in self.channels: - # Set channel history back to false because we will miss messages that came - # while we were disconnected otherwise. - chan.got_history = False - if chan.channel_buffer and chan.muted: - w.buffer_set(chan.channel_buffer, "hotlist", "-1") - return True +def handle_next(*args): + """ + complete + This is just a place to call the event router globally. + This is a dirty hack. There must be a better way. + """ + try: + EVENTROUTER.handle_next() + except: + if config.debug_mode: + traceback.print_exc() else: - token_start = self.token[:10] - error = """ -!! slack.com login error: {} - The problematic token starts with {} - Please check your API token with - "/set plugins.var.python.slack_extension.slack_api_token (token)" - -""".format(login_data["error"], token_start) - w.prnt("", error) - self.connected = False - - def print_connection_info(self, login_data): - self.buffer_prnt('Connected to Slack', backlog=True) - self.buffer_prnt('{:<20} {}'.format(u"Websocket URL", login_data["url"]), backlog=True) - self.buffer_prnt('{:<20} {}'.format(u"User name", login_data["self"]["name"]), backlog=True) - self.buffer_prnt('{:<20} {}'.format(u"User ID", login_data["self"]["id"]), backlog=True) - self.buffer_prnt('{:<20} {}'.format(u"Team name", login_data["team"]["name"]), backlog=True) - self.buffer_prnt('{:<20} {}'.format(u"Team domain", login_data["team"]["domain"]), backlog=True) - self.buffer_prnt('{:<20} {}'.format(u"Team id", login_data["team"]["id"]), backlog=True) - - def create_local_buffer(self): - if not w.buffer_search("", self.server_buffer_name): - self.buffer = w.buffer_new(self.server_buffer_name, "buffer_input_cb", "", "", "") - if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': - w.buffer_merge(self.buffer, w.buffer_search_main()) - w.buffer_set(self.buffer, "nicklist", "1") - - def create_slack_websocket(self, data): - web_socket_url = data['url'] - try: - self.ws = create_connection(web_socket_url, sslopt=sslopt_ca_certs) - self.ws_hook = w.hook_fd(self.ws.sock._sock.fileno(), 1, 0, 0, "slack_websocket_cb", self.identifier) - self.ws.sock.setblocking(0) - return True - except Exception as e: - print("websocket connection error: {}".format(e)) - return False - - def create_slack_mappings(self, data): - - for item in data["users"]: - self.add_user(User(self, item["name"], item["id"], item["presence"], item["deleted"], is_bot=item.get('is_bot', False))) - - for item in data["bots"]: - self.add_bot(Bot(self, item["name"], item["id"], item["deleted"])) - - for item in data["channels"]: - item["is_open"] = item["is_member"] - item["prepend_name"] = "#" - if not item["is_archived"]: - self.add_channel(Channel(self, **item)) + pass + return w.WEECHAT_RC_OK - for item in data["groups"]: - item["prepend_name"] = "#" - if not item["is_archived"]: - if item["name"].startswith("mpdm-"): - self.add_channel(MpdmChannel(self, **item)) - else: - self.add_channel(GroupChannel(self, **item)) - - for item in data["ims"]: - if item["unread_count"] > 0 or item["is_open"]: - item["is_open"] = True - item['name'] = self.users.find(item["user"]).name - self.add_channel(DmChannel(self, **item)) - - for item in data['self']['prefs']['muted_channels'].split(','): - if item == '': - continue - maybe_muted_chan = self.channels.find(item) - if maybe_muted_chan is not None: - maybe_muted_chan.muted = True - - #for item in self.channels: - # item.get_history() - - def buffer_prnt(self, message='no message', user="SYSTEM", backlog=False): - message = message.encode('ascii', 'ignore') - if backlog: - tags = "no_highlight,notify_none,logger_backlog_end" +class WeechatController(object): + """ + Encapsulates our interaction with weechat + """ + def __init__(self, eventrouter): + self.eventrouter = eventrouter + self.buffers = {} + self.previous_buffer = None + self.buffer_list_stale = False + def iter_buffers(self): + for b in self.buffers: + yield (b, self.buffers[b]) + def register_buffer(self, buffer_ptr, channel): + """ + complete + Adds a weechat buffer to the list of handled buffers for this EventRouter + """ + if isinstance(buffer_ptr, str): + self.buffers[buffer_ptr] = channel else: - tags = "" - if user == "SYSTEM": - user = w.config_string(w.config_get('weechat.look.prefix_network')) - if self.buffer: - w.prnt_date_tags(self.buffer, 0, tags, "{}\t{}".format(user, message)) + raise InvalidType(type(buffer_ptr)) + def unregister_buffer(self, buffer_ptr, update_remote=False, close_buffer=False): + """ + complete + Adds a weechat buffer to the list of handled buffers for this EventRouter + """ + if isinstance(buffer_ptr, str): + try: + self.buffers[buffer_ptr].destroy_buffer(update_remote) + if close_buffer: + w.buffer_close(buffer_ptr) + del self.buffers[buffer_ptr] + except: + dbg("Tried to close unknown buffer") else: - pass - # w.prnt("", "%s\t%s" % (user, message)) + raise InvalidType(type(buffer_ptr)) + def get_channel_from_buffer_ptr(self, buffer_ptr): + return self.buffers.get(buffer_ptr, None) + def get_all(self, buffer_ptr): + return self.buffers + def get_previous_buffer_ptr(self): + return self.previous_buffer + def set_previous_buffer(self, data): + self.previous_buffer = data + def check_refresh_buffer_list(self): + return self.buffer_list_stale and self.last_buffer_list_update + 1 < time.time() + def set_refresh_buffer_list(self, setting): + self.buffer_list_stale = setting + + +###### New Local Processors + +def local_process_async_slack_api_request(request, event_router): + """ + complete + Sends an API request to Slack. You'll need to give this a well formed SlackRequest object. + DEBUGGING!!! The context here cannot be very large. Weechat will crash. + """ + if not event_router.shutting_down: + weechat_request = 'url:{}'.format(request.request_string()) + weechat_request += '&nonce={}'.format(''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(4))) + params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} + request.tried() + 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, config.slack_timeout, "receive_httprequest_callback", context) + +###### New Callbacks - def set_away(self, msg): - async_slack_api_request(self.domain, self.token, 'presence.set', {"presence": "away"}) - for c in self.channels: - if c.channel_buffer is not None: - w.buffer_set(c.channel_buffer, "localvar_set_away", msg) +def receive_httprequest_callback(data, command, return_code, out, err): + """ + complete + This is a dirty hack. There must be a better way. + """ + #def url_processor_cb(data, command, return_code, out, err): + EVENTROUTER.receive_httprequest_callback(data, command, return_code, out, err) + return w.WEECHAT_RC_OK + +def receive_ws_callback(*args): + """ + complete + The first arg is all we want here. It contains the team + hash which is set when we _hook the descriptor. + This is a dirty hack. There must be a better way. + """ + EVENTROUTER.receive_ws_callback(args[0]) + return w.WEECHAT_RC_OK - def set_active(self): - async_slack_api_request(self.domain, self.token, 'presence.set', {"presence": "active"}) - for c in self.channels: - if c.channel_buffer is not None: - w.buffer_set(c.channel_buffer, "localvar_set_away", '') - w.buffer_set(c.channel_buffer, "localvar_del_away", '') +def reconnect_callback(*args): + EVENTROUTER.reconnect_if_disconnected() + return w.WEECHAT_RC_OK +def buffer_closing_callback(signal, sig_type, data): + """ + complete + Receives a callback from weechat when a buffer is being closed. + We pass the eventrouter variable name in as a string, as + that is the only way we can do dependency injection via weechat + callback, hence the eval. + """ + eval(signal).weechat_controller.unregister_buffer(data, True, False) + return w.WEECHAT_RC_OK -def buffer_input_cb(b, buffer, data): - channel = channels.find(buffer) +def buffer_input_callback(signal, buffer_ptr, data): + """ + incomplete + Handles everything a user types in the input bar. In our case + this includes add/remove reactions, modifying messages, and + sending messages. + """ + eventrouter = eval(signal) + channel = eventrouter.weechat_controller.get_channel_from_buffer_ptr(buffer_ptr) if not channel: return w.WEECHAT_RC_OK_EAT + reaction = re.match("^\s*(\d*)(\+|-):(.*):\s*$", data) if reaction: if reaction.group(2) == "+": @@ -411,1456 +548,1657 @@ def buffer_input_cb(b, buffer, data): # rid of escapes. new = new.replace(r'\/', '/') old = old.replace(r'\/', '/') - channel.change_previous_message(old.decode("utf-8"), new.decode("utf-8"), flags) + channel.edit_previous_message(old.decode("utf-8"), new.decode("utf-8"), flags) else: channel.send_message(data) - # channel.buffer_prnt(channel.server.nick, data) - channel.mark_read(True) + #this is probably wrong channel.mark_read(update_remote=True, force=True) return w.WEECHAT_RC_ERROR +def buffer_switch_callback(signal, sig_type, data): + """ + incomplete + Every time we change channels in weechat, we call this to: + 1) set read marker 2) determine if we have already populated + channel history data + """ + eventrouter = eval(signal) + + prev_buffer_ptr = eventrouter.weechat_controller.get_previous_buffer_ptr() + # this is to see if we need to gray out things in the buffer list + prev = eventrouter.weechat_controller.get_channel_from_buffer_ptr(prev_buffer_ptr) + if prev: + prev.mark_read() + + new_channel = eventrouter.weechat_controller.get_channel_from_buffer_ptr(data) + if new_channel: + if not new_channel.got_history: + new_channel.get_history() + + eventrouter.weechat_controller.set_previous_buffer(data) + return w.WEECHAT_RC_OK -class Channel(object): +def buffer_list_update_callback(data, somecount): """ - Represents a single channel and is the source of truth - for channel <> weechat buffer + incomplete + A simple timer-based callback that will update the buffer list + if needed. We only do this max 1x per second, as otherwise it + uses a lot of cpu for minimal changes. We use buffer short names + to indicate typing via "#channel" <-> ">channel" and + user presence via " name" <-> "+name". """ - #def __init__(self, server, name, identifier, active, last_read=0, prepend_name="", members=[], topic="", unread_count=0): - def __init__(self, server, **kwargs): + eventrouter = eval(data) + #global buffer_list_update + + for b in eventrouter.weechat_controller.iter_buffers(): + b[1].refresh() +# buffer_list_update = True +# if eventrouter.weechat_controller.check_refresh_buffer_list(): +# # gray_check = False +# # if len(servers) > 1: +# # gray_check = True +# eventrouter.weechat_controller.set_refresh_buffer_list(False) + return w.WEECHAT_RC_OK - self.name = kwargs.get('prepend_name', "") + kwargs.get('name') - self.current_short_name = kwargs.get('prepend_name', "") + kwargs.get('name') - self.identifier = kwargs.get('id', 0) - self.active = kwargs.get('is_open', False) - self.last_read = float(kwargs.get('last_read', 0)) - self.members = set(kwargs.get('members', [])) - self.topic = kwargs.get('topic', {"value": ""})["value"] - self.unread_count = kwargs.get('unread_count_display', 0) +def quit_notification_callback(signal, sig_type, data): + stop_talking_to_slack() - self.members_table = {} - self.channel_buffer = None - self.type = "channel" - self.server = server - self.typing = {} - self.last_received = None - self.messages = [] - self.scrolling = False - self.last_active_user = None - self.muted = False - self.got_history = False - #w.prnt("", "unread: {}".format(self.unread_count)) - if self.active: - self.create_buffer() - self.attach_buffer() - self.create_members_table() - self.update_nicklist() - self.set_topic(self.topic) - buffer_list_update_next() +def typing_notification_cb(signal, sig_type, data): + msg = w.buffer_get_string(data, "input") + if len(msg) > 8 and msg[:1] != "/": + global typing_timer + now = time.time() + if typing_timer + 4 < now: + current_buffer = w.current_buffer() + channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + if channel: + identifier = channel.identifier + request = {"type": "typing", "channel": identifier} + channel.team.send_to_websocket(request, expect_reply=False) + typing_timer = now + return w.WEECHAT_RC_OK - def __str__(self): - return self.name +def typing_update_cb(data, remaining_calls): + w.bar_item_update("slack_typing_notice") + return w.WEECHAT_RC_OK - def __repr__(self): - return self.name +def slack_never_away_cb(data, remaining_calls): + if config.never_away: + for t in EVENTROUTER.teams.values(): + slackbot = t.get_channel_map()['slackbot'] + channel = t.channels[slackbot] + request = {"type": "typing", "channel": channel.identifier} + channel.team.send_to_websocket(request, expect_reply=False) + return w.WEECHAT_RC_OK - def __eq__(self, compare_str): - if compare_str == self.fullname() or compare_str == self.name or compare_str == self.identifier or compare_str == self.name[1:] or (compare_str == self.channel_buffer and self.channel_buffer is not None): - return True - else: - return False +def typing_bar_item_cb(data, current_buffer, args): + """ + Privides a bar item indicating who is typing in the current channel AND + why is typing a DM to you globally. + """ + typers = [] + current_buffer = w.current_buffer() + current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) - def get_aliases(self): - aliases = [self.fullname(), self.name, self.identifier, self.name[1:], ] - if self.channel_buffer is not None: - aliases.append(self.channel_buffer) - return aliases + # first look for people typing in this channel + if current_channel: + # this try is mostly becuase server buffers don't implement is_someone_typing + try: + if current_channel.type != 'im' and current_channel.is_someone_typing(): + typers += current_channel.get_typing_list() + except: + pass - def create_members_table(self): - for user in self.members: - self.members_table[user] = self.server.users.find(user) + # here is where we notify you that someone is typing in DM + # regardless of which buffer you are in currently + for t in EVENTROUTER.teams.values(): + for channel in t.channels.values(): + if channel.type == "im": + if channel.is_someone_typing(): + typers.append("D/" + channel.slack_name) + pass - def create_buffer(self): - channel_buffer = w.buffer_search("", "{}.{}".format(self.server.server_buffer_name, self.name)) - if channel_buffer: - self.channel_buffer = channel_buffer - else: - self.channel_buffer = w.buffer_new("{}.{}".format(self.server.server_buffer_name, self.name), "buffer_input_cb", self.name, "", "") - if self.type == "im": - w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') - else: - w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') - if self.server.alias: - w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.alias) - else: - w.buffer_set(self.channel_buffer, "localvar_set_server", self.server.team) - w.buffer_set(self.channel_buffer, "localvar_set_channel", self.name) - w.buffer_set(self.channel_buffer, "short_name", self.name) - buffer_list_update_next() - if self.unread_count != 0 and not self.muted: - w.buffer_set(self.channel_buffer, "hotlist", "1") - - def attach_buffer(self): - channel_buffer = w.buffer_search("", "{}.{}".format(self.server.server_buffer_name, self.name)) - if channel_buffer != main_weechat_buffer: - self.channel_buffer = channel_buffer - w.buffer_set(self.channel_buffer, "localvar_set_nick", self.server.nick) - w.buffer_set(self.channel_buffer, "highlight_words", self.server.nick) - else: - self.channel_buffer = None - channels.update_hashtable() - self.server.channels.update_hashtable() + typing = ", ".join(typers) + if typing != "": + typing = w.color('yellow') + "typing: " + typing - def detach_buffer(self): - if self.channel_buffer is not None: - w.buffer_close(self.channel_buffer) - self.channel_buffer = None - channels.update_hashtable() - self.server.channels.update_hashtable() + return typing - def update_nicklist(self, user=None): - if not self.channel_buffer: - return +def nick_completion_cb(data, completion_item, current_buffer, completion): + """ + Adds all @-prefixed nicks to completion list + """ - w.buffer_set(self.channel_buffer, "nicklist", "1") + current_buffer = w.current_buffer() + current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) - # create nicklists for the current channel if they don't exist - # if they do, use the existing pointer - here = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_HERE) - if not here: - here = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_HERE, "weechat.color.nicklist_group", 1) - afk = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_AWAY) - if not afk: - afk = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 1) - - if user: - user = self.members_table[user] - 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.. - w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) + if current_channel is None or current_channel.members is None: + return w.WEECHAT_RC_OK + for m in current_channel.members: + u = current_channel.team.users.get(m, None) + if u: + w.hook_completion_list_add(completion, "@" + u.slack_name, 1, w.WEECHAT_LIST_POS_SORT) + return w.WEECHAT_RC_OK - # if we didn't get a user, build a complete list. this is expensive. - else: - try: - for user in self.members: - user = self.members_table[user] - if user.deleted: - continue - w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) - except Exception as e: - dbg("DEBUG: {} {} {}".format(self.identifier, self.name, e)) - - def fullname(self): - return "{}.{}".format(self.server.server_buffer_name, self.name) - - def has_user(self, name): - return name in self.members - - def user_join(self, name): - self.members.add(name) - self.create_members_table() - self.update_nicklist() +def emoji_completion_cb(data, completion_item, current_buffer, completion): + """ + Adds all :-prefixed emoji to completion list + """ - def user_leave(self, name): - if name in self.members: - self.members.remove(name) - self.create_members_table() - self.update_nicklist() + current_buffer = w.current_buffer() + current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) - def set_active(self): - self.active = True + if current_channel is None: + return w.WEECHAT_RC_OK + for e in EMOJI['emoji']: + w.hook_completion_list_add(completion, ":" + e + ":", 0, w.WEECHAT_LIST_POS_SORT) + return w.WEECHAT_RC_OK - def set_inactive(self): - self.active = False +def complete_next_cb(data, current_buffer, command): + """Extract current word, if it is equal to a nick, prefix it with @ and + rely on nick_completion_cb adding the @-prefixed versions to the + completion lists, then let Weechat's internal completion do its + thing - def set_typing(self, user): - if self.channel_buffer: - if w.buffer_get_integer(self.channel_buffer, "hidden") == 0: - self.typing[user] = time.time() - buffer_list_update_next() + """ - def unset_typing(self, user): - if self.channel_buffer: - if w.buffer_get_integer(self.channel_buffer, "hidden") == 0: - try: - del self.typing[user] - buffer_list_update_next() - except: - pass + current_buffer = w.current_buffer() + current_channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) - def send_message(self, message): - message = self.linkify_text(message) - dbg(message) - request = {"type": "message", "channel": self.identifier, "text": message, "_server": self.server.domain} - self.server.send_to_websocket(request) - - def linkify_text(self, message): - message = message.split(' ') - for item in enumerate(message): - targets = re.match('.*([@#])([\w.]+\w)(\W*)', item[1]) - if targets and targets.groups()[0] == '@': - named = targets.groups() - if named[1] in ["group", "channel", "here"]: - message[item[0]] = "".format(named[1]) - if self.server.users.find(named[1]): - message[item[0]] = "<@{}>{}".format(self.server.users.find(named[1]).identifier, named[2]) - if targets and targets.groups()[0] == '#': - named = targets.groups() - if self.server.channels.find(named[1]): - message[item[0]] = "<#{}|{}>{}".format(self.server.channels.find(named[1]).identifier, named[1], named[2]) - dbg(message) - return " ".join(message) + #channel = channels.find(current_buffer) + if not hasattr(current_channel, 'members') or current_channel is None or current_channel.members is None: + return w.WEECHAT_RC_OK - def set_topic(self, topic): - self.topic = topic.encode('utf-8') - w.buffer_set(self.channel_buffer, "title", self.topic) + line_input = w.buffer_get_string(current_buffer, "input") + current_pos = w.buffer_get_integer(current_buffer, "input_pos") - 1 + input_length = w.buffer_get_integer(current_buffer, "input_length") - def open(self, update_remote=True): - self.create_buffer() - self.active = True - self.get_history() - if "info" in SLACK_API_TRANSLATOR[self.type]: - async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.name.lstrip("#")}) - if update_remote: - if "join" in SLACK_API_TRANSLATOR[self.type]: - async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.name.lstrip("#")}) - - def close(self, update_remote=True): - # remove from cache so messages don't reappear when reconnecting - if self.active: - self.active = False - self.current_short_name = "" - self.detach_buffer() - if update_remote: - async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}) + word_start = 0 + word_end = input_length + # If we're on a non-word, look left for something to complete + while current_pos >= 0 and line_input[current_pos] != '@' and not line_input[current_pos].isalnum(): + current_pos = current_pos - 1 + if current_pos < 0: + current_pos = 0 + for l in range(current_pos, 0, -1): + if line_input[l] != '@' and not line_input[l].isalnum(): + word_start = l + 1 + break + for l in range(current_pos, input_length): + if not line_input[l].isalnum(): + word_end = l + break + word = line_input[word_start:word_end] - def closed(self): - self.channel_buffer = None - self.last_received = None - self.close() + for m in current_channel.members: + u = current_channel.team.users.get(m, None) + if u and u.slack_name == word: + # Here, we cheat. Insert a @ in front and rely in the @ + # nicks being in the completion list + w.buffer_set(current_buffer, "input", line_input[:word_start] + "@" + line_input[word_start:]) + w.buffer_set(current_buffer, "input_pos", str(w.buffer_get_integer(current_buffer, "input_pos") + 1)) + return w.WEECHAT_RC_OK_EAT + return w.WEECHAT_RC_OK - def is_someone_typing(self): - for user in self.typing.keys(): - if self.typing[user] + 4 > time.time(): - return True - if len(self.typing) > 0: - self.typing = {} - buffer_list_update_next() - return False +def script_unloaded(): + stop_talking_to_slack() + return w.WEECHAT_RC_OK - def get_typing_list(self): - typing = [] - for user in self.typing.keys(): - if self.typing[user] + 4 > time.time(): - typing.append(user) - return typing +def stop_talking_to_slack(): + """ + complete + Prevents a race condition where quitting closes buffers + which triggers leaving the channel because of how close + buffer is handled + """ + EVENTROUTER.shutdown() + return w.WEECHAT_RC_OK - def mark_read(self, update_remote=True): - if self.channel_buffer: - w.buffer_set(self.channel_buffer, "unread", "") - if update_remote: - self.last_read = time.time() - self.update_read_marker(self.last_read) - def update_read_marker(self, time): - async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": time}) +##### New Classes +class SlackRequest(object): + """ + complete + Encapsulates a Slack api request. Valuable as an object that we can add to the queue and/or retry. + makes a SHA of the requst url and current time so we can re-tag this on the way back through. + """ + def __init__(self, token, request, post_data={}, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + self.tries = 0 + self.start_time = time.time() + self.domain = 'api.slack.com' + self.request = request + self.request_normalized = re.sub(r'\W+', '', request) + self.token = token + post_data["token"] = token + self.post_data = post_data + self.params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} + self.url = 'https://{}/api/{}?{}'.format(self.domain, request, urllib.urlencode(post_data)) + self.response_id = sha.sha("{}{}".format(self.url, self.start_time)).hexdigest() + self.retries = kwargs.get('retries', 3) +# def __repr__(self): +# return "URL: {} Tries: {} ID: {}".format(self.url, self.tries, self.response_id) + def request_string(self): + return "{}".format(self.url) + def tried(self): + self.tries += 1 + self.response_id = sha.sha("{}{}".format(self.url, time.time())).hexdigest() + def should_try(self): + return self.tries < self.retries + def retry_ready(self): + return (self.start_time + (self.tries**2)) < time.time() + +class SlackTeam(object): + """ + incomplete + Team object under which users and channels live.. Does lots. + """ + def __init__(self, eventrouter, token, websocket_url, subdomain, nick, myidentifier, users, bots, channels, **kwargs): + self.ws_url = websocket_url + self.connected = False + self.connecting = False + #self.ws = None + self.ws_counter = 0 + self.ws_replies = {} + self.eventrouter = eventrouter + self.token = token + self.team = self + self.subdomain = subdomain + self.domain = subdomain + ".slack.com" + self.preferred_name = self.domain + self.nick = nick + self.myidentifier = myidentifier + try: + if self.channels: + for c in channels.keys(): + if not self.channels.get(c): + self.channels[c] = channels[c] + except: + self.channels = channels + self.users = users + self.bots = bots + self.team_hash = SlackTeam.generate_team_hash(self.nick, self.subdomain) + #self.team_hash = str(sha.sha("{}{}".format(self.nick, self.subdomain)).hexdigest()) + self.name = self.domain + self.channel_buffer = None + self.got_history = True + self.create_buffer() + self.set_muted_channels(kwargs.get('muted_channels', "")) + for c in self.channels.keys(): + channels[c].set_related_server(self) + channels[c].check_should_open() + # self.channel_set_related_server(c) + # Last step is to make sure my nickname is the set color + self.users[self.myidentifier].force_color(w.config_string(w.config_get('weechat.color.chat_nick_self'))) + # This highlight step must happen after we have set related server + self.set_highlight_words(kwargs.get('highlight_words', "")) + def __eq__(self, compare_str): + if compare_str == self.token or compare_str == self.domain or compare_str == self.subdomain: + return True + else: + return False + def add_channel(self, channel): + self.channels[channel["id"]] = channel + channel.set_related_server(self) +# def connect_request_generate(self): +# return SlackRequest(self.token, 'rtm.start', {}) + #def close_all_buffers(self): + # for channel in self.channels: + # self.eventrouter.weechat_controller.unregister_buffer(channel.channel_buffer, update_remote=False, close_buffer=True) + # #also close this server buffer + # self.eventrouter.weechat_controller.unregister_buffer(self.channel_buffer, update_remote=False, close_buffer=True) + def create_buffer(self): + if not self.channel_buffer: + if config.short_buffer_names: + self.preferred_name = self.subdomain + elif config.server_aliases not in ['', None]: + name = config.server_aliases.get(self.subdomain, None) + if name: + self.preferred_name = name + else: + self.preferred_name = self.domain + self.channel_buffer = w.buffer_new("{}".format(self.preferred_name), "buffer_input_callback", "EVENTROUTER", "", "") + self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) + w.buffer_set(self.channel_buffer, "localvar_set_type", 'server') + if w.config_string(w.config_get('irc.look.server_buffer')) == 'merge_with_core': + w.buffer_merge(self.channel_buffer, w.buffer_search_main()) + w.buffer_set(self.channel_buffer, "nicklist", "1") + def set_muted_channels(self, muted_str): + self.muted_channels = {x for x in muted_str.split(',')} + def set_highlight_words(self, highlight_str): + self.highlight_words = {x for x in highlight_str.split(',')} + if len(self.highlight_words) > 0: + for v in self.channels.itervalues(): + v.set_highlights() + def formatted_name(self, **kwargs): + return self.domain + def buffer_prnt(self, data): + w.prnt_date_tags(self.channel_buffer, SlackTS().major, tag("backlog"), data) + def get_channel_map(self): + return {v.slack_name: k for k, v in self.channels.iteritems()} + def get_username_map(self): + return {v.slack_name: k for k, v in self.users.iteritems()} + def get_team_hash(self): + return self.team_hash + @staticmethod + def generate_team_hash(nick, subdomain): + return str(sha.sha("{}{}".format(nick, subdomain)).hexdigest()) + def refresh(self): + self.rename() def rename(self): - if self.is_someone_typing(): - new_name = ">{}".format(self.name[1:]) + pass + #def attach_websocket(self, ws): + # self.ws = ws + def is_user_present(self, user_id): + user = self.users.get(user_id) + if user.presence == 'active': + return True + else: + return False + def mark_read(self): + pass + def connect(self): + if not self.connected and not self.connecting: + self.connecting = True + if self.ws_url: + try: + ws = create_connection(self.ws_url, sslopt=sslopt_ca_certs) + w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", self.get_team_hash()) + ws.sock.setblocking(0) + self.ws = ws + #self.attach_websocket(ws) + self.set_connected() + self.connecting = False + except Exception as e: + dbg("websocket connection error: {}".format(e)) + self.connecting = False + return False + else: + #The fast reconnect failed, so start over-ish + for chan in self.channels: + self.channels[chan].got_history = False + s = SlackRequest(self.token, 'rtm.start', {}, retries=999) + self.eventrouter.receive(s) + self.connecting = False + #del self.eventrouter.teams[self.get_team_hash()] + self.set_reconnect_url(None) + def set_connected(self): + self.connected = True + def set_disconnected(self): + self.connected = False + def set_reconnect_url(self, url): + self.ws_url = url + def next_ws_transaction_id(self): + if self.ws_counter > 999: + self.ws_counter = 0 + self.ws_counter += 1 + return self.ws_counter + def send_to_websocket(self, data, expect_reply=True): + data["id"] = self.next_ws_transaction_id() + message = json.dumps(data) + try: + if expect_reply: + self.ws_replies[data["id"]] = data + self.ws.send(message) + dbg("Sent {}...".format(message[:100])) + except: + print "WS ERROR" + dbg("Unexpected error: {}\nSent: {}".format(sys.exc_info()[0], data)) + self.set_connected() + +class SlackChannel(object): + """ + Represents an individual slack channel. + """ + def __init__(self, eventrouter, **kwargs): + # We require these two things for a vaid object, + # the rest we can just learn from slack + self.active = False + for key, value in kwargs.items(): + setattr(self, key, value) + self.members = set(kwargs.get('members', set())) + self.eventrouter = eventrouter + self.slack_name = kwargs["name"] + self.slack_topic = kwargs.get("topic", {"value": ""}) + self.slack_purpose = kwargs.get("purpose", {"value": ""}) + self.identifier = kwargs["id"] + self.last_read = SlackTS(kwargs.get("last_read", SlackTS())) + #print self.last_read + self.channel_buffer = None + self.team = kwargs.get('team', None) + self.got_history = False + self.messages = {} + self.hashed_messages = {} + self.new_messages = False + self.typing = {} + self.type = 'channel' + self.set_name(self.slack_name) + #short name relates to the localvar we change for typing indication + self.current_short_name = self.name + self.update_nicklist() + def __eq__(self, compare_str): + if compare_str == self.slack_name or compare_str == self.formatted_name() or compare_str == self.formatted_name(style="long_default"): + return True else: - new_name = self.name + return False + def __repr__(self): + return "Name:{} Identifier:{}".format(self.name, self.identifier) + def set_name(self, slack_name): + self.name = "#" + slack_name + def refresh(self): + return self.rename() + def rename(self): if self.channel_buffer: + new_name = self.formatted_name(typing=self.is_someone_typing(), style="sidebar") if self.current_short_name != new_name: self.current_short_name = new_name w.buffer_set(self.channel_buffer, "short_name", new_name) - - def buffer_prnt(self, user='unknown_user', message='no message', time=0): - """ - writes output (message) to a buffer (channel) - """ - set_read_marker = False - time_float = float(time) - tags = "nick_" + user - user_obj = self.server.users.find(user) - # XXX: we should not set log1 for robots. - if time_float != 0 and self.last_read >= time_float: - tags += ",no_highlight,notify_none,logger_backlog_end" - set_read_marker = True - elif message.find(self.server.nick.encode('utf-8')) > -1: - tags += ",notify_highlight,log1" - elif user != self.server.nick and self.name in self.server.users: - tags += ",notify_private,notify_message,log1,irc_privmsg" - elif self.muted: - tags += ",no_highlight,notify_none,logger_backlog_end" - elif user in [x.strip() for x in w.prefix("join"), w.prefix("quit")]: - tags += ",irc_smart_filter" + return True + return False + def formatted_name(self, style="default", typing=False, **kwargs): + if config.channel_name_typing_indicator: + if not typing: + prepend = "#" + else: + prepend = ">" else: - tags += ",notify_message,log1,irc_privmsg" - # don't write these to local log files - # tags += ",no_log" - time_int = int(time_float) + prepend = "#" + select = { + "default": prepend + self.slack_name, + "sidebar": prepend + self.slack_name, + "base": self.slack_name, + "long_default": "{}.{}{}".format(self.team.preferred_name, prepend, self.slack_name), + "long_base": "{}.{}".format(self.team.preferred_name, self.slack_name), + } + return select[style] + def render_topic(self, topic=None): if self.channel_buffer: - prefix_same_nick = w.config_string(w.config_get('weechat.look.prefix_same_nick')) - if user == self.last_active_user and prefix_same_nick != "": - if config.colorize_nicks and user_obj: - name = user_obj.color + prefix_same_nick + if not topic: + if self.slack_topic['value'] != "": + encoded_topic = self.slack_topic['value'].encode('utf-8') else: - name = prefix_same_nick + encoded_topic = self.slack_purpose['value'].encode('utf-8') else: - nick_prefix = w.config_string(w.config_get('weechat.look.nick_prefix')) - nick_prefix_color_name = w.config_string(w.config_get('weechat.color.chat_nick_prefix')) - nick_prefix_color = w.color(nick_prefix_color_name) - - nick_suffix = w.config_string(w.config_get('weechat.look.nick_suffix')) - nick_suffix_color_name = w.config_string(w.config_get('weechat.color.chat_nick_prefix')) - nick_suffix_color = w.color(nick_suffix_color_name) - - if user_obj: - name = user_obj.formatted_name() - self.last_active_user = user - # XXX: handle bots properly here. - else: - name = user - self.last_active_user = None - name = nick_prefix_color + nick_prefix + w.color("reset") + name + nick_suffix_color + nick_suffix + w.color("reset") - name = name.decode('utf-8') - # colorize nicks in each line - chat_color = w.config_string(w.config_get('weechat.color.chat')) - if type(message) is not unicode: - message = message.decode('UTF-8', 'replace') - curr_color = w.color(chat_color) - if config.colorize_nicks and config.colorize_messages and user_obj: - curr_color = user_obj.color - message = curr_color + message - for user in self.server.users: - if user.name in message: - message = user.name_regex.sub( - r'\1\2{}\3'.format(user.formatted_name() + curr_color), - message) - - message = HTMLParser.HTMLParser().unescape(message) - data = u"{}\t{}".format(name, message).encode('utf-8') - w.prnt_date_tags(self.channel_buffer, time_int, tags, data) - - if set_read_marker: - self.mark_read(False) + encoded_topic = topic.encode('utf-8') + self.encoded_topic = topic.encode('utf-8') + w.buffer_set(self.channel_buffer, "title", encoded_topic) + def update_from_message_json(self, message_json): + for key, value in message_json.items(): + setattr(self, key, value) + def open(self, update_remote=True): + if update_remote: + if "join" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + self.create_buffer() + self.active = True + self.get_history() + if "info" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + #self.create_buffer() + def check_should_open(self, force=False): + try: + if self.is_archived: + return + except: + pass + if force: + self.create_buffer() else: - self.open(False) - self.last_received = time - self.unset_typing(user) - - def buffer_redraw(self): - if self.channel_buffer and not self.scrolling: - w.buffer_clear(self.channel_buffer) - self.messages.sort() - for message in self.messages: - process_message(message.message_json, False) - - def set_scrolling(self): - self.scrolling = True - - def unset_scrolling(self): - self.scrolling = False - - def has_message(self, ts): - return self.messages.count(ts) > 0 - - def change_message(self, ts, text=None, suffix=''): - if self.has_message(ts): - message_index = self.messages.index(ts) - - if text is not None: - self.messages[message_index].change_text(text) - text = render_message(self.messages[message_index].message_json, True) - - # if there is only one message with this timestamp, modify it directly. - # we do this because time resolution in weechat is less than slack - int_time = int(float(ts)) - if self.messages.count(str(int_time)) == 1: - modify_buffer_line(self.channel_buffer, text + suffix, int_time) - # otherwise redraw the whole buffer, which is expensive + for reason in ["is_member", "is_open", "unread_count_display"]: + try: + if eval("self." + reason): + self.create_buffer() + if config.background_load_all_history: + self.get_history(slow_queue=True) + except: + pass + def set_related_server(self, team): + self.team = team + def set_highlights(self): + #highlight my own name and any set highlights + if self.channel_buffer: + highlights = self.team.highlight_words.union({'@' + self.team.nick, "!here", "!channel", "!everyone"}) + h_str = ",".join(highlights) + w.buffer_set(self.channel_buffer, "highlight_words", h_str) + def create_buffer(self): + """ + incomplete (muted doesn't work) + Creates the weechat buffer where the channel magic happens. + """ + if not self.channel_buffer: + self.active = True + self.channel_buffer = w.buffer_new(self.formatted_name(style="long_default"), "buffer_input_callback", "EVENTROUTER", "", "") + self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) + if self.type == "im": + w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') else: - self.buffer_redraw() - return True - - def add_reaction(self, ts, reaction, user): - if self.has_message(ts): - message_index = self.messages.index(ts) - self.messages[message_index].add_reaction(reaction, user) - self.change_message(ts) - return True - - def remove_reaction(self, ts, reaction, user): - if self.has_message(ts): - message_index = self.messages.index(ts) - self.messages[message_index].remove_reaction(reaction, user) - self.change_message(ts) - return True - - def send_add_reaction(self, msg_number, reaction): - self.send_change_reaction("reactions.add", msg_number, reaction) + w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') + w.buffer_set(self.channel_buffer, "localvar_set_channel", self.formatted_name()) + w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) + self.render_topic() + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + if self.channel_buffer: + #if self.team.server_alias: + #w.buffer_set(self.channel_buffer, "localvar_set_server", self.team.server_alias) + #else: + w.buffer_set(self.channel_buffer, "localvar_set_server", self.team.preferred_name) + #else: + # self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) + try: + for c in range(self.unread_count_display): + if self.type == "im": + w.buffer_set(self.channel_buffer, "hotlist", "2") + else: + w.buffer_set(self.channel_buffer, "hotlist", "1") + else: + pass + #dbg("no unread in {}".format(self.name)) + except: + pass - def send_remove_reaction(self, msg_number, reaction): - self.send_change_reaction("reactions.remove", msg_number, reaction) + self.update_nicklist() + #dbg("exception no unread count") + #if self.unread_count != 0 and not self.muted: + # w.buffer_set(self.channel_buffer, "hotlist", "1") + def destroy_buffer(self, update_remote): + if self.channel_buffer is not None: + self.channel_buffer = None + self.messages = {} + self.hashed_messages = {} + self.got_history = False + #if update_remote and not eventrouter.shutting_down: + self.active = False + if update_remote and not self.eventrouter.shutting_down: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + def buffer_prnt(self, nick, text, timestamp=str(time.time()), tagset=None, tag_nick=None, **kwargs): + data = "{}\t{}".format(nick, text) + ts = SlackTS(timestamp) + last_read = SlackTS(self.last_read) + #without this, DMs won't open automatically + if not self.channel_buffer and ts > last_read: + self.open(update_remote=False) + if self.channel_buffer: + #backlog messages - we will update the read marker as we print these + backlog = True if ts <= last_read else False + if tagset: + tags = tag(tagset, user=tag_nick) + self.new_messages = True + + #we have to infer the tagset because we weren't told + elif ts <= last_read: + tags = tag("backlog", user=tag_nick) + elif self.type in ["im", "mpdm"]: + if nick != self.team.nick: + tags = tag("dm", user=tag_nick) + self.new_messages = True + else: + tags = tag("dmfromme") + else: + tags = tag("default", user=tag_nick) + self.new_messages = True - def send_change_reaction(self, method, msg_number, reaction): - if 0 < msg_number < len(self.messages): - timestamp = self.messages[-msg_number].message_json["ts"] - data = {"channel": self.identifier, "timestamp": timestamp, "name": reaction} - async_slack_api_request(self.server.domain, self.server.token, method, data) + try: + if config.unhide_buffers_with_activity and not self.is_visible() and (self.identifier not in self.team.muted_channels): + w.buffer_set(self.channel_buffer, "hidden", "0") - def change_previous_message(self, old, new, flags): + w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) + modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) + if backlog: + self.mark_read(ts, update_remote=False, force=True) + except: + dbg("Problem processing buffer_prnt") + def send_message(self, message, request_dict_ext={}): + #team = self.eventrouter.teams[self.team] + message = linkify_text(message, self.team, self) + dbg(message) + request = {"type": "message", "channel": self.identifier, "text": message, "_team": self.team.team_hash, "user": self.team.myidentifier} + request.update(request_dict_ext) + self.team.send_to_websocket(request) + self.mark_read(update_remote=False, force=True) + def store_message(self, message, team, from_me=False): + if not self.active: + return + if from_me: + message.message_json["user"] = team.myidentifier + self.messages[SlackTS(message.ts)] = message + if len(self.messages.keys()) > SCROLLBACK_SIZE: + mk = self.messages.keys() + mk.sort() + for k in mk[:SCROLLBACK_SIZE]: + msg_to_delete = self.messages[k] + if msg_to_delete.hash: + del self.hashed_messages[msg_to_delete.hash] + del self.messages[k] + def change_message(self, ts, text=None, suffix=None): + ts = SlackTS(ts) + if ts in self.messages: + m = self.messages[ts] + if text: + m.change_text(text) + if suffix: + m.change_suffix(suffix) + text = m.render(force=True) + modify_buffer_line(self.channel_buffer, text, ts.major, ts.minor) + return True + def edit_previous_message(self, old, new, flags): message = self.my_last_message() if new == "" and old == "": - async_slack_api_request(self.server.domain, self.server.token, 'chat.delete', {"channel": self.identifier, "ts": message['ts']}) + s = SlackRequest(self.team.token, "chat.delete", {"channel": self.identifier, "ts": message['ts']}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) else: num_replace = 1 if 'g' in flags: num_replace = 0 new_message = re.sub(old, new, message["text"], num_replace) if new_message != message["text"]: - async_slack_api_request(self.server.domain, self.server.token, 'chat.update', {"channel": self.identifier, "ts": message['ts'], "text": new_message.encode("utf-8")}) - + s = SlackRequest(self.team.token, "chat.update", {"channel": self.identifier, "ts": message['ts'], "text": new_message.encode("utf-8")}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) def my_last_message(self): - for message in reversed(self.messages): - if "user" in message.message_json and "text" in message.message_json and message.message_json["user"] == self.server.users.find(self.server.nick).identifier: - return message.message_json - - def cache_message(self, message_json, from_me=False): - if from_me: - message_json["user"] = self.server.users.find(self.server.nick).identifier - self.messages.append(Message(message_json)) - if len(self.messages) > SCROLLBACK_SIZE: - self.messages = self.messages[-SCROLLBACK_SIZE:] - - def get_history(self): - if self.active: - for message in message_cache[self.identifier]: - process_message(json.loads(message), True) - async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}) - self.got_history = True - - -class GroupChannel(Channel): - - def __init__(self, server, **kwargs): - super(GroupChannel, self).__init__(server, **kwargs) - self.type = "group" - - -class MpdmChannel(Channel): - - def __init__(self, server, **kwargs): - n = kwargs.get('name') - name = "|".join("-".join(n.split("-")[1:-1]).split("--")) - kwargs["name"] = name - super(MpdmChannel, self).__init__(server, **kwargs) - self.type = "group" - - -class DmChannel(Channel): - - def __init__(self, server, **kwargs): - super(DmChannel, self).__init__(server, **kwargs) - self.type = "im" - - def rename(self): - if self.server.users.find(self.name).presence == "active": - new_name = self.server.users.find(self.name).formatted_name('+', config.colorize_private_chats) - else: - new_name = self.server.users.find(self.name).formatted_name(' ', config.colorize_private_chats) - - if self.channel_buffer: - if self.current_short_name != new_name: - self.current_short_name = new_name - w.buffer_set(self.channel_buffer, "short_name", new_name) - - def update_nicklist(self, user=None): - pass - - -class User(object): - - def __init__(self, server, name, identifier, presence="away", deleted=False, is_bot=False): - self.server = server - self.name = name - self.identifier = identifier - self.deleted = deleted - self.presence = presence - - self.channel_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, self.name)) - self.update_color() - self.name_regex = re.compile(r"([\W]|\A)(@{0,1})" + self.name + "('s|[^'\w]|\Z)") - self.is_bot = is_bot - - if deleted: - return - self.nicklist_pointer = w.nicklist_add_nick(server.buffer, "", self.name, self.color_name, "", "", 1) - if self.presence == 'away': - w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "0") - else: - w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "1") -# w.nicklist_add_nick(server.buffer, "", self.formatted_name(), "", "", "", 1) - - def __str__(self): - return self.name - - def __repr__(self): - return self.name - - def __eq__(self, compare_str): - try: - if compare_str == self.name or compare_str == self.identifier: - return True - elif compare_str[0] == '@' and compare_str[1:] == self.name: + for message in reversed(self.sorted_message_keys()): + m = self.messages[message] + if "user" in m.message_json and "text" in m.message_json and m.message_json["user"] == self.team.myidentifier: + return m.message_json + def is_visible(self): + return w.buffer_get_integer(self.channel_buffer, "hidden") == 0 + def get_history(self, slow_queue=False): + if not self.got_history: + #we have probably reconnected. flush the buffer + if self.team.connected: + w.buffer_clear(self.channel_buffer) + self.buffer_prnt('', 'getting channel history...', tagset='backlog') + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}, team_hash=self.team.team_hash, channel_identifier=self.identifier, clear=True) + if not slow_queue: + self.eventrouter.receive(s) + else: + self.eventrouter.receive_slow(s) + self.got_history = True + def send_add_reaction(self, msg_number, reaction): + self.send_change_reaction("reactions.add", msg_number, reaction) + def send_remove_reaction(self, msg_number, reaction): + self.send_change_reaction("reactions.remove", msg_number, reaction) + def send_change_reaction(self, method, msg_number, reaction): + if 0 < msg_number < len(self.messages): + timestamp = self.sorted_message_keys()[-msg_number] + data = {"channel": self.identifier, "timestamp": timestamp, "name": reaction} + s = SlackRequest(self.team.token, method, data) + self.eventrouter.receive(s) + def sorted_message_keys(self): + keys = [] + for k in self.messages: + if type(self.messages[k]) == SlackMessage: + keys.append(k) + return sorted(keys) + # Typing related + def set_typing(self, user): + if self.channel_buffer and self.is_visible(): + self.typing[user] = time.time() + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + def unset_typing(self, user): + if self.channel_buffer and self.is_visible(): + u = self.typing.get(user, None) + if u: + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + def is_someone_typing(self): + """ + Walks through dict of typing folks in a channel and fast + returns if any of them is actively typing. If none are, + nulls the dict and returns false. + """ + for user, timestamp in self.typing.iteritems(): + if timestamp + 4 > time.time(): return True + if len(self.typing) > 0: + self.typing = {} + self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + return False + def get_typing_list(self): + """ + Returns the names of everyone in the channel who is currently typing. + """ + typing = [] + for user, timestamp in self.typing.iteritems(): + if timestamp + 4 > time.time(): + typing.append(user) else: - return False - except: - return False - - def get_aliases(self): - return [self.name, "@" + self.name, self.identifier] - - def set_active(self): - if not self.deleted: - self.presence = "active" - dm_channel = self.server.channels.find(self.name) - if dm_channel and dm_channel.active: - buffer_list_update_next() - - return #temporarily noop this - for channel in self.server.channels: - if channel.has_user(self.identifier): - channel.update_nicklist(self.identifier) - w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "1") - - def set_inactive(self): - if not self.deleted: - self.presence = "away" - dm_channel = self.server.channels.find(self.name) - if dm_channel and dm_channel.active: - buffer_list_update_next() - - return #temporarily noop this - if self.deleted: + del self.typing[user] + return typing + def mark_read(self, ts=None, update_remote=True, force=False): + if not ts: + ts = SlackTS() + if self.new_messages or force: + if self.channel_buffer: + w.buffer_set(self.channel_buffer, "unread", "") + w.buffer_set(self.channel_buffer, "hotlist", "-1") + if update_remote: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["mark"], {"channel": self.identifier, "ts": ts}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + self.new_messages = False + def user_joined(self, user_id): + #ugly hack - for some reason this gets turned into a list + self.members = set(self.members) + self.members.add(user_id) + self.update_nicklist(user_id) + def user_left(self, user_id): + #pass + #filter(lambda u: u != user_id, self.members) + self.members.discard(user_id) + self.update_nicklist(user_id) + def update_nicklist(self, user=None): + if not self.channel_buffer: return + if self.type not in ["channel", "group"]: + return + w.buffer_set(self.channel_buffer, "nicklist", "1") + # create nicklists for the current channel if they don't exist + # if they do, use the existing pointer + #TODO: put this back for mithrandir + #here = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_HERE) + #if not here: + # here = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_HERE, "weechat.color.nicklist_group", 1) + #afk = w.nicklist_search_group(self.channel_buffer, '', NICK_GROUP_AWAY) + #if not afk: + # afk = w.nicklist_add_group(self.channel_buffer, '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 1) + + if user and len(self.members) < 1000: + user = self.team.users[user] + nick = w.nicklist_search_nick(self.channel_buffer, "", user.slack_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.. + if user.identifier in self.members: + w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, "", "", 1) + #w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) - for channel in self.server.channels: - if channel.has_user(self.identifier): - channel.update_nicklist(self.identifier) - w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "0") - - def update_color(self): - if config.colorize_nicks: - if self.name == self.server.nick: - self.color_name = w.config_string(w.config_get('weechat.color.chat_nick_self')) + # if we didn't get a user, build a complete list. this is expensive. + else: + if len(self.members) < 1000: + try: + for user in self.members: + user = self.team.users[user] + if user.deleted: + continue + w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, "", "", 1) + #w.nicklist_add_nick(self.channel_buffer, here, user.name, user.color_name, "", "", 1) + except Exception as e: + dbg("DEBUG: {} {} {}".format(self.identifier, self.name, e)) else: - self.color_name = w.info_get('irc_nick_color_name', self.name) + for fn in ["1| too", "2| many", "3| users", "4| to", "5| show"]: + w.nicklist_add_group(self.channel_buffer, '', fn, w.color('white'), 1) + def hash_message(self, ts): + ts = SlackTS(ts) + + def calc_hash(msg): + return sha.sha(str(msg.ts)).hexdigest() + + if ts in self.messages and not self.messages[ts].hash: + message = self.messages[ts] + tshash = calc_hash(message) + l = 3 + shorthash = tshash[:l] + while any(x.startswith(shorthash) for x in self.hashed_messages): + l += 1 + shorthash = tshash[:l] + + if shorthash[:-1] in self.hashed_messages: + col_msg = self.hashed_messages.pop(shorthash[:-1]) + col_new_hash = calc_hash(col_msg)[:l] + col_msg.hash = col_new_hash + self.hashed_messages[col_new_hash] = col_msg + self.change_message(str(col_msg.ts)) + if col_msg.thread_channel: + col_msg.thread_channel.rename() + + self.hashed_messages[shorthash] = message + message.hash = shorthash + + +class SlackDMChannel(SlackChannel): + """ + Subclass of a normal channel for person-to-person communication, which + has some important differences. + """ + def __init__(self, eventrouter, users, **kwargs): + dmuser = kwargs["user"] + kwargs["name"] = users[dmuser].name + super(SlackDMChannel, self).__init__(eventrouter, **kwargs) + self.type = 'im' + self.update_color() + self.set_name(self.slack_name) + def set_name(self, slack_name): + self.name = slack_name + def create_buffer(self): + if not self.channel_buffer: + super(SlackDMChannel, self).create_buffer() + w.buffer_set(self.channel_buffer, "localvar_set_type", 'private') + def update_color(self): + if config.colorize_private_chats: + self.color_name = w.info_get('irc_nick_color_name', self.name.encode('utf-8')) self.color = w.color(self.color_name) else: self.color = "" self.color_name = "" - - def formatted_name(self, prepend="", enable_color=True): - if config.colorize_nicks and enable_color: + def formatted_name(self, style="default", typing=False, present=True, enable_color=False, **kwargs): + if config.colorize_private_chats and enable_color: print_color = self.color else: print_color = "" - return print_color + prepend + self.name - - def create_dm_channel(self): - async_slack_api_request(self.server.domain, self.server.token, "im.open", {"user": self.identifier}) - - -class Bot(object): - - def __init__(self, server, name, identifier, deleted=False): - self.server = server - self.name = name - self.identifier = identifier - self.deleted = deleted - self.update_color() - - def __eq__(self, compare_str): - if compare_str == self.identifier or compare_str == self.name: - return True - else: - return False - - def __str__(self): - return "{}".format(self.identifier) - - def __repr__(self): - return "{}".format(self.identifier) - - def update_color(self): - if config.colorize_nicks: - self.color_name = w.info_get('irc_nick_color_name', self.name.encode('utf-8')) - self.color = w.color(self.color_name) + if not present: + prepend = " " else: - self.color_name = "" - self.color = "" - - def formatted_name(self, prepend="", enable_color=True): - if config.colorize_nicks and enable_color: - print_color = self.color - else: - print_color = "" - return print_color + prepend + self.name - - -class Message(object): - - def __init__(self, message_json): - self.message_json = message_json - self.ts = message_json['ts'] - # split timestamp into time and counter - self.ts_time, self.ts_counter = message_json['ts'].split('.') - - def change_text(self, new_text): - if not isinstance(new_text, unicode): - new_text = unicode(new_text, 'utf-8') - self.message_json["text"] = new_text - - def add_reaction(self, reaction, user): - if "reactions" in self.message_json: - found = False - for r in self.message_json["reactions"]: - if r["name"] == reaction and user not in r["users"]: - r["users"].append(user) - found = True - - if not found: - self.message_json["reactions"].append({u"name": reaction, u"users": [user]}) - else: - self.message_json["reactions"] = [{u"name": reaction, u"users": [user]}] - - def remove_reaction(self, reaction, user): - if "reactions" in self.message_json: - for r in self.message_json["reactions"]: - if r["name"] == reaction and user in r["users"]: - r["users"].remove(user) - else: - pass - - def __eq__(self, other): - return self.ts_time == other or self.ts == other - - def __repr__(self): - return "{} {} {} {}\n".format(self.ts_time, self.ts_counter, self.ts, self.message_json) - - def __lt__(self, other): - return self.ts < other.ts - - -def slack_buffer_or_ignore(f): - """ - Only run this function if we're in a slack buffer, else ignore - """ - @wraps(f) - def wrapper(current_buffer, *args, **kwargs): - server = servers.find(current_domain_name()) - if not server: - return w.WEECHAT_RC_OK - return f(current_buffer, *args, **kwargs) - return wrapper - - -def slack_command_cb(data, current_buffer, args): - a = args.split(' ', 1) - if len(a) > 1: - function_name, args = a[0], " ".join(a[1:]) - else: - function_name, args = a[0], None - - try: - cmds[function_name](current_buffer, args) - except KeyError: - w.prnt("", "Command not found: " + function_name) - return w.WEECHAT_RC_OK - - -@slack_buffer_or_ignore -def me_command_cb(data, current_buffer, args): - if channels.find(current_buffer): - # channel = channels.find(current_buffer) - # nick = channel.server.nick - message = "_{}_".format(args) - buffer_input_cb("", current_buffer, message) - return w.WEECHAT_RC_OK - - -@slack_buffer_or_ignore -def join_command_cb(data, current_buffer, args): - args = args.split() - if len(args) < 2: - w.prnt(current_buffer, "Missing channel argument") - return w.WEECHAT_RC_OK_EAT - elif command_talk(current_buffer, args[1]): - return w.WEECHAT_RC_OK_EAT - else: - return w.WEECHAT_RC_OK - - -@slack_buffer_or_ignore -def part_command_cb(data, current_buffer, args): - if channels.find(current_buffer) or servers.find(current_buffer): - args = args.split() - if len(args) > 1: - channel = args[1:] - servers.find(current_domain_name()).channels.find(channel).close(True) - else: - channels.find(current_buffer).close(True) - return w.WEECHAT_RC_OK_EAT - else: - return w.WEECHAT_RC_OK - - -# Wrap command_ functions that require they be performed in a slack buffer -def slack_buffer_required(f): - @wraps(f) - def wrapper(current_buffer, *args, **kwargs): - server = servers.find(current_domain_name()) - if not server: - w.prnt(current_buffer, "This command must be used in a slack buffer") - return w.WEECHAT_RC_ERROR - return f(current_buffer, *args, **kwargs) - return wrapper - - -def command_register(current_buffer, args): - CLIENT_ID = "2468770254.51917335286" - CLIENT_SECRET = "dcb7fe380a000cba0cca3169a5fe8d70" # this is not really a secret - if not args: - message = """ -# ### Retrieving a Slack token via OAUTH #### - -1) Paste this into a browser: https://slack.com/oauth/authorize?client_id=2468770254.51917335286&scope=client -2) Select the team you wish to access from wee-slack in your browser. -3) Click "Authorize" in the browser **IMPORTANT: the redirect will fail, this is expected** -4) Copy the "code" portion of the URL to your clipboard -5) Return to weechat and run `/slack register [code]` -6) Add the returned token per the normal wee-slack setup instructions - - -""" - w.prnt(current_buffer, message) - else: - aargs = args.split(None, 2) - if len(aargs) != 1: - w.prnt(current_buffer, "ERROR: invalid args to register") - else: - # w.prnt(current_buffer, "https://slack.com/api/oauth.access?client_id={}&client_secret={}&code={}".format(CLIENT_ID, CLIENT_SECRET, aargs[0])) - ret = urllib.urlopen("https://slack.com/api/oauth.access?client_id={}&client_secret={}&code={}".format(CLIENT_ID, CLIENT_SECRET, aargs[0])).read() - d = json.loads(ret) - if d["ok"] == True: - w.prnt(current_buffer, "Success! Access token is: " + d['access_token']) - else: - w.prnt(current_buffer, "Failed! Error is: " + d['error']) - - -@slack_buffer_or_ignore -def msg_command_cb(data, current_buffer, args): - dbg("msg_command_cb") - aargs = args.split(None, 2) - who = aargs[1] - - command_talk(current_buffer, who) - - if len(aargs) > 2: - message = aargs[2] - server = servers.find(current_domain_name()) - if server: - channel = server.channels.find(who) - channel.send_message(message) - return w.WEECHAT_RC_OK_EAT + prepend = "+" + select = { + "default": self.slack_name, + "sidebar": prepend + self.slack_name, + "base": self.slack_name, + "long_default": "{}.{}".format(self.team.preferred_name, self.slack_name), + "long_base": "{}.{}".format(self.team.preferred_name, self.slack_name), + } + return print_color + select[style] + def open(self, update_remote=True): + self.create_buffer() + #self.active = True + self.get_history() + if "info" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + if update_remote: + if "join" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"user": self.user}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + self.create_buffer() + def rename(self): + if self.channel_buffer: + new_name = self.formatted_name(style="sidebar", present=self.team.is_user_present(self.user), enable_color=config.colorize_private_chats) + if self.current_short_name != new_name: + self.current_short_name = new_name + w.buffer_set(self.channel_buffer, "short_name", new_name) + return True + return False + def refresh(self): + return self.rename() -@slack_buffer_required -def command_upload(current_buffer, args): +class SlackGroupChannel(SlackChannel): """ - Uploads a file to the current buffer - /slack upload [file_path] + A group channel is a private discussion group. """ - post_data = {} - channel = current_buffer_name(short=True) - domain = current_domain_name() - token = servers.find(domain).token - - if servers.find(domain).channels.find(channel): - channel_identifier = servers.find(domain).channels.find(channel).identifier - - if channel_identifier: - post_data["token"] = token - post_data["channels"] = channel_identifier - post_data["file"] = args - async_slack_api_upload_request(token, "files.upload", post_data) - + def __init__(self, eventrouter, **kwargs): + super(SlackGroupChannel, self).__init__(eventrouter, **kwargs) + self.name = "#" + kwargs['name'] + self.type = "group" + self.set_name(self.slack_name) + def set_name(self, slack_name): + self.name = "#" + slack_name + #def formatted_name(self, prepend="#", enable_color=True, basic=False): + # return prepend + self.slack_name -def command_talk(current_buffer, args): +class SlackMPDMChannel(SlackChannel): """ - Open a chat with the specified user - /slack talk [user] + An MPDM channel is a special instance of a 'group' channel. + We change the name to look less terrible in weechat. """ - - server = servers.find(current_domain_name()) - if server: - channel = server.channels.find(args) - if channel is None: - user = server.users.find(args) - if user: - user.create_dm_channel() + def __init__(self, eventrouter, **kwargs): + super(SlackMPDMChannel, self).__init__(eventrouter, **kwargs) + n = kwargs.get('name') + self.set_name(n) + self.type = "group" + def open(self, update_remote=False): + self.create_buffer() + self.active = True + self.get_history() + if "info" in SLACK_API_TRANSLATOR[self.type]: + s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + self.eventrouter.receive(s) + #self.create_buffer() + def set_name(self, n): + self.name = "|".join("-".join(n.split("-")[1:-1]).split("--")) + def formatted_name(self, style="default", typing=False, **kwargs): + adjusted_name = "|".join("-".join(self.slack_name.split("-")[1:-1]).split("--")) + if config.channel_name_typing_indicator: + if not typing: + prepend = "#" else: - server.buffer_prnt("User or channel {} not found.".format(args)) + prepend = ">" else: - channel.open() - if config.switch_buffer_on_join: - w.buffer_set(channel.channel_buffer, "display", "1") - return True - else: - return False - - -def command_join(current_buffer, args): - """ - Join the specified channel - /slack join [channel] - """ - domain = current_domain_name() - if domain == "": - if len(servers) == 1: - domain = servers[0] - else: - w.prnt(current_buffer, "You are connected to multiple Slack instances, please execute /join from a server buffer. i.e. (domain).slack.com") - return - channel = servers.find(domain).channels.find(args) - if channel is not None: - servers.find(domain).channels.find(args).open() - else: - w.prnt(current_buffer, "Channel not found.") - + prepend = "#" + select = { + "default": adjusted_name, + "sidebar": prepend + adjusted_name, + "base": adjusted_name, + "long_default": "{}.{}".format(self.team.preferred_name, adjusted_name), + "long_base": "{}.{}".format(self.team.preferred_name, adjusted_name), + } + return select[style] + +# def formatted_name(self, **kwargs): +# return self.name + def rename(self): + pass -@slack_buffer_required -def command_channels(current_buffer, args): +class SlackThreadChannel(object): """ - List all the channels for the slack instance (name, id, active) - /slack channels + A thread channel is a virtual channel. We don't inherit from + SlackChannel, because most of how it operates will be different. """ - server = servers.find(current_domain_name()) - for channel in server.channels: - line = "{:<25} {} {}".format(channel.name, channel.identifier, channel.active) - server.buffer_prnt(line) - - -def command_nodistractions(current_buffer, args): - global hide_distractions - hide_distractions = not hide_distractions - if config.distracting_channels != ['']: - for channel in config.distracting_channels: - try: - channel_buffer = channels.find(channel).channel_buffer - if channel_buffer: - w.buffer_set(channels.find(channel).channel_buffer, "hidden", str(int(hide_distractions))) - except: - dbg("Can't hide channel {} .. removing..".format(channel), main_buffer=True) - config.distracting_channels.pop(config.distracting_channels.index(channel)) - save_distracting_channels() + def __init__(self, eventrouter, parent_message): + self.eventrouter = eventrouter + self.parent_message = parent_message + self.channel_buffer = None + #self.identifier = "" + #self.name = "#" + kwargs['name'] + self.type = "thread" + self.got_history = False + self.label = None + #self.set_name(self.slack_name) + #def set_name(self, slack_name): + # self.name = "#" + slack_name + def formatted_name(self, style="default", **kwargs): + hash_or_ts = self.parent_message.hash or self.parent_message.ts + styles = { + "default": " +{}".format(hash_or_ts), + "long_default": "{}.{}".format(self.parent_message.channel.formatted_name(style="long_default"), hash_or_ts), + "sidebar": " +{}".format(hash_or_ts), + } + return styles[style] + def refresh(self): + self.rename() + def mark_read(self, ts=None, update_remote=True, force=False): + if self.channel_buffer: + w.buffer_set(self.channel_buffer, "unread", "") + w.buffer_set(self.channel_buffer, "hotlist", "-1") + def buffer_prnt(self, nick, text, timestamp, **kwargs): + data = "{}\t{}".format(nick, text) + ts = SlackTS(timestamp) + if self.channel_buffer: + #backlog messages - we will update the read marker as we print these + #backlog = False + #if ts <= SlackTS(self.last_read): + # tags = tag("backlog") + # backlog = True + #elif self.type in ["im", "mpdm"]: + # tags = tag("dm") + # self.new_messages = True + #else: + tags = tag("default") + #self.new_messages = True + w.prnt_date_tags(self.channel_buffer, ts.major, tags, data) + modify_print_time(self.channel_buffer, ts.minorstr(), ts.major) + #if backlog: + # self.mark_read(ts, update_remote=False, force=True) + def get_history(self): + self.got_history = True + for message in self.parent_message.submessages: -def command_distracting(current_buffer, args): - if channels.find(current_buffer) is None: - w.prnt(current_buffer, "This command must be used in a channel buffer") - return - fullname = channels.find(current_buffer).fullname() - if config.distracting_channels.count(fullname) == 0: - config.distracting_channels.append(fullname) - else: - config.distracting_channels.pop(config.distracting_channels.index(fullname)) - save_distracting_channels() + #message = SlackMessage(message_json, team, channel) + text = message.render() + #print text + suffix = '' + if 'edited' in message.message_json: + suffix = ' (edited)' + #try: + # channel.unread_count += 1 + #except: + # channel.unread_count = 1 + self.buffer_prnt(message.sender, text + suffix, message.ts) -def save_distracting_channels(): - w.config_set_plugin('distracting_channels', ','.join(config.distracting_channels)) + def send_message(self, message): + #team = self.eventrouter.teams[self.team] + message = linkify_text(message, self.parent_message.team, self) + dbg(message) + request = {"type": "message", "channel": self.parent_message.channel.identifier, "text": message, "_team": self.parent_message.team.team_hash, "user": self.parent_message.team.myidentifier, "thread_ts": str(self.parent_message.ts)} + self.parent_message.team.send_to_websocket(request) + self.mark_read(update_remote=False, force=True) + def open(self, update_remote=True): + self.create_buffer() + self.active = True + self.get_history() + #if "info" in SLACK_API_TRANSLATOR[self.type]: + # s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["info"], {"name": self.identifier}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + # self.eventrouter.receive(s) + #if update_remote: + # if "join" in SLACK_API_TRANSLATOR[self.type]: + # s = SlackRequest(self.team.token, SLACK_API_TRANSLATOR[self.type]["join"], {"name": self.name}, team_hash=self.team.team_hash, channel_identifier=self.identifier) + # self.eventrouter.receive(s) + self.create_buffer() -@slack_buffer_required -def command_users(current_buffer, args): - """ - List all the users for the slack instance (name, id, away) - /slack users - """ - server = servers.find(current_domain_name()) - for user in server.users: - line = "{:<40} {} {}".format(user.formatted_name(), user.identifier, user.presence) - server.buffer_prnt(line) + def rename(self): + if self.channel_buffer and not self.label: + w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) + def create_buffer(self): + """ + incomplete (muted doesn't work) + Creates the weechat buffer where the thread magic happens. + """ + if not self.channel_buffer: + self.channel_buffer = w.buffer_new(self.formatted_name(style="long_default"), "buffer_input_callback", "EVENTROUTER", "", "") + self.eventrouter.weechat_controller.register_buffer(self.channel_buffer, self) + w.buffer_set(self.channel_buffer, "localvar_set_type", 'channel') + w.buffer_set(self.channel_buffer, "localvar_set_channel", self.formatted_name()) + w.buffer_set(self.channel_buffer, "short_name", self.formatted_name(style="sidebar", enable_color=True)) + time_format = w.config_string(w.config_get("weechat.look.buffer_time_format")) + parent_time = time.localtime(SlackTS(self.parent_message.ts).major) + topic = '{} {} | {}'.format(time.strftime(time_format, parent_time), self.parent_message.sender, self.parent_message.render() ) + w.buffer_set(self.channel_buffer, "title", topic.encode('utf-8')) + + #self.eventrouter.weechat_controller.set_refresh_buffer_list(True) + + #try: + # if self.unread_count != 0: + # for c in range(1, self.unread_count): + # if self.type == "im": + # w.buffer_set(self.channel_buffer, "hotlist", "2") + # else: + # w.buffer_set(self.channel_buffer, "hotlist", "1") + # else: + # pass + # #dbg("no unread in {}".format(self.name)) + #except: + # pass + #dbg("exception no unread count") + #if self.unread_count != 0 and not self.muted: + # w.buffer_set(self.channel_buffer, "hotlist", "1") + def destroy_buffer(self, update_remote): + if self.channel_buffer is not None: + self.channel_buffer = None + self.got_history = False + #if update_remote and not eventrouter.shutting_down: + self.active = False -def command_setallreadmarkers(current_buffer, args): +class SlackUser(object): """ - Sets the read marker for all channels - /slack setallreadmarkers + Represends an individual slack user. Also where you set their name formatting. """ - for channel in channels: - channel.mark_read() - - -def command_changetoken(current_buffer, args): - w.config_set_plugin('slack_api_token', args) - - -def command_test(current_buffer, args): - w.prnt(current_buffer, "worked!") - - -def away_command_cb(data, current_buffer, args): - (all, message) = re.match("^/away(?:\s+(-all))?(?:\s+(.+))?", args).groups() - if all is None: - server = servers.find(current_domain_name()) - if not server: - return w.WEECHAT_RC_OK - if message is None: - server.set_active() + def __init__(self, **kwargs): + # We require these two things for a vaid object, + # the rest we can just learn from slack + self.identifier = kwargs["id"] + self.slack_name = kwargs["name"] + self.name = kwargs["name"] + for key, value in kwargs.items(): + setattr(self, key, value) + self.update_color() + def __repr__(self): + return "Name:{} Identifier:{}".format(self.name, self.identifier) + def force_color(self, color_name): + self.color_name = color_name + self.color = w.color(self.color_name) + def update_color(self): + if config.colorize_nicks: + self.color_name = w.info_get('irc_nick_color_name', self.name.encode('utf-8')) + self.color = w.color(self.color_name) else: - server.set_away(message) - return w.WEECHAT_RC_OK_EAT - for server in servers: - if message is None: - server.set_active() + self.color = "" + self.color_name = "" + def formatted_name(self, prepend="", enable_color=True): + if config.colorize_nicks and enable_color: + print_color = self.color else: - server.set_away(message) - return w.WEECHAT_RC_OK - + print_color = "" + return print_color + prepend + self.name -@slack_buffer_required -def command_away(current_buffer, args): +class SlackBot(SlackUser): """ - Sets your status as 'away' - /slack away + Basically the same as a user, but split out to identify and for future + needs """ - server = servers.find(current_domain_name()) - async_slack_api_request(server.domain, server.token, 'presence.set', {"presence": "away"}) - + def __init__(self, **kwargs): + super(SlackBot, self).__init__(**kwargs) -@slack_buffer_required -def command_back(current_buffer, args): +class SlackMessage(object): """ - Sets your status as 'back' - /slack back + Represents a single slack message and associated context/metadata. + These are modifiable and can be rerendered to change a message, + delete a message, add a reaction, add a thread. + Note: these can't be tied to a SlackUser object because users + can be deleted, so we have to store sender in each one. """ - server = servers.find(current_domain_name()) - async_slack_api_request(server.domain, server.token, 'presence.set', {"presence": "active"}) - - -@slack_buffer_required -def command_markread(current_buffer, args): - """ - Marks current channel as read - /slack markread - """ - # refactor this - one liner i think - channel = current_buffer_name(short=True) - domain = current_domain_name() - if servers.find(domain).channels.find(channel): - servers.find(domain).channels.find(channel).mark_read() - - -@slack_buffer_required -def command_slash(current_buffer, args): - """ - Support for custom slack commands - /slack slash /customcommand arg1 arg2 arg3 - """ - - server = servers.find(current_domain_name()) - channel = current_buffer_name(short=True) - domain = current_domain_name() - - if args is None: - server.buffer_prnt("Usage: /slack slash /someslashcommand [arguments...].") - return - - split_args = args.split(None, 1) - - command = split_args[0] - text = split_args[1] if len(split_args) > 1 else "" - - if servers.find(domain).channels.find(channel): - channel_identifier = servers.find(domain).channels.find(channel).identifier - - if channel_identifier: - async_slack_api_request(server.domain, server.token, 'chat.command', {'command': command, 'text': text, 'channel': channel_identifier}) - else: - server.buffer_prnt("User or channel not found.") - - -def command_flushcache(current_buffer, args): - global message_cache - message_cache = collections.defaultdict(list) - cache_write_cb("", "") - - -def command_cachenow(current_buffer, args): - cache_write_cb("", "") - - -def command_neveraway(current_buffer, args): - global never_away - if never_away: - never_away = False - dbg("unset never_away", main_buffer=True) - else: - never_away = True - dbg("set never_away", main_buffer=True) - - -def command_printvar(current_buffer, args): - w.prnt("", "{}".format(eval(args))) - - -def command_p(current_buffer, args): - w.prnt("", "{}".format(eval(args))) - - -def command_debug(current_buffer, args): - create_slack_debug_buffer() - - -def command_debugstring(current_buffer, args): - global debug_string - if args == '': - debug_string = None - else: - debug_string = args - - -def command_search(current_buffer, args): - pass -# if not slack_buffer: -# create_slack_buffer() -# w.buffer_set(slack_buffer, "display", "1") -# query = args -# w.prnt(slack_buffer,"\nSearched for: %s\n\n" % (query)) -# reply = slack_api_request('search.messages', {"query":query}).read() -# data = json.loads(reply) -# for message in data['messages']['matches']: -# message["text"] = message["text"].encode('ascii', 'ignore') -# formatted_message = "%s / %s:\t%s" % (message["channel"]["name"], message['username'], message['text']) -# w.prnt(slack_buffer,str(formatted_message)) - - -def command_nick(current_buffer, args): - pass -# urllib.urlopen("https://%s/account/settings" % (domain)) -# browser.select_form(nr=0) -# browser.form['username'] = args -# reply = browser.submit() - - -def command_help(current_buffer, args): - help_cmds = {k[8:]: v.__doc__ for k, v in globals().items() if k.startswith("command_")} - - if args: - try: - help_cmds = {args: help_cmds[args]} - except KeyError: - w.prnt("", "Command not found: " + args) - return - - for cmd, helptext in help_cmds.items(): - w.prnt('', w.color("bold") + cmd) - w.prnt('', (helptext or 'No help text').strip()) - w.prnt('', '') - -# Websocket handling methods - - -def command_openweb(current_buffer, args): - trigger = config.trigger_value - if trigger != "0": - if args is None: - channel = channels.find(current_buffer) - url = "{}/messages/{}".format(channel.server.server_buffer_name, channel.name) - topic = w.buffer_get_string(channel.channel_buffer, "title") - w.buffer_set(channel.channel_buffer, "title", "{}:{}".format(trigger, url)) - w.hook_timer(1000, 0, 1, "command_openweb", json.dumps({"topic": topic, "buffer": current_buffer})) + def __init__(self, message_json, team, channel, override_sender=None): + self.team = team + self.channel = channel + self.message_json = message_json + self.submessages = [] + self.thread_channel = None + self.hash = None + if override_sender: + self.sender = override_sender + self.sender_plain = override_sender else: - # TODO: fix this dirty hack because i don't know the right way to send multiple args. - args = current_buffer - data = json.loads(args) - channel_buffer = channels.find(data["buffer"]).channel_buffer - w.buffer_set(channel_buffer, "title", data["topic"]) - return w.WEECHAT_RC_OK - - -@slack_buffer_or_ignore -def topic_command_cb(data, current_buffer, args): - n = len(args.split()) - if n < 2: - channel = channels.find(current_buffer) - if channel: - w.prnt(current_buffer, 'Topic for {} is "{}"'.format(channel.name, channel.topic)) - return w.WEECHAT_RC_OK_EAT - elif command_topic(current_buffer, args.split(None, 1)[1]): - return w.WEECHAT_RC_OK_EAT - else: - return w.WEECHAT_RC_ERROR - - -def command_topic(current_buffer, args): - """ - Change the topic of a channel - /slack topic [] [|-delete] - """ - server = servers.find(current_domain_name()) - if server: - arrrrgs = args.split(None, 1) - if arrrrgs[0].startswith('#'): - channel = server.channels.find(arrrrgs[0]) - topic = arrrrgs[1] + senders = self.get_sender() + self.sender, self.sender_plain = senders[0], senders[1] + self.suffix = '' + self.ts = SlackTS(message_json['ts']) + def __hash__(self): + return hash(self.ts) + def render(self, force=False): + if len(self.submessages) > 0: + return "{} {} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix, "{}[ Thread: {} Replies: {} ]".format(w.color(config.thread_suffix_color), self.hash or self.ts, len(self.submessages))) + return "{} {}".format(render(self.message_json, self.team, self.channel, force), self.suffix) + def change_text(self, new_text): + self.message_json["text"] = new_text + dbg(self.message_json) + def change_suffix(self, new_suffix): + self.suffix = new_suffix + dbg(self.message_json) + def get_sender(self, utf8=True): + name = u"" + name_plain = u"" + if 'bot_id' in self.message_json and self.message_json['bot_id'] is not None: + name = u"{} :]".format(self.team.bots[self.message_json["bot_id"]].formatted_name()) + name_plain = u"{}".format(self.team.bots[self.message_json["bot_id"]].formatted_name(enable_color=False)) + elif 'user' in self.message_json: + if self.message_json['user'] == self.team.myidentifier: + name = self.team.users[self.team.myidentifier].name + name_plain = self.team.users[self.team.myidentifier].name + elif self.message_json['user'] in self.team.users: + u = self.team.users[self.message_json['user']] + if u.is_bot: + name = u"{} :]".format(u.formatted_name()) + else: + name = u"{}".format(u.formatted_name()) + name_plain = u"{}".format(u.formatted_name(enable_color=False)) + elif 'username' in self.message_json: + name = u"-{}-".format(self.message_json["username"]) + name_plain = u"{}".format(self.message_json["username"]) + elif 'service_name' in self.message_json: + name = u"-{}-".format(self.message_json["service_name"]) + name_plain = u"{}".format(self.message_json["service_name"]) else: - channel = server.channels.find(current_buffer) - topic = args - - if channel: - if topic == "-delete": - async_slack_api_request(server.domain, server.token, 'channels.setTopic', {"channel": channel.identifier, "topic": ""}) - else: - async_slack_api_request(server.domain, server.token, 'channels.setTopic', {"channel": channel.identifier, "topic": topic}) - return True + name = u"" + name_plain = u"" + if utf8: + return (name.encode('utf-8'), name_plain.encode('utf-8')) else: - return False - else: - return False - - -def slack_websocket_cb(server, fd): - try: - data = servers.find(server).ws.recv() - message_json = json.loads(data) - # this magic attaches json that helps find the right dest - message_json['_server'] = server - except WebSocketConnectionClosedException: - servers.find(server).ws.close() - return w.WEECHAT_RC_OK - except Exception: - dbg("socket issue: {}\n".format(traceback.format_exc())) - return w.WEECHAT_RC_OK - # dispatch here - if "reply_to" in message_json: - function_name = "reply" - elif "type" in message_json: - function_name = message_json["type"] - else: - function_name = "unknown" - try: - proc[function_name](message_json) - except KeyError: - if function_name: - dbg("Function not implemented: {}\n{}".format(function_name, message_json)) + return (name, name_plain) + def add_reaction(self, reaction, user): + m = self.message_json.get('reactions', None) + if m: + found = False + for r in m: + if r["name"] == reaction and user not in r["users"]: + r["users"].append(user) + found = True + if not found: + self.message_json["reactions"].append({u"name": reaction, u"users": [user]}) else: - dbg("Function not implemented\n{}".format(message_json)) - w.bar_item_update("slack_typing_notice") - return w.WEECHAT_RC_OK + self.message_json["reactions"] = [{u"name": reaction, u"users": [user]}] + def remove_reaction(self, reaction, user): + m = self.message_json.get('reactions', None) + if m: + for r in m: + if r["name"] == reaction and user in r["users"]: + r["users"].remove(user) + else: + pass +class SlackThreadMessage(SlackMessage): + def __init__(self, parent_id, *args): + super(SlackThreadMessage, self).__init__(*args) + #super(SlackBot, self).__init__(**kwargs) + self.parent_id = parent_id -def process_reply(message_json): - server = servers.find(message_json["_server"]) - identifier = message_json["reply_to"] - item = server.message_buffer.pop(identifier) - if 'text' in item and type(item['text']) is not unicode: - item['text'] = item['text'].decode('UTF-8', 'replace') - if "type" in item: - if item["type"] == "message" and "channel" in item.keys(): - item["ts"] = message_json["ts"] - channels.find(item["channel"]).cache_message(item, from_me=True) - text = unfurl_refs(item["text"], ignore_alt_text=config.unfurl_ignore_alt_text) - channels.find(item["channel"]).buffer_prnt(item["user"], text, item["ts"]) - dbg("REPLY {}".format(item)) +class WeeSlackMetadata(object): + """ + A simple container that we pickle/unpickle to hold data. + """ + def __init__(self, meta): + self.meta = meta + def jsonify(self): + return self.meta + +class SlackTS(object): + def __init__(self, ts=None): + if ts: + self.major, self.minor = [int(x) for x in ts.split('.', 1)] + else: + self.major = int(time.time()) + self.minor = 0 + def __cmp__(self, other): + if isinstance(other, SlackTS): + if self.major < other.major: + return -1 + elif self.major > other.major: + return 1 + elif self.major == other.major: + if self.minor < other.minor: + return -1 + elif self.minor > other.minor: + return 1 + else: + return 0 + else: + s = self.__str__() + if s < other: + return -1 + elif s > other: + return 1 + elif s == other: + return 0 + def __hash__(self): + return hash("{}.{}".format(self.major, self.minor)) + def __repr__(self): + return str("{0}.{1:06d}".format(self.major, self.minor)) + def split(self, *args, **kwargs): + return [self.major, self.minor] + def majorstr(self): + return str(self.major) + def minorstr(self): + return str(self.minor) +###### New handlers -def process_pong(message_json): - pass +def handle_rtmstart(login_data, eventrouter): + """ + This handles the main entry call to slack, rtm.start + """ + if login_data["ok"]: + metadata = pickle.loads(login_data["wee_slack_request_metadata"]) -def process_pref_change(message_json): - server = servers.find(message_json["_server"]) - if message_json['name'] == u'muted_channels': - muted = message_json['value'].split(',') - for c in server.channels: - if c.identifier in muted: - c.muted = True - else: - c.muted = False - else: - dbg("Preference change not implemented: {}\n".format(message_json['name'])) + #Let's reuse a team if we have it already. + th = SlackTeam.generate_team_hash(login_data['self']['name'], login_data['team']['domain']) + if not eventrouter.teams.get(th): + users = {} + for item in login_data["users"]: + users[item["id"]] = SlackUser(**item) + #users.append(SlackUser(**item)) -def process_team_join(message_json): - server = servers.find(message_json["_server"]) - item = message_json["user"] - server.add_user(User(server, item["name"], item["id"], item["presence"])) - server.buffer_prnt("New user joined: {}".format(item["name"])) + bots = {} + for item in login_data["bots"]: + bots[item["id"]] = SlackBot(**item) + channels = {} + for item in login_data["channels"]: + channels[item["id"]] = SlackChannel(eventrouter, **item) -def process_manual_presence_change(message_json): - process_presence_change(message_json) + for item in login_data["ims"]: + channels[item["id"]] = SlackDMChannel(eventrouter, users, **item) + for item in login_data["groups"]: + if item["name"].startswith('mpdm-'): + channels[item["id"]] = SlackMPDMChannel(eventrouter, **item) + else: + channels[item["id"]] = SlackGroupChannel(eventrouter, **item) + + t = SlackTeam( + eventrouter, + metadata.token, + login_data['url'], + login_data["team"]["domain"], + login_data["self"]["name"], + login_data["self"]["id"], + users, + bots, + channels, + muted_channels=login_data["self"]["prefs"]["muted_channels"], + highlight_words=login_data["self"]["prefs"]["highlight_words"], + ) + eventrouter.register_team(t) -def process_presence_change(message_json): - server = servers.find(message_json["_server"]) - identifier = message_json.get("user", server.nick) - if message_json["presence"] == 'active': - server.users.find(identifier).set_active() - else: - server.users.find(identifier).set_inactive() + else: + t = eventrouter.teams.get(th) + t.set_reconnect_url(login_data['url']) + t.connect() + + #web_socket_url = login_data['url'] + #try: + # ws = create_connection(web_socket_url, sslopt=sslopt_ca_certs) + # w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", t.get_team_hash()) + # #ws_hook = w.hook_fd(ws.sock._sock.fileno(), 1, 0, 0, "receive_ws_callback", pickle.dumps(t)) + # ws.sock.setblocking(0) + # t.attach_websocket(ws) + # t.set_connected() + #except Exception as e: + # dbg("websocket connection error: {}".format(e)) + # return False + + t.buffer_prnt('Connected to Slack') + t.buffer_prnt('{:<20} {}'.format(u"Websocket URL", login_data["url"])) + t.buffer_prnt('{:<20} {}'.format(u"User name", login_data["self"]["name"])) + t.buffer_prnt('{:<20} {}'.format(u"User ID", login_data["self"]["id"])) + t.buffer_prnt('{:<20} {}'.format(u"Team name", login_data["team"]["name"])) + t.buffer_prnt('{:<20} {}'.format(u"Team domain", login_data["team"]["domain"])) + t.buffer_prnt('{:<20} {}'.format(u"Team id", login_data["team"]["id"])) + + dbg("connected to {}".format(t.domain)) + + #self.identifier = self.domain + +def handle_groupshistory(message_json, eventrouter, **kwargs): + handle_history(message_json, eventrouter, **kwargs) + +def handle_channelshistory(message_json, eventrouter, **kwargs): + handle_history(message_json, eventrouter, **kwargs) + +def handle_imhistory(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] + kwargs['channel'] = kwargs['team'].channels[request_metadata.channel_identifier] + try: + clear = request_metadata.clear + except: + clear = False + dbg(clear) + kwargs['output_type'] = "backlog" + if clear: + w.buffer_clear(kwargs['channel'].channel_buffer) + for message in reversed(message_json["messages"]): + process_message(message, eventrouter, **kwargs) +###### New/converted process_ and subprocess_ methods -def process_channel_marked(message_json): - channel = channels.find(message_json["channel"]) - channel.mark_read(False) - w.buffer_set(channel.channel_buffer, "hotlist", "-1") +def process_reconnect_url(message_json, eventrouter, **kwargs): + kwargs['team'].set_reconnect_url(message_json['url']) +def process_manual_presence_change(message_json, eventrouter, **kwargs): + process_presence_change(message_json, eventrouter, **kwargs) -def process_group_marked(message_json): - channel = channels.find(message_json["channel"]) - channel.mark_read(False) - w.buffer_set(channel.channel_buffer, "hotlist", "-1") +def process_presence_change(message_json, eventrouter, **kwargs): + kwargs["user"].presence = message_json["presence"] -def process_channel_created(message_json): - server = servers.find(message_json["_server"]) - item = message_json["channel"] - if server.channels.find(message_json["channel"]["name"]): - server.channels.find(message_json["channel"]["name"]).open(False) +def process_pref_change(message_json, eventrouter, **kwargs): + team = kwargs["team"] + if message_json['name'] == u'muted_channels': + team.set_muted_channels(message_json['value']) + elif message_json['name'] == u'highlight_words': + team.set_highlight_words(message_json['value']) else: - item = message_json["channel"] - item["prepend_name"] = "#" - server.add_channel(Channel(server, **item)) - server.buffer_prnt("New channel created: {}".format(item["name"])) - - -def process_channel_left(message_json): - server = servers.find(message_json["_server"]) - server.channels.find(message_json["channel"]).close(False) + dbg("Preference change not implemented: {}\n".format(message_json['name'])) +def process_user_typing(message_json, eventrouter, **kwargs): + channel = kwargs["channel"] + team = kwargs["team"] + if channel: + channel.set_typing(team.users.get(message_json["user"]).name) + w.bar_item_update("slack_typing_notice") -def process_channel_join(message_json): - server = servers.find(message_json["_server"]) - channel = server.channels.find(message_json["channel"]) - text = unfurl_refs(message_json["text"], ignore_alt_text=False) - channel.buffer_prnt(w.prefix("join").rstrip(), text, message_json["ts"]) - channel.user_join(message_json["user"]) +def process_team_join(message_json, eventrouter, **kwargs): + user = message_json['user'] + team = kwargs["team"] + team.users[user["id"]] = SlackUser(**user) +def process_pong(message_json, eventrouter, **kwargs): + pass -def process_channel_topic(message_json): - server = servers.find(message_json["_server"]) - channel = server.channels.find(message_json["channel"]) - text = unfurl_refs(message_json["text"], ignore_alt_text=False) - channel.buffer_prnt(w.prefix("network").rstrip(), text, message_json["ts"]) - channel.set_topic(message_json["topic"]) +def process_message(message_json, eventrouter, store=True, **kwargs): + channel = kwargs["channel"] + team = kwargs["team"] + #try: + # send these subtype messages elsewhere + known_subtypes = [ + 'thread_message', + 'message_replied', + 'message_changed', + 'message_deleted', + 'channel_join', + 'channel_leave', + 'channel_topic', + #'group_join', + #'group_leave', + ] + if "thread_ts" in message_json and "reply_count" not in message_json: + message_json["subtype"] = "thread_message" + subtype = message_json.get("subtype", None) + if subtype and subtype in known_subtypes: + f = eval('subprocess_' + subtype) + f(message_json, eventrouter, channel, team) + else: + message = SlackMessage(message_json, team, channel) + text = message.render() + dbg(text) + + # special case with actions. + if text.startswith("_") and text.endswith("_"): + text = text[1:-1] + if message.sender != channel.team.nick: + text = message.sender + " " + text + try: + channel.unread_count_display += 1 + except: + channel.unread_count_display += 1 + channel.buffer_prnt(w.prefix("action").rstrip(), text, message.ts, tag_nick=message.sender_plain, **kwargs) -def process_channel_joined(message_json): - server = servers.find(message_json["_server"]) - if server.channels.find(message_json["channel"]["name"]): - server.channels.find(message_json["channel"]["name"]).open(False) + else: + suffix = '' + if 'edited' in message_json: + suffix = ' (edited)' + try: + channel.unread_count_display += 1 + except: + channel.unread_count_display = 1 + channel.buffer_prnt(message.sender, text + suffix, message.ts, tag_nick=message.sender_plain, **kwargs) + + if store: + channel.store_message(message, team) + dbg("NORMAL REPLY {}".format(message_json)) + #except: + # channel.buffer_prnt("WEE-SLACK-ERROR", json.dumps(message_json).encode('utf-8'), message_json["ts"], **kwargs) + # traceback.print_exc() + +def subprocess_thread_message(message_json, eventrouter, channel, team): + #print ("THREADED: " + str(message_json)) + parent_ts = message_json.get('thread_ts', None) + if parent_ts: + #parent_ts = SlackTS(parent_ts) + parent_ts = parent_ts + parent_message = channel.messages.get(SlackTS(parent_ts), None) + if parent_message: + message = SlackThreadMessage(parent_ts, message_json, team, channel) + parent_message.submessages.append(message) + channel.hash_message(parent_ts) + channel.store_message(message, team) + channel.change_message(parent_ts) + + text = message.render() + #channel.buffer_prnt(message.sender, text, message.ts, **kwargs) + if parent_message.thread_channel: + parent_message.thread_channel.buffer_prnt(message.sender, text, message.ts) + +# channel = channels.find(message_json["channel"]) +# server = channel.server +# #threadinfo = channel.get_message(message_json["thread_ts"]) +# message = Message(message_json, server=server, channel=channel) +# dbg(message, main_buffer=True) +# +# orig = channel.get_message(message_json['thread_ts']) +# if orig[0]: +# channel.get_message(message_json['thread_ts'])[2].add_thread_message(message) +# else: +# dbg("COULDN'T find orig message {}".format(message_json['thread_ts']), main_buffer=True) + + #if threadinfo[0]: + # channel.messages[threadinfo[1]].become_thread() + # message_json["item"]["ts"], message_json) + #channel.change_message(message_json["thread_ts"], None, message_json["text"]) + #channel.become_thread(message_json["item"]["ts"], message_json) + +def subprocess_channel_join(message_json, eventrouter, channel, team): + joinprefix = w.prefix("join") + message = SlackMessage(message_json, team, channel, override_sender=joinprefix) + channel.buffer_prnt(joinprefix, message.render(), message_json["ts"], tagset='joinleave') + channel.user_joined(message_json['user']) + +def subprocess_channel_leave(message_json, eventrouter, channel, team): + leaveprefix = w.prefix("quit") + message = SlackMessage(message_json, team, channel, override_sender=leaveprefix) + channel.buffer_prnt(leaveprefix, message.render(), message_json["ts"], tagset='joinleave') + channel.user_left(message_json['user']) + #channel.update_nicklist(message_json['user']) + #channel.update_nicklist() + +def subprocess_message_replied(message_json, eventrouter, channel, team): + pass + #print ("REPLIED: " + str(message_json)) + +def subprocess_message_changed(message_json, eventrouter, channel, team): + m = message_json.get("message", None) + if m: + new_message = m + #message = SlackMessage(new_message, team, channel) + #if "attachments" in m: + # message_json["attachments"] = m["attachments"] + #if "text" in m: + # if "text" in message_json: + # message_json["text"] += m["text"] + # dbg("added text!") + # else: + # message_json["text"] = m["text"] + #if "fallback" in m: + # if "fallback" in message_json: + # message_json["fallback"] += m["fallback"] + # else: + # message_json["fallback"] = m["fallback"] + + text_before = (len(new_message['text']) > 0) + new_message["text"] += unwrap_attachments(message_json, text_before) + if "edited" in new_message: + channel.change_message(new_message["ts"], new_message["text"], ' (edited)') else: - item = message_json["channel"] - item["prepend_name"] = "#" - server.add_channel(Channel(server, **item)) + channel.change_message(new_message["ts"], new_message["text"]) +def subprocess_message_deleted(message_json, eventrouter, channel, team): + channel.change_message(message_json["deleted_ts"], "(deleted)", '') -def process_channel_leave(message_json): - server = servers.find(message_json["_server"]) - channel = server.channels.find(message_json["channel"]) +def subprocess_channel_topic(message_json, eventrouter, channel, team): text = unfurl_refs(message_json["text"], ignore_alt_text=False) - channel.buffer_prnt(w.prefix("quit").rstrip(), text, message_json["ts"]) - channel.user_leave(message_json["user"]) - - -def process_channel_archive(message_json): - server = servers.find(message_json["_server"]) - channel = server.channels.find(message_json["channel"]) - channel.detach_buffer() - - -def process_group_join(message_json): - process_channel_join(message_json) - - -def process_group_leave(message_json): - process_channel_leave(message_json) - - -def process_group_topic(message_json): - process_channel_topic(message_json) - - -def process_group_left(message_json): - server = servers.find(message_json["_server"]) - server.channels.find(message_json["channel"]).close(False) - - -def process_group_joined(message_json): - server = servers.find(message_json["_server"]) - if server.channels.find(message_json["channel"]["name"]): - server.channels.find(message_json["channel"]["name"]).open(False) - else: - item = message_json["channel"] - item["prepend_name"] = "#" - if item["name"].startswith("mpdm-"): - server.add_channel(MpdmChannel(server, **item)) + if type(text) != unicode: + text = text.decode('utf-8', 'ignore') + text = text.encode('utf-8') + channel.buffer_prnt(w.prefix("network").rstrip(), text, message_json["ts"], tagset="muted") + channel.render_topic(message_json["topic"]) + +def process_reply(message_json, eventrouter, **kwargs): + dbg('processing reply') + team = kwargs["team"] + identifier = message_json["reply_to"] + try: + original_message_json = team.ws_replies[identifier] + del team.ws_replies[identifier] + if "ts" in message_json: + original_message_json["ts"] = message_json["ts"] else: - server.add_channel(GroupChannel(server, **item)) - -def process_group_archive(message_json): - channel = server.channels.find(message_json["channel"]) - channel.detach_buffer() - + dbg("no reply ts {}".format(message_json)) -def process_mpim_close(message_json): - server = servers.find(message_json["_server"]) - server.channels.find(message_json["channel"]).close(False) + c = original_message_json.get('channel', None) + channel = team.channels[c] + m = SlackMessage(original_message_json, team, channel) + # m = Message(message_json, server=server) + #dbg(m, True) + #if "type" in message_json: + # if message_json["type"] == "message" and "channel" in message_json.keys(): + # message_json["ts"] = message_json["ts"] + # channels.find(message_json["channel"]).store_message(m, from_me=True) -def process_mpim_open(message_json): - server = servers.find(message_json["_server"]) - server.channels.find(message_json["channel"]).open(False) + # channels.find(message_json["channel"]).buffer_prnt(server.nick, m.render(), m.ts) + process_message(m.message_json, eventrouter, channel=channel, team=team) + channel.mark_read(update_remote=True, force=True) + dbg("REPLY {}".format(message_json)) + except KeyError: + dbg("Unexpected reply {}".format(message_json)) -def process_im_close(message_json): - server = servers.find(message_json["_server"]) - server.channels.find(message_json["channel"]).close(False) - - -def process_im_open(message_json): - server = servers.find(message_json["_server"]) - server.channels.find(message_json["channel"]).open() - +def process_channel_marked(message_json, eventrouter, **kwargs): + """ + complete + """ + channel = kwargs["channel"] + ts = message_json.get("ts", None) + if ts: + channel.mark_read(ts=ts, force=True, update_remote=False) + else: + dbg("tried to mark something weird {}".format(message_json)) +def process_group_marked(message_json, eventrouter, **kwargs): + process_channel_marked(message_json, eventrouter, **kwargs) +def process_im_marked(message_json, eventrouter, **kwargs): + process_channel_marked(message_json, eventrouter, **kwargs) +def process_mpim_marked(message_json, eventrouter, **kwargs): + process_channel_marked(message_json, eventrouter, **kwargs) + +def process_channel_joined(message_json, eventrouter, **kwargs): + item = message_json["channel"] + kwargs['team'].channels[item["id"]].update_from_message_json(item) + kwargs['team'].channels[item["id"]].open() -def process_im_marked(message_json): - channel = channels.find(message_json["channel"]) - channel.mark_read(False) - if channel.channel_buffer is not None: - w.buffer_set(channel.channel_buffer, "hotlist", "-1") +def process_channel_created(message_json, eventrouter, **kwargs): + item = message_json["channel"] + c = SlackChannel(eventrouter, team=kwargs["team"], **item) + kwargs['team'].channels[item["id"]] = c + kwargs['team'].buffer_prnt('Channel created: {}'.format(c.slack_name)) +def process_channel_rename(message_json, eventrouter, **kwargs): + item = message_json["channel"] + channel = kwargs['team'].channels[item["id"]] + channel.slack_name = message_json['channel']['name'] -def process_im_created(message_json): - server = servers.find(message_json["_server"]) +def process_im_created(message_json, eventrouter, **kwargs): + team = kwargs['team'] item = message_json["channel"] - channel_name = server.users.find(item["user"]).name - if server.channels.find(channel_name): - server.channels.find(channel_name).open(False) + c = SlackDMChannel(eventrouter, team=team, users=team.users, **item) + team.channels[item["id"]] = c + kwargs['team'].buffer_prnt('IM channel created: {}'.format(c.name)) + +def process_im_open(message_json, eventrouter, **kwargs): + channel = kwargs['channel'] + item = message_json + kwargs['team'].channels[item["channel"]].check_should_open(True) + w.buffer_set(channel.channel_buffer, "hotlist", "2") + +def process_im_close(message_json, eventrouter, **kwargs): + item = message_json + cbuf = kwargs['team'].channels[item["channel"]].channel_buffer + eventrouter.weechat_controller.unregister_buffer(cbuf, False, True) + +def process_group_joined(message_json, eventrouter, **kwargs): + item = message_json["channel"] + if item["name"].startswith("mpdm-"): + c = SlackMPDMChannel(eventrouter, team=kwargs["team"], **item) else: - item = message_json["channel"] - item['name'] = server.users.find(item["user"]).name - server.add_channel(DmChannel(server, **item)) - server.buffer_prnt("New direct message channel created: {}".format(item["name"])) - + c = SlackGroupChannel(eventrouter, team=kwargs["team"], **item) + kwargs['team'].channels[item["id"]] = c + kwargs['team'].channels[item["id"]].open() -def process_user_typing(message_json): - server = servers.find(message_json["_server"]) - channel = server.channels.find(message_json["channel"]) - if channel: - channel.set_typing(server.users.find(message_json["user"]).name) - - -def process_bot_enable(message_json): - process_bot_integration(message_json) - - -def process_bot_disable(message_json): - process_bot_integration(message_json) - - -def process_bot_integration(message_json): - server = servers.find(message_json["_server"]) - channel = server.channels.find(message_json["channel"]) - - time = message_json['ts'] - text = "{} {}".format(server.users.find(message_json['user']).formatted_name(), - render_message(message_json)) - bot_name = get_user(message_json, server) - bot_name = bot_name.encode('utf-8') - channel.buffer_prnt(bot_name, text, time) - -# todo: does this work? - - -def process_error(message_json): - pass - - -def process_reaction_added(message_json): +def process_reaction_added(message_json, eventrouter, **kwargs): + channel = kwargs['team'].channels[message_json["item"]["channel"]] if message_json["item"].get("type") == "message": - channel = channels.find(message_json["item"]["channel"]) - channel.add_reaction(message_json["item"]["ts"], message_json["reaction"], message_json["user"]) - else: - dbg("Reaction to item type not supported: " + str(message_json)) + ts = SlackTS(message_json['item']["ts"]) - -def process_reaction_removed(message_json): - if message_json["item"].get("type") == "message": - channel = channels.find(message_json["item"]["channel"]) - channel.remove_reaction(message_json["item"]["ts"], message_json["reaction"], message_json["user"]) + message = channel.messages.get(ts, None) + if message: + message.add_reaction(message_json["reaction"], message_json["user"]) + channel.change_message(ts) else: - dbg("Reaction to item type not supported: " + str(message_json)) + dbg("reaction to item type not supported: " + str(message_json)) +def process_reaction_removed(message_json, eventrouter, **kwargs): + channel = kwargs['team'].channels[message_json["item"]["channel"]] + if message_json["item"].get("type") == "message": + ts = SlackTS(message_json['item']["ts"]) -def create_reaction_string(reactions): - count = 0 - if not isinstance(reactions, list): - reaction_string = " [{}]".format(reactions) + message = channel.messages.get(ts, None) + if message: + message.remove_reaction(message_json["reaction"], message_json["user"]) + channel.change_message(ts) else: - reaction_string = ' [' - for r in reactions: - if len(r["users"]) > 0: - count += 1 - if config.show_reaction_nicks: - nicks = [resolve_ref("@{}".format(user)) for user in r["users"]] - users = "({})".format(",".join(nicks)) - else: - users = len(r["users"]) - reaction_string += ":{}:{} ".format(r["name"], users) - reaction_string = reaction_string[:-1] + ']' - if count == 0: - reaction_string = '' - return reaction_string - - -def modify_buffer_line(buffer, new_line, time): - time = int(float(time)) - # get a pointer to this buffer's lines - own_lines = w.hdata_pointer(w.hdata_get('buffer'), buffer, 'own_lines') - if own_lines: - # get a pointer to the last line - line_pointer = w.hdata_pointer(w.hdata_get('lines'), own_lines, 'last_line') - # hold the structure of a line and of line data - struct_hdata_line = w.hdata_get('line') - struct_hdata_line_data = w.hdata_get('line_data') - - while line_pointer: - # get a pointer to the data in line_pointer via layout of struct_hdata_line - data = w.hdata_pointer(struct_hdata_line, line_pointer, 'data') - if data: - date = w.hdata_time(struct_hdata_line_data, data, 'date') - # prefix = w.hdata_string(struct_hdata_line_data, data, 'prefix') - - if int(date) == int(time): - # w.prnt("", "found matching time date is {}, time is {} ".format(date, time)) - w.hdata_update(struct_hdata_line_data, data, {"message": new_line}) - break - else: - pass - # move backwards one line and try again - exit the while if you hit the end - line_pointer = w.hdata_move(struct_hdata_line, line_pointer, -1) - return w.WEECHAT_RC_OK + dbg("Reaction to item type not supported: " + str(message_json)) +###### New module/global methods -def render_message(message_json, force=False): +def render(message_json, team, channel, force=False): # If we already have a rendered version in the object, just return that. if not force and message_json.get("_rendered_text", ""): return message_json["_rendered_text"] @@ -1887,96 +2225,91 @@ def render_message(message_json, force=False): text = text.replace("<", "<") text = text.replace(">", ">") text = text.replace("&", "&") + + if type(text) is not unicode: + text = text.decode('UTF-8', 'replace') text = text.encode('utf-8') - if "reactions" in message_json: - text += create_reaction_string(message_json["reactions"]) - message_json["_rendered_text"] = text - return text +# if self.threads: +# text += " [Replies: {} Thread ID: {} ] ".format(len(self.threads), self.thread_id) +# #for thread in self.threads: + text += create_reaction_string(message_json.get("reactions", "")) -def process_message(message_json, cache=True): - try: - # send these subtype messages elsewhere - known_subtypes = ["message_changed", 'message_deleted', 'channel_join', 'channel_leave', 'channel_topic', 'group_join', 'group_leave', 'group_topic', 'bot_enable', 'bot_disable'] - if "subtype" in message_json and message_json["subtype"] in known_subtypes: - proc[message_json["subtype"]](message_json) + message_json["_rendered_text"] = text - else: - server = servers.find(message_json["_server"]) - channel = channels.find(message_json["channel"]) + return text - # do not process messages in unexpected channels - if not channel.active: - channel.open(False) - dbg("message came for closed channel {}".format(channel.name)) - return +def linkify_text(message, team, channel): + # The get_username_map function is a bit heavy, but this whole + # function is only called on message send.. + usernames = team.get_username_map() + channels = team.get_channel_map() + message = message.split(' ') + for item in enumerate(message): + targets = re.match('^\s*([@#])([\w.-]+[\w. -])(\W*)', item[1]) + #print targets + if targets and targets.groups()[0] == '@': + #print targets.groups() + named = targets.groups() + if named[1] in ["group", "channel", "here"]: + message[item[0]] = "".format(named[1]) + else: + try: + if usernames[named[1]]: + message[item[0]] = "<@{}>{}".format(usernames[named[1]], named[2]) + except: + message[item[0]] = "@{}{}".format(named[1], named[2]) + if targets and targets.groups()[0] == '#': + named = targets.groups() + try: + if channels[named[1]]: + message[item[0]] = "<#{}|{}>{}".format(channels[named[1]], named[1], named[2]) + except: + message[item[0]] = "#{}{}".format(named[1], named[2]) - time = message_json['ts'] - text = render_message(message_json) - name = get_user(message_json, server) - name = name.encode('utf-8') + #dbg(message) + return " ".join(message) - # special case with actions. - if text.startswith("_") and text.endswith("_"): - text = text[1:-1] - if name != channel.server.nick: - text = name + " " + text - channel.buffer_prnt(w.prefix("action").rstrip(), text, time) +def unfurl_refs(text, ignore_alt_text=False): + """ + input : <@U096Q7CQM|someuser> has joined the channel + ouput : someuser has joined the channel + """ + # Find all strings enclosed by <> + # - + # - <#C2147483705|#otherchannel> + # - <@U2147483697|@othernick> + # Test patterns lives in ./_pytest/test_unfurl.py + matches = re.findall(r"(<[@#]?(?:[^<]*)>)", text) + for m in matches: + # Replace them with human readable strings + text = text.replace(m, unfurl_ref(m[1:-1], ignore_alt_text)) + return text +def unfurl_ref(ref, ignore_alt_text=False): + id = ref.split('|')[0] + display_text = ref + if ref.find('|') > -1: + if ignore_alt_text: + display_text = resolve_ref(id) + else: + if id.startswith("#C") or id.startswith("@U"): + display_text = ref.split('|')[1] else: - suffix = '' - if 'edited' in message_json: - suffix = ' (edited)' - channel.buffer_prnt(name, text + suffix, time) - - if cache: - channel.cache_message(message_json) - - except Exception: - channel = channels.find(message_json["channel"]) - dbg("cannot process message {}\n{}".format(message_json, traceback.format_exc())) - if channel and ("text" in message_json) and message_json['text'] is not None: - channel.buffer_prnt('unknown', message_json['text']) - - -def process_message_changed(message_json): - m = message_json["message"] - if "message" in message_json: - if "attachments" in m: - message_json["attachments"] = m["attachments"] - if "text" in m: - if "text" in message_json: - message_json["text"] += m["text"] - dbg("added text!") - else: - message_json["text"] = m["text"] - if "fallback" in m: - if "fallback" in message_json: - message_json["fallback"] += m["fallback"] - else: - message_json["fallback"] = m["fallback"] - - text_before = (len(m['text']) > 0) - m["text"] += unwrap_attachments(message_json, text_before) - channel = channels.find(message_json["channel"]) - if "edited" in m: - channel.change_message(m["ts"], m["text"], ' (edited)') + url, desc = ref.split('|', 1) + display_text = u"{} ({})".format(url, desc) else: - channel.change_message(m["ts"], m["text"]) - - -def process_message_deleted(message_json): - channel = channels.find(message_json["channel"]) - channel.change_message(message_json["deleted_ts"], "(deleted)") - + display_text = resolve_ref(ref) + return display_text def unwrap_attachments(message_json, text_before): attachment_text = '' - if "attachments" in message_json: + a = message_json.get("attachments", None) + if a: if text_before: attachment_text = u'\n' - for attachment in message_json["attachments"]: + for attachment in a: # Attachments should be rendered roughly like: # # $pretext @@ -1989,423 +2322,474 @@ def unwrap_attachments(message_json, text_before): prepend_title_text = attachment['author_name'] + ": " if 'pretext' in attachment: t.append(attachment['pretext']) - if "title" in attachment: - if 'title_link' in attachment: - t.append('%s%s (%s)' % (prepend_title_text, attachment["title"], attachment["title_link"],)) - else: - t.append(prepend_title_text + attachment["title"]) + title = attachment.get('title', None) + title_link = attachment.get('title_link', None) + if title and title_link: + t.append('%s%s (%s)' % (prepend_title_text, title, title_link,)) + prepend_title_text = '' + elif title and not title_link: + t.append(prepend_title_text + title) prepend_title_text = '' - elif "from_url" in attachment: - t.append(attachment["from_url"]) - if "text" in attachment: - tx = re.sub(r' *\n[\n ]+', '\n', attachment["text"]) + t.append(attachment.get("from_url", "")) + + atext = attachment.get("text", None) + if atext: + tx = re.sub(r' *\n[\n ]+', '\n', atext) t.append(prepend_title_text + tx) prepend_title_text = '' - if 'fields' in attachment: - for f in attachment['fields']: + fields = attachment.get("fields", None) + if fields: + for f in fields: if f['title'] != '': t.append('%s %s' % (f['title'], f['value'],)) else: t.append(f['value']) - if t == [] and "fallback" in attachment: - t.append(attachment["fallback"]) + fallback = attachment.get("fallback", None) + if t == [] and fallback: + t.append(fallback) attachment_text += "\n".join([x.strip() for x in t if x]) return attachment_text def resolve_ref(ref): - if ref.startswith('@U') or ref.startswith('@W'): - if users.find(ref[1:]): - try: - return "@{}".format(users.find(ref[1:]).name) - except: - dbg("NAME: {}".format(ref)) - elif ref.startswith('#C'): - if channels.find(ref[1:]): - try: - return "{}".format(channels.find(ref[1:]).name) - except: - dbg("CHANNEL: {}".format(ref)) - - # Something else, just return as-is + #TODO: This hack to use eventrouter needs to go + #this resolver should probably move to the slackteam or eventrouter itself + #global EVENTROUTER + if 'EVENTROUTER' in globals(): + 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)) + elif ref.startswith('#C'): + for t in e.teams.keys(): + if ref[1:] in e.teams[t].channels: + #try: + return "{}".format(e.teams[t].channels[ref[1:]].name) + #except: + # dbg("CHANNEL: {}".format(ref)) + + # Something else, just return as-is return ref +def create_reaction_string(reactions): + count = 0 + if not isinstance(reactions, list): + reaction_string = " [{}]".format(reactions) + else: + reaction_string = ' [' + for r in reactions: + if len(r["users"]) > 0: + count += 1 + if config.show_reaction_nicks: + nicks = [resolve_ref("@{}".format(user)) for user in r["users"]] + users = "({})".format(",".join(nicks)) + else: + users = len(r["users"]) + reaction_string += ":{}:{} ".format(r["name"], users) + reaction_string = reaction_string[:-1] + ']' + if count == 0: + reaction_string = '' + return reaction_string -def unfurl_ref(ref, ignore_alt_text=False): - id = ref.split('|')[0] - display_text = ref - if ref.find('|') > -1: - if ignore_alt_text: - display_text = resolve_ref(id) - else: - if id.startswith("#C") or id.startswith("@U"): - display_text = ref.split('|')[1] - else: - url, desc = ref.split('|', 1) - display_text = u"{} ({})".format(url, desc) - else: - display_text = resolve_ref(ref) - return display_text - - -def unfurl_refs(text, ignore_alt_text=False): - """ - input : <@U096Q7CQM|someuser> has joined the channel - ouput : someuser has joined the channel - """ - # Find all strings enclosed by <> - # - - # - <#C2147483705|#otherchannel> - # - <@U2147483697|@othernick> - # Test patterns lives in ./_pytest/test_unfurl.py - matches = re.findall(r"(<[@#]?(?:[^<]*)>)", text) - for m in matches: - # Replace them with human readable strings - text = text.replace(m, unfurl_ref(m[1:-1], ignore_alt_text)) - return text - - -def get_user(message_json, server): - if 'bot_id' in message_json and message_json['bot_id'] is not None: - name = u"{} :]".format(server.bots.find(message_json["bot_id"]).formatted_name()) - elif 'user' in message_json: - u = server.users.find(message_json['user']) - if u.is_bot: - name = u"{} :]".format(u.formatted_name()) - else: - name = u.name - elif 'username' in message_json: - name = u"-{}-".format(message_json["username"]) - elif 'service_name' in message_json: - name = u"-{}-".format(message_json["service_name"]) - else: - name = u"" - return name - -# END Websocket handling methods - - -def typing_bar_item_cb(data, buffer, args): - typers = [x for x in channels if x.is_someone_typing()] - if len(typers) > 0: - direct_typers = [] - channel_typers = [] - for dm in channels.find_by_class(DmChannel): - direct_typers.extend(dm.get_typing_list()) - direct_typers = ["D/" + x for x in direct_typers] - current_channel = w.current_buffer() - channel = channels.find(current_channel) - try: - if channel and channel.__class__ != DmChannel: - channel_typers = channels.find(current_channel).get_typing_list() - except: - w.prnt("", "Bug on {}".format(channel)) - typing_here = ", ".join(channel_typers + direct_typers) - if len(typing_here) > 0: - color = w.color('yellow') - return color + "typing: " + typing_here - return "" - - -def typing_update_cb(data, remaining_calls): - w.bar_item_update("slack_typing_notice") - return w.WEECHAT_RC_OK - - -def buffer_list_update_cb(data, remaining_calls): - global buffer_list_update - - now = time.time() - if buffer_list_update and previous_buffer_list_update + 1 < now: - # gray_check = False - # if len(servers) > 1: - # gray_check = True - for channel in channels: - channel.rename() - buffer_list_update = False - return w.WEECHAT_RC_OK - - -def buffer_list_update_next(): - global buffer_list_update - buffer_list_update = True +def modify_buffer_line(buffer, new_line, timestamp, time_id): + # get a pointer to this buffer's lines + own_lines = w.hdata_pointer(w.hdata_get('buffer'), buffer, 'own_lines') + if own_lines: + # get a pointer to the last line + line_pointer = w.hdata_pointer(w.hdata_get('lines'), own_lines, 'last_line') + # hold the structure of a line and of line data + struct_hdata_line = w.hdata_get('line') + struct_hdata_line_data = w.hdata_get('line_data') + while line_pointer: + # get a pointer to the data in line_pointer via layout of struct_hdata_line + data = w.hdata_pointer(struct_hdata_line, line_pointer, 'data') + if data: + line_timestamp = w.hdata_time(struct_hdata_line_data, data, 'date') + line_time_id = w.hdata_integer(struct_hdata_line_data, data, 'date_printed') + # prefix = w.hdata_string(struct_hdata_line_data, data, 'prefix') -def hotlist_cache_update_cb(data, remaining_calls): - # this keeps the hotlist dupe up to date for the buffer switch, but is prob technically a race condition. (meh) - global hotlist - prev_hotlist = hotlist - hotlist = w.infolist_get("hotlist", "", "") - w.infolist_free(prev_hotlist) + if timestamp == int(line_timestamp) and int(time_id) == line_time_id: + # w.prnt("", "found matching time date is {}, time is {} ".format(timestamp, line_timestamp)) + w.hdata_update(struct_hdata_line_data, data, {"message": new_line}) + break + else: + pass + # move backwards one line and try again - exit the while if you hit the end + line_pointer = w.hdata_move(struct_hdata_line, line_pointer, -1) return w.WEECHAT_RC_OK -def buffer_closing_cb(signal, sig_type, data): - if channels.find(data): - channels.find(data).closed() - return w.WEECHAT_RC_OK +def modify_print_time(buffer, new_id, time): + """ + This overloads the time printed field to let us store the slack + per message unique id that comes after the "." in a slack ts + """ + # get a pointer to this buffer's lines + own_lines = w.hdata_pointer(w.hdata_get('buffer'), buffer, 'own_lines') + if own_lines: + # get a pointer to the last line + line_pointer = w.hdata_pointer(w.hdata_get('lines'), own_lines, 'last_line') + # hold the structure of a line and of line data + struct_hdata_line = w.hdata_get('line') + struct_hdata_line_data = w.hdata_get('line_data') + # get a pointer to the data in line_pointer via layout of struct_hdata_line + data = w.hdata_pointer(struct_hdata_line, line_pointer, 'data') + if data: + w.hdata_update(struct_hdata_line_data, data, {"date_printed": new_id}) -def buffer_opened_cb(signal, sig_type, data): - channels.update_hashtable() return w.WEECHAT_RC_OK +def tag(tagset, user=None): + if user: + user.replace(" ", "_") + default_tag = "nick_" + user + else: + default_tag = 'nick_unknown' + tagsets = { + #when replaying something old + "backlog": "no_highlight,notify_none,logger_backlog_end", + #when posting messages to a muted channel + "muted": "no_highlight,notify_none,logger_backlog_end", + #when my nick is in the message + "highlightme": "notify_highlight,log1", + #when receiving a direct message + "dm": "notify_private,notify_message,log1,irc_privmsg", + "dmfromme": "notify_none,log1,irc_privmsg", + #when this is a join/leave, attach for smart filter ala: + #if user in [x.strip() for x in w.prefix("join"), w.prefix("quit")] + "joinleave": "irc_smart_filter,no_highlight", + #catchall ? + "default": "notify_message,log1", + } + return default_tag + "," + tagsets[tagset] -def buffer_switch_cb(signal, sig_type, data): - global previous_buffer, hotlist - # this is to see if we need to gray out things in the buffer list - if channels.find(previous_buffer): - channels.find(previous_buffer).mark_read() - - new_channel = channels.find(data) - if new_channel: - if new_channel.got_history == False: - new_channel.get_history() - # channel_name = current_buffer_name() - previous_buffer = data - return w.WEECHAT_RC_OK +###### New/converted command_ commands -def typing_notification_cb(signal, sig_type, data): - msg = w.buffer_get_string(data, "input") - if len(msg) > 8 and msg[:1] != "/": - global typing_timer - now = time.time() - if typing_timer + 4 < now: - channel = channels.find(current_buffer_name()) - if channel: - identifier = channel.identifier - request = {"type": "typing", "channel": identifier} - channel.server.send_to_websocket(request, expect_reply=False) - typing_timer = now - return w.WEECHAT_RC_OK +@slack_buffer_or_ignore +def part_command_cb(data, current_buffer, args): + e = EVENTROUTER + args = args.split() + if len(args) > 1: + team = e.weechat_controller.buffers[current_buffer].team + cmap = team.get_channel_map() + channel = "".join(args[1:]) + if channel in cmap: + buffer_ptr = team.channels[cmap[channel]].channel_buffer + e.weechat_controller.unregister_buffer(buffer_ptr, update_remote=True, close_buffer=True) + else: + e.weechat_controller.unregister_buffer(current_buffer, update_remote=True, close_buffer=True) + return w.WEECHAT_RC_OK_EAT +@slack_buffer_or_ignore +def topic_command_cb(data, current_buffer, args): + n = len(args.split()) + if n < 2: + channel = channels.find(current_buffer) + if channel: + w.prnt(current_buffer, 'Topic for {} is "{}"'.format(channel.name, channel.topic)) + return w.WEECHAT_RC_OK_EAT + elif command_topic(data, current_buffer, args.split(None, 1)[1]): + return w.WEECHAT_RC_OK_EAT + else: + return w.WEECHAT_RC_ERROR -def slack_ping_cb(data, remaining): +@slack_buffer_required +def command_topic(data, current_buffer, args): """ - Periodic websocket ping to detect broken connection. + Change the topic of a channel + /slack topic [] [|-delete] """ - servers.find(data).ping() - return w.WEECHAT_RC_OK - + e = EVENTROUTER + team = e.weechat_controller.buffers[current_buffer].team + #server = servers.find(current_domain_name()) + args = args.split(' ') + if len(args) > 2 and args[1].startswith('#'): + cmap = team.get_channel_map() + channel_name = args[1][1:] + channel = team.channels[cmap[channel_name]] + topic = " ".join(args[2:]) + else: + channel = e.weechat_controller.buffers[current_buffer] + topic = " ".join(args[1:]) -def slack_connection_persistence_cb(data, remaining_calls): - """ - Reconnect if a connection is detected down - """ - for server in servers: - if not server.connected: - server.buffer_prnt("Disconnected from slack, trying to reconnect..") - if server.ws_hook is not None: - w.unhook(server.ws_hook) - server.connect_to_slack() - return w.WEECHAT_RC_OK + if channel: + if topic == "-delete": + topic = '' + s = SlackRequest(team.token, "channels.setTopic", {"channel": channel.identifier, "topic": topic}, team_hash=team.team_hash) + EVENTROUTER.receive(s) + return w.WEECHAT_RC_OK_EAT + else: + return w.WEECHAT_RC_ERROR_EAT +@slack_buffer_or_ignore +def me_command_cb(data, current_buffer, args): + message = "_{}_".format(args.split(' ', 1)[1]) + buffer_input_callback("EVENTROUTER", current_buffer, message) + return w.WEECHAT_RC_OK_EAT -def slack_never_away_cb(data, remaining): - global never_away - if never_away: - for server in servers: - identifier = server.channels.find("slackbot").identifier - request = {"type": "typing", "channel": identifier} - # request = {"type":"typing","channel":"slackbot"} - server.send_to_websocket(request, expect_reply=False) - return w.WEECHAT_RC_OK +@slack_buffer_or_ignore +def msg_command_cb(data, current_buffer, args): + dbg("msg_command_cb") + aargs = args.split(None, 2) + who = aargs[1] + command_talk(data, current_buffer, who) + if len(aargs) > 2: + message = aargs[2] + team = EVENTROUTER.weechat_controller.buffers[current_buffer].team + cmap = team.get_channel_map() + if who in cmap: + channel = team.channels[cmap[channel]] + channel.send_message(message) + return w.WEECHAT_RC_OK_EAT -def nick_completion_cb(data, completion_item, buffer, completion): +@slack_buffer_or_ignore +def command_talk(data, current_buffer, args): """ - Adds all @-prefixed nicks to completion list + Open a chat with the specified user + /slack talk [user] """ + e = EVENTROUTER + team = e.weechat_controller.buffers[current_buffer].team + channel_name = args.split(' ')[1] + c = team.get_channel_map() + if channel_name not in c: + u = team.get_username_map() + if channel_name in u: + s = SlackRequest(team.token, "im.open", {"user": u[channel_name]}, team_hash=team.team_hash) + EVENTROUTER.receive(s) + dbg("found user") + #refresh channel map here + c = team.get_channel_map() + + if channel_name.startswith('#'): + channel_name = arg[1:] + if channel_name in c: + chan = team.channels[c[channel_name]] + chan.open() + if config.switch_buffer_on_join: + w.buffer_set(chan.channel_buffer, "display", "1") + return w.WEECHAT_RC_OK_EAT + return w.WEECHAT_RC_OK_EAT - channel = channels.find(buffer) - if channel is None or channel.members is None: - return w.WEECHAT_RC_OK - for m in channel.members: - user = channel.server.users.find(m) - w.hook_completion_list_add(completion, "@" + user.name, 1, w.WEECHAT_LIST_POS_SORT) - return w.WEECHAT_RC_OK - - -def complete_next_cb(data, buffer, command): - """Extract current word, if it is equal to a nick, prefix it with @ and - rely on nick_completion_cb adding the @-prefixed versions to the - completion lists, then let Weechat's internal completion do its - thing - - """ +def command_showmuted(data, current_buffer, args): + current = w.current_buffer() + w.prnt(EVENTROUTER.weechat_controller.buffers[current].team.channel_buffer, str(EVENTROUTER.weechat_controller.buffers[current].team.muted_channels)) - channel = channels.find(buffer) - if channel is None or channel.members is None: - return w.WEECHAT_RC_OK - input = w.buffer_get_string(buffer, "input") - current_pos = w.buffer_get_integer(buffer, "input_pos") - 1 - input_length = w.buffer_get_integer(buffer, "input_length") - word_start = 0 - word_end = input_length - # If we're on a non-word, look left for something to complete - while current_pos >= 0 and input[current_pos] != '@' and not input[current_pos].isalnum(): - current_pos = current_pos - 1 - if current_pos < 0: - current_pos = 0 - for l in range(current_pos, 0, -1): - if input[l] != '@' and not input[l].isalnum(): - word_start = l + 1 - break - for l in range(current_pos, input_length): - if not input[l].isalnum(): - word_end = l - break - word = input[word_start:word_end] - for m in channel.members: - user = channel.server.users.find(m) - if user.name == word: - # Here, we cheat. Insert a @ in front and rely in the @ - # nicks being in the completion list - w.buffer_set(buffer, "input", input[:word_start] + "@" + input[word_start:]) - w.buffer_set(buffer, "input_pos", str(w.buffer_get_integer(buffer, "input_pos") + 1)) +def thread_command_callback(data, current_buffer, args): + current = w.current_buffer() + channel = EVENTROUTER.weechat_controller.buffers.get(current) + if channel: + args = args.split() + if args[0] == '/thread': + if len(args) == 2: + try: + pm = channel.messages[SlackTS(args[1])] + except: + pm = channel.hashed_messages[args[1]] + tc = SlackThreadChannel(EVENTROUTER, pm) + pm.thread_channel = tc + tc.open() + #tc.create_buffer() + return w.WEECHAT_RC_OK_EAT + elif args[0] == '/reply': + count = int(args[1]) + msg = " ".join(args[2:]) + mkeys = channel.sorted_message_keys() + mkeys.reverse() + parent_id = str(mkeys[count - 1]) + channel.send_message(msg, request_dict_ext={"thread_ts": parent_id}) return w.WEECHAT_RC_OK_EAT - return w.WEECHAT_RC_OK + w.prnt(current, "Invalid thread command.") + return w.WEECHAT_RC_OK_EAT +def rehistory_command_callback(data, current_buffer, args): + current = w.current_buffer() + channel = EVENTROUTER.weechat_controller.buffers.get(current) + channel.got_history = False + w.buffer_clear(channel.channel_buffer) + channel.get_history() + return w.WEECHAT_RC_OK_EAT -# Slack specific requests -def async_slack_api_request(domain, token, request, post_data, priority=False): - if not STOP_TALKING_TO_SLACK: - post_data["token"] = token - url = 'url:https://{}/api/{}?{}'.format(domain, request, urllib.urlencode(post_data)) - context = pickle.dumps({"request": request, "token": token, "post_data": post_data}) - params = {'useragent': 'wee_slack {}'.format(SCRIPT_VERSION)} - dbg("URL: {} context: {} params: {}".format(url, context, params)) - w.hook_process_hashtable(url, params, config.slack_timeout, "url_processor_cb", context) +@slack_buffer_required +def hide_command_callback(data, current_buffer, args): + c = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + if c: + name = c.formatted_name(style='long_default') + if name in config.distracting_channels: + w.buffer_set(c.channel_buffer, "hidden", "1") + return w.WEECHAT_RC_OK_EAT +def slack_command_cb(data, current_buffer, args): + a = args.split(' ', 1) + if len(a) > 1: + function_name, args = a[0], args + else: + function_name, args = a[0], args -def async_slack_api_upload_request(token, request, post_data, priority=False): - if not STOP_TALKING_TO_SLACK: - url = 'https://slack.com/api/{}'.format(request) - file_path = os.path.expanduser(post_data["file"]) - if ' ' in file_path: - file_path = file_path.replace(' ','\ ') - command = 'curl -F file=@{} -F channels={} -F token={} {}'.format(file_path, post_data["channels"], token, url) - context = pickle.dumps({"request": request, "token": token, "post_data": post_data}) - w.hook_process(command, config.slack_timeout, "url_processor_cb", context) + try: + EVENTROUTER.cmds[function_name]("", current_buffer, args) + except KeyError: + w.prnt("", "Command not found: " + function_name) + return w.WEECHAT_RC_OK +@slack_buffer_required +def command_distracting(data, current_buffer, args): + channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer, None) + if channel: + fullname = channel.formatted_name(style="long_default") + if config.distracting_channels.count(fullname) == 0: + config.distracting_channels.append(fullname) + else: + config.distracting_channels.pop(config.distracting_channels.index(fullname)) + save_distracting_channels() -# funny, right? -big_data = {} +def save_distracting_channels(): + w.config_set_plugin('distracting_channels', ','.join(config.distracting_channels)) +@slack_buffer_required +def command_slash(data, current_buffer, args): + """ + Support for custom slack commands + /slack slash /customcommand arg1 arg2 arg3 + """ + e = EVENTROUTER + channel = e.weechat_controller.buffers.get(current_buffer, None) + if channel: + team = channel.team -def url_processor_cb(data, command, return_code, out, err): - global big_data - data = pickle.loads(data) - identifier = sha.sha("{}{}".format(data, command)).hexdigest() - if identifier not in big_data: - big_data[identifier] = '' - big_data[identifier] += out - if return_code == 0: - try: - my_json = json.loads(big_data[identifier]) - except: - dbg("request failed, doing again...") - dbg("response length: {} identifier {}\n{}".format(len(big_data[identifier]), identifier, data)) - my_json = False + if args is None: + server.buffer_prnt("Usage: /slack slash /someslashcommand [arguments...].") + return - big_data.pop(identifier, None) + split_args = args.split(None, 2) + command = split_args[1] + text = split_args[2] if len(split_args) > 2 else "" - if my_json: - if data["request"] == 'rtm.start': - servers.find(data["token"]).connected_to_slack(my_json) - servers.update_hashtable() + s = SlackRequest(team.token, "chat.command", {"command": command, "text": text, 'channel': channel.identifier}, team_hash=team.team_hash, channel_identifier=channel.identifier) + EVENTROUTER.receive(s) - else: - if "channel" in data["post_data"]: - channel = data["post_data"]["channel"] - token = data["token"] - if "messages" in my_json: - my_json["messages"].reverse() - for message in my_json["messages"]: - message["_server"] = servers.find(token).domain - message["channel"] = servers.find(token).channels.find(channel).identifier - process_message(message) - if "channel" in my_json: - if "members" in my_json["channel"]: - channels.find(my_json["channel"]["id"]).members = set(my_json["channel"]["members"]) +@slack_buffer_required +def command_mute(data, current_buffer, args): + current = w.current_buffer() + channel_id = EVENTROUTER.weechat_controller.buffers[current].identifier + team = EVENTROUTER.weechat_controller.buffers[current].team + if channel_id not in team.muted_channels: + team.muted_channels.add(channel_id) else: - if return_code != -1: - big_data.pop(identifier, None) - dbg("return code: {}, data: {}, output: {}, error: {}".format(return_code, data, out, err)) - - return w.WEECHAT_RC_OK - - -def cache_write_cb(data, remaining): - cache_file = open("{}/{}".format(WEECHAT_HOME, CACHE_NAME), 'w') - cache_file.write(CACHE_VERSION + "\n") - for channel in channels: - if channel.active: - for message in channel.messages: - cache_file.write("{}\n".format(json.dumps(message.message_json))) - return w.WEECHAT_RC_OK + team.muted_channels.discard(channel_id) + s = SlackRequest(team.token, "users.prefs.set", {"name": "muted_channels", "value": ",".join(team.muted_channels)}, team_hash=team.team_hash, channel_identifier=channel_id) + EVENTROUTER.receive(s) +@slack_buffer_required +def command_openweb(data, current_buffer, args): + #if done from server buffer, open slack for reals + channel = EVENTROUTER.weechat_controller.buffers[current_buffer] + if isinstance(channel, SlackTeam): + url = "https://{}".format(channel.team.domain) + else: + now = SlackTS() + url = "https://{}/archives/{}/p{}000000".format(channel.team.domain, channel.slack_name, now.majorstr()) + w.prnt_date_tags(channel.team.channel_buffer, SlackTS().major, "openweb,logger_backlog_end,notify_none", url) -def cache_load(): - global message_cache - try: - file_name = "{}/{}".format(WEECHAT_HOME, CACHE_NAME) - cache_file = open(file_name, 'r') - if cache_file.readline() == CACHE_VERSION + "\n": - dbg("Loading messages from cache.", main_buffer=True) - for line in cache_file: - j = json.loads(line) - message_cache[j["channel"]].append(line) - dbg("Completed loading messages from cache.", main_buffer=True) - except ValueError: - w.prnt("", "Failed to load cache file, probably illegal JSON.. Ignoring") - pass - except IOError: - w.prnt("", "cache file not found") - pass +def command_nodistractions(data, current_buffer, args): + global hide_distractions + hide_distractions = not hide_distractions + if config.distracting_channels != ['']: + for channel in config.distracting_channels: + dbg('hiding channel {}'.format(channel)) + #try: + for c in EVENTROUTER.weechat_controller.buffers.itervalues(): + if c == channel: + dbg('found channel {} to hide'.format(channel)) + w.buffer_set(c.channel_buffer, "hidden", str(int(hide_distractions))) + #except: + # dbg("Can't hide channel {} .. removing..".format(channel), main_buffer=True) +# config.distracting_channels.pop(config.distracting_channels.index(channel)) +# save_distracting_channels() -# END Slack specific requests +@slack_buffer_required +def command_upload(data, current_buffer, args): + channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer) + url = 'https://slack.com/api/files.upload' + fname = args.split(' ', 1) + file_path = os.path.expanduser(fname[1]) + team = EVENTROUTER.weechat_controller.buffers[current_buffer].team + if ' ' in file_path: + file_path = file_path.replace(' ', '\ ') + + command = 'curl -F file=@{} -F channels={} -F token={} {}'.format(file_path, channel.identifier, team.token, url) + w.hook_process(command, config.slack_timeout, '', '') -# Utility Methods +def away_command_cb(data, current_buffer, args): + #TODO: reimplement all.. maybe + (all, message) = re.match("^/away(?:\s+(-all))?(?:\s+(.+))?", args).groups() + if message is None: + command_back(data, current_buffer, args) + else: + command_away(data, current_buffer, args) + return w.WEECHAT_RC_OK +@slack_buffer_required +def command_away(data, current_buffer, args): + """ + Sets your status as 'away' + /slack away + """ + team = EVENTROUTER.weechat_controller.buffers[current_buffer].team + s = SlackRequest(team.token, "presence.set", {"presence": "away"}, team_hash=team.team_hash) + EVENTROUTER.receive(s) -def current_domain_name(): - buffer = w.current_buffer() - if servers.find(buffer): - return servers.find(buffer).domain - else: - # number = w.buffer_get_integer(buffer, "number") - name = w.buffer_get_string(buffer, "name") - name = ".".join(name.split(".")[:-1]) - return name +@slack_buffer_required +def command_back(data, current_buffer, args): + """ + Sets your status as 'back' + /slack back + """ + team = EVENTROUTER.weechat_controller.buffers[current_buffer].team + s = SlackRequest(team.token, "presence.set", {"presence": "active"}, team_hash=team.team_hash) + EVENTROUTER.receive(s) -def current_buffer_name(short=False): - buffer = w.current_buffer() - # number = w.buffer_get_integer(buffer, "number") - name = w.buffer_get_string(buffer, "name") - if short: - try: - name = name.split('.')[-1] - except: - pass - return name +@slack_buffer_required +def label_command_cb(data, current_buffer, args): + channel = EVENTROUTER.weechat_controller.buffers.get(current_buffer) + if channel and channel.type == 'thread': + aargs = args.split(None, 2) + new_name = " +" + aargs[1] + channel.label = new_name + w.buffer_set(channel.channel_buffer, "short_name", new_name) +def command_p(data, current_buffer, args): + args = args.split(' ', 1)[1] + w.prnt("", "{}".format(eval(args))) -def closed_slack_buffer_cb(data, buffer): - global slack_buffer - slack_buffer = None - return w.WEECHAT_RC_OK +###### NEW EXCEPTIONS +class ProcessNotImplemented(Exception): + """ + Raised when we try to call process_(something), but + (something) has not been defined as a function. + """ + def __init__(self, function_name): + super(ProcessNotImplemented, self).__init__(function_name) -def create_slack_buffer(): - global slack_buffer - slack_buffer = w.buffer_new("slack", "", "", "closed_slack_buffer_cb", "") - w.buffer_set(slack_buffer, "notify", "0") - # w.buffer_set(slack_buffer, "display", "1") - return w.WEECHAT_RC_OK +class InvalidType(Exception): + """ + Raised when we do type checking to ensure objects of the wrong + type are not used improperly. + """ + def __init__(self, type_str): + super(InvalidType, self).__init__(type_str) +###### New but probably old and need to migrate def closed_slack_debug_buffer_cb(data, buffer): global slack_debug @@ -2422,39 +2806,100 @@ def create_slack_debug_buffer(): slack_debug = w.buffer_new("slack-debug", "", "", "closed_slack_debug_buffer_cb", "") w.buffer_set(slack_debug, "notify", "0") - -def quit_notification_cb(signal, sig_type, data): - stop_talking_to_slack() - - -def script_unloaded(): - stop_talking_to_slack() +def load_emoji(): + try: + global EMOJI + DIR = w.info_get("weechat_dir", "") + #no idea why this does't work w/o checking the type?! + dbg(type(DIR), 0) + ef = open('{}/weemoji.json'.format(DIR), 'r') + EMOJI = json.loads(ef.read()) + ef.close() + except: + dbg("Unexpected error: {}".format(sys.exc_info()), 5) return w.WEECHAT_RC_OK - -def stop_talking_to_slack(): +def setup_hooks(): + cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} + + w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') + + w.hook_timer(1000, 0, 0, "typing_update_cb", "") + w.hook_timer(1000, 0, 0, "buffer_list_update_callback", "EVENTROUTER") + w.hook_timer(3000, 0, 0, "reconnect_callback", "EVENTROUTER") + w.hook_timer(1000 * 60 * 5, 0, 0, "slack_never_away_cb", "") + + w.hook_signal('buffer_closing', "buffer_closing_callback", "EVENTROUTER") + w.hook_signal('buffer_switch', "buffer_switch_callback", "EVENTROUTER") + w.hook_signal('window_switch', "buffer_switch_callback", "EVENTROUTER") + w.hook_signal('quit', "quit_notification_cb", "") + w.hook_signal('input_text_changed', "typing_notification_cb", "") + + w.hook_command( + # Command name and description + 'slack', 'Plugin to allow typing notification and sync of read markers for slack.com', + # Usage + '[command] [command options]', + # Description of arguments + 'Commands:\n' + + '\n'.join(cmds.keys()) + + '\nUse /slack help [command] to find out more\n', + # Completions + '|'.join(cmds.keys()), + # Function name + 'slack_command_cb', '') + #w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') + + w.hook_command_run('/me', 'me_command_cb', '') + w.hook_command_run('/query', 'command_talk', '') + w.hook_command_run('/join', 'command_talk', '') + w.hook_command_run('/part', 'part_command_cb', '') + w.hook_command_run('/leave', 'part_command_cb', '') + w.hook_command_run('/topic', 'command_topic', '') + w.hook_command_run('/thread', 'thread_command_callback', '') + w.hook_command_run('/reply', 'thread_command_callback', '') + w.hook_command_run('/rehistory', 'rehistory_command_callback', '') + w.hook_command_run('/hide', 'hide_command_callback', '') + w.hook_command_run('/msg', 'msg_command_cb', '') + w.hook_command_run('/label', 'label_command_cb', '') + w.hook_command_run("/input complete_next", "complete_next_cb", "") + w.hook_command_run('/away', 'away_command_cb', '') + + w.hook_completion("nicks", "complete @-nicks for slack", "nick_completion_cb", "") + w.hook_completion("emoji", "complete :emoji: for slack", "emoji_completion_cb", "") + + # Hooks to fix/implement + #w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") + #w.hook_signal('buffer_opened', "buffer_opened_cb", "") + #w.hook_signal('window_scrolled', "scrolled_cb", "") + #w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") + + +##### END NEW + + +def dbg(message, level=0, main_buffer=False, fout=False): """ - Prevents a race condition where quitting closes buffers - which triggers leaving the channel because of how close - buffer is handled + send debug output to the slack-debug buffer and optionally write to a file. """ - global STOP_TALKING_TO_SLACK - STOP_TALKING_TO_SLACK = True - cache_write_cb("", "") - return w.WEECHAT_RC_OK - - -def scrolled_cb(signal, sig_type, data): - try: - if w.window_get_integer(data, "scrolling") == 1: - channels.find(w.current_buffer()).set_scrolling() + #TODO: do this smarter + #return + if level >= config.debug_level: + global debug_string + message = "DEBUG: {}".format(message) + # message = message.encode('utf-8', 'replace') + if fout: + file('/tmp/debug.log', 'a+').writelines(message + '\n') + if main_buffer: + #w.prnt("", "---------") + w.prnt("", "slack: " + message) else: - channels.find(w.current_buffer()).unset_scrolling() - except: - pass - return w.WEECHAT_RC_OK + if slack_debug and (not debug_string or debug_string in message): + #w.prnt(slack_debug, "---------") + w.prnt(slack_debug, message) -# END Utility Methods + +###### Config code class PluginConfig(object): # Default settings. @@ -2468,6 +2913,7 @@ class PluginConfig(object): 'colorize_nicks': 'true', 'colorize_private_chats': 'false', 'debug_mode': 'false', + 'debug_level': '3', 'distracting_channels': '', 'show_reaction_nicks': 'false', 'slack_api_token': 'INSERT VALID KEY HERE!', @@ -2475,12 +2921,22 @@ class PluginConfig(object): 'switch_buffer_on_join': 'true', 'trigger_value': 'false', 'unfurl_ignore_alt_text': 'false', + 'cache_messages': 'true', + 'record_events': 'false', + 'thread_suffix_color': 'lightcyan', + 'unhide_buffers_with_activity': 'false', + 'short_buffer_names': 'false', + 'channel_name_typing_indicator': 'true', + 'background_load_all_history': 'false', + 'never_away': 'false', + 'server_aliases': '', } # Set missing settings to their defaults. Load non-missing settings from # weechat configs. def __init__(self): - for key,default in self.settings.iteritems(): + self.migrate() + for key, default in self.settings.iteritems(): if not w.config_get_plugin(key): w.config_set_plugin(key, default) self.config_changed(None, None, None) @@ -2514,6 +2970,11 @@ class PluginConfig(object): def get_distracting_channels(self, key): return [x.strip() for x in w.config_get_plugin(key).split(',')] + def get_server_aliases(self, key): + alias_list = w.config_get_plugin(key) + if len(alias_list) > 0: + return dict(item.split(":") for item in alias_list.split(",")) + def get_slack_api_token(self, key): token = w.config_get_plugin("slack_api_token") if token.startswith('${sec.data'): @@ -2521,9 +2982,56 @@ class PluginConfig(object): else: return token + def get_thread_suffix_color(self, key): + return w.config_get_plugin("thread_suffix_color") + + def get_debug_level(self, key): + return int(w.config_get_plugin(key)) + def get_slack_timeout(self, key): return int(w.config_get_plugin(key)) + def migrate(self): + """ + This is to migrate the extension name from slack_extension to slack + """ + if not w.config_get_plugin("migrated"): + for k in self.settings.keys(): + if not w.config_is_set_plugin(k): + p = w.config_get("plugins.var.python.slack_extension.{}".format(k)) + data = w.config_string(p) + if data != "": + w.config_set_plugin(k, data) + w.config_set_plugin("migrated", "true") + + +# to Trace execution, add `setup_trace()` to startup +# and to a function and sys.settrace(trace_calls) to a function +def setup_trace(): + global f + now = time.time() + f = open('{}/{}-trace.json'.format(RECORD_DIR, now), 'w') + +def trace_calls(frame, event, arg): + global f + if event != 'call': + return + co = frame.f_code + func_name = co.co_name + if func_name == 'write': + # Ignore write() calls from print statements + return + func_line_no = frame.f_lineno + func_filename = co.co_filename + caller = frame.f_back + caller_line_no = caller.f_lineno + caller_filename = caller.f_code.co_filename + print >> f, 'Call to %s on line %s of %s from line %s of %s' % \ + (func_name, func_line_no, func_filename, + caller_line_no, caller_filename) + f.flush() + return + # Main if __name__ == "__main__": @@ -2536,81 +3044,46 @@ if __name__ == "__main__": w.prnt("", "\nERROR: Weechat version 1.3+ is required to use {}.\n\n".format(SCRIPT_NAME)) else: - WEECHAT_HOME = w.info_get("weechat_dir", "") - CACHE_NAME = "slack.cache" - STOP_TALKING_TO_SLACK = False + global EVENTROUTER + EVENTROUTER = EventRouter() + #setup_trace() + + #WEECHAT_HOME = w.info_get("weechat_dir", "") + #CACHE_NAME = "slack.cache" + #STOP_TALKING_TO_SLACK = False # Global var section slack_debug = None config = PluginConfig() config_changed_cb = config.config_changed - cmds = {k[8:]: v for k, v in globals().items() if k.startswith("command_")} - proc = {k[8:]: v for k, v in globals().items() if k.startswith("process_")} - typing_timer = time.time() - domain = None - previous_buffer = None - slack_buffer = None + #domain = None + #previous_buffer = None + #slack_buffer = None - buffer_list_update = False - previous_buffer_list_update = 0 - - never_away = False + #never_away = False hide_distractions = False - hotlist = w.infolist_get("hotlist", "", "") - main_weechat_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, "DOESNOTEXIST!@#$")) - - message_cache = collections.defaultdict(list) - cache_load() + #hotlist = w.infolist_get("hotlist", "", "") + #main_weechat_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, "DOESNOTEXIST!@#$")) - servers = SearchList() - for token in config.slack_api_token.split(','): - server = SlackServer(token) - servers.append(server) - channels = SearchList() - users = SearchList() + #message_cache = collections.defaultdict(list) + #if config.cache_messages: + # cache_load() w.hook_config("plugins.var.python." + SCRIPT_NAME + ".*", "config_changed_cb", "") - w.hook_timer(3000, 0, 0, "slack_connection_persistence_cb", "") + + load_emoji() + setup_hooks() # attach to the weechat hooks we need - w.hook_timer(1000, 0, 0, "typing_update_cb", "") - w.hook_timer(1000, 0, 0, "buffer_list_update_cb", "") - w.hook_timer(1000, 0, 0, "hotlist_cache_update_cb", "") - w.hook_timer(1000 * 60 * 29, 0, 0, "slack_never_away_cb", "") - w.hook_timer(1000 * 60 * 5, 0, 0, "cache_write_cb", "") - w.hook_signal('buffer_closing', "buffer_closing_cb", "") - w.hook_signal('buffer_opened', "buffer_opened_cb", "") - w.hook_signal('buffer_switch', "buffer_switch_cb", "") - w.hook_signal('window_switch', "buffer_switch_cb", "") - w.hook_signal('input_text_changed', "typing_notification_cb", "") - w.hook_signal('quit', "quit_notification_cb", "") - w.hook_signal('window_scrolled', "scrolled_cb", "") - w.hook_command( - # Command name and description - 'slack', 'Plugin to allow typing notification and sync of read markers for slack.com', - # Usage - '[command] [command options]', - # Description of arguments - 'Commands:\n' + - '\n'.join(cmds.keys()) + - '\nUse /slack help [command] to find out more\n', - # Completions - '|'.join(cmds.keys()), - # Function name - 'slack_command_cb', '') - # w.hook_command('me', 'me_command_cb', '') - w.hook_command('me', '', 'stuff', 'stuff2', '', 'me_command_cb', '') - w.hook_command_run('/query', 'join_command_cb', '') - w.hook_command_run('/join', 'join_command_cb', '') - w.hook_command_run('/part', 'part_command_cb', '') - w.hook_command_run('/leave', 'part_command_cb', '') - w.hook_command_run('/topic', 'topic_command_cb', '') - w.hook_command_run('/msg', 'msg_command_cb', '') - w.hook_command_run("/input complete_next", "complete_next_cb", "") - w.hook_command_run('/away', 'away_command_cb', '') - w.hook_completion("nicks", "complete @-nicks for slack", - "nick_completion_cb", "") - w.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') + + tokens = config.slack_api_token.split(',') + for t in tokens: + s = SlackRequest(t, 'rtm.start', {}) + EVENTROUTER.receive(s) + if config.record_events: + EVENTROUTER.record() + EVENTROUTER.handle_next() + w.hook_timer(10, 0, 0, "handle_next", "") # END attach to the weechat hooks we need diff --git a/weemoji.json b/weemoji.json new file mode 100644 index 0000000..eb19849 --- /dev/null +++ b/weemoji.json @@ -0,0 +1 @@ +{"emoji": ["wine_glass", "flag-tl", "flag-tn", "clock830", "flag-th", "rabbit", "flag-tj", "european_post_office", "flag-nr", "tram", "wink", "flag-tg", "department_store", "flag-ta", "slightly_smiling_face", "alien", "crocodile", "flag-au", "flag-tz", "bulb", "heavy_heart_exclamation_mark_ornament", "bomb", "flag-tv", "golfer", "hole", "flag-tr", "maple_leaf", "building_construction", "face_with_rolling_eyes", "man_with_gua_pi_mao", "person_with_ball", "e-mail", "tv", "open_hands", "sweat_drops", "pager", "file_cabinet", "laughing", "part_alternation_mark", "flag-td", "tm", "mountain_cableway", "melon", "smile", "snow_cloud", "large_blue_circle", "persevere", "sound", "fax", "woman", "eight_pointed_black_star", "thought_balloon", "end", "oncoming_automobile", "wave", "u7a7a", "woman-woman-boy-boy", "flag-cd", "hammer_and_wrench", "ticket", "flag-tc", "ramen", "twisted_rightwards_arrows", "cool", "four", "school", "small_airplane", "high_brightness", "nerd_face", "upside_down_face", "deciduous_tree", "notes", "white_flower", "biohazard_sign", "gun", "video_game", "saxophone", "car", "flag-ic", "notebook_with_decorative_cover", "triumph", "flag-io", "flag-in", "flag-im", "slightly_frowning_face", "black_right_pointing_double_triangle_with_vertical_bar", "tea", "flag-ls", "flag-bn", "dove_of_peace", "flag-ie", "arrow_left", "old_key", "flag-tt", "zero", "small_orange_diamond", "a", "white_square_button", "flag-is", "hankey", "flag-iq", "cactus", "spaghetti", "white_small_square", "ribbon", "flag-it", "toilet", "mega", "abc", "hocho", "flag-sr", "knife_fork_plate", "flag-fo", "purple_heart", "love_letter", "flag-fk", "file_folder", "flag-fi", "clipboard", "baby_bottle", "new", "bird", "flag-ua", "1234", "peace_symbol", "spock-hand", "couch_and_lamp", "no_smoking", "no_bicycles", "herb", "pouting_cat", "vertical_traffic_light", "leo", "house_with_garden", "flag-pm", "baseball", "busstop", "new_moon", "kissing", "man-woman-boy-boy", "100", "flag-na", "boy", "flag-sl", "capital_abcd", "no_entry", "wheel_of_dharma", "metro", "leaves", "heavy_plus_sign", "roller_coaster", "game_die", "man-man-girl-girl", "classical_building", "hamster", "flag-gy", "pick", "popcorn", "cold_sweat", "massage", "fleur_de_lis", "flag-pr", "chains", "flag-pt", "apple", "family", "scales", "sleeping_accommodation", "rice_cracker", "wind_blowing_face", "inbox_tray", "flag-ma", "flag-pa", "green_heart", "mahjong", "flag-pf", "flag-pg", "flag-ph", "flag-ec", "sleuth_or_spy", "clock330", "flag-ca", "dango", "honey_pot", "eye", "keycap_star", "baby", "sake", "confounded", "hospital", "poodle", "frog", "musical_note", "camera", "sleeping", "crescent_moon", "world_map", "aries", "flag-nl", "ear_of_rice", "flag-si", "video_camera", "mouse2", "chestnut", "flag-mg", "guardsman", "clock230", "baby_symbol", "atom_symbol", "steam_locomotive", "man_in_business_suit_levitating", "motor_boat", "tangerine", "blue_heart", "mantelpiece_clock", "recycle", "train", "beers", "water_buffalo", "flag-cz", "first_quarter_moon_with_face", "mailbox_closed", "curly_loop", "lower_left_fountain_pen", "pouch", "flag-ba", "jack_o_lantern", "izakaya_lantern", "palm_tree", "derelict_house_building", "tired_face", "cat", "dizzy", "nine", "chocolate_bar", "v", "running_shirt_with_sash", "ferry", "arrow_lower_left", "put_litter_in_its_place", "coffin", "abcd", "heart", "chart_with_upwards_trend", "arrow_backward", "hamburger", "pushpin", "lock", "flag-eu", "dolphin", "flag-es", "confused", "accept", "night_with_stars", "studio_microphone", "pig2", "white_medium_small_square", "flag-eh", "flag-eg", "sunglasses", "airplane", "trumpet", "flag-ee", "bow", "flag-bj", "clock12", "earth_americas", "see_no_evil", "scorpius", "flag-bo", "flag-bl", "flag-bm", "flag-bb", "mouse", "speedboat", "six", "snowman_without_snow", "ledger", "flag-bd", "flag-be", "flag-bz", "small_blue_diamond", "leftwards_arrow_with_hook", "amphora", "rewind", "flag-br", "no_bell", "flag-mc", "earth_asia", "flag-bv", "goat", "flag-bt", "pizza", "heavy_check_mark", "trident", "briefcase", "cocktail", "kissing_closed_eyes", "sunny", "star_of_david", "flag-bh", "customs", "motorway", "fork_and_knife", "birthday", "fast_forward", "heartpulse", "mag", "taco", "sparkler", "sparkles", "flag-va", "shirt", "tomato", "womens", "octopus", "wheelchair", "volleyball", "dragon", "mostly_sunny", "tulip", "flag-cu", "truck", "wrench", "flag-je", "ambulance", "sa", "point_up_2", "egg", "small_red_triangle", "umbrella_with_rain_drops", "flag-gp", "shield", "office", "mute", "clapper", "flag-bf", "funeral_urn", "haircut", "soon", "flag-bg", "symbols", "black_square_button", "flag-jp", "keyboard", "japan", "post_office", "last_quarter_moon_with_face", "flag-sb", "rosette", "pray", "linked_paperclips", "flushed", "flag-sa", "dark_sunglasses", "dizzy_face", "rugby_football", "currency_exchange", "flag-by", "paperclip", "moneybag", "mailbox_with_no_mail", "man-woman-girl-girl", "sob", "soccer", "dolls", "flag-gr", "coffee", "tiger2", "flag-la", "flag-lb", "neutral_face", "black_right_pointing_triangle_with_double_vertical_bar", "monorail", "elephant", "flag-li", "open_mouth", "bar_chart", "flag-lt", "european_castle", "flag-lv", "page_with_curl", "woman-heart-woman", "snake", "kiss", "blue_car", "confetti_ball", "flag-ly", "bank", "bread", "minidisc", "flag-mt", "flag-bq", "rice_ball", "oncoming_police_car", "capricorn", "point_left", "flag-gw", "tokyo_tower", "barely_sunny", "weary", "flag-bw", "clock930", "fishing_pole_and_fish", "repeat_one", "bowling", "volcano", "older_woman", "railway_car", "smiley_cat", "flag-er", "information_source", "cry", "telescope", "beginner", "earth_africa", "postal_horn", "house", "fish", "construction_worker", "money_mouth_face", "spider", "u7121", "bride_with_veil", "camera_with_flash", "books", "keycap_ten", "fist", "beetle", "lock_with_ink_pen", "8ball", "worried", "weight_lifter", "sunrise", "exclamation", "no_good", "flag-zm", "lipstick", "lower_left_crayon", "flag-ps", "smirk", "racing_car", "card_file_box", "factory", "baggage_claim", "cherry_blossom", "om_symbol", "sparkle", "fountain", "point_right", "cyclone", "-1", "blue_book", "reminder_ribbon", "dancers", "sheep", "flower_playing_cards", "umbrella", "flag-np", "hatching_chick", "black_circle_for_record", "flag-vi", "free", "traffic_light", "five", "grimacing", "cookie", "poultry_leg", "grapes", "raised_hand_with_fingers_splayed", "smirk_cat", "flag-ws", "diamond_shape_with_a_dot_inside", "lollipop", "flag-id", "man-heart-man", "high_heel", "dagger_knife", "black_medium_small_square", "green_book", "flag-kw", "headphones", "no_mobile_phones", "sun_with_face", "mailbox", "mosque", "passport_control", "bookmark", "+1", "notebook", "yum", "closed_lock_with_key", "heartbeat", "man-woman-girl", "blush", "radioactive_sign", "bullettrain_front", "flag-mh", "ophiuchus", "flag-mp", "bouquet", "sports_medal", "flag-uy", "fire_engine", "one", "feet", "date", "flag-vu", "cow2", "scissors", "ring", "disappointed_relieved", "whale", "zap", "children_crossing", "national_park", "clock430", "horse", "basketball", "monkey", "thinking_face", "blossom", "gift_heart", "top", "flag-il", "spider_web", "clock630", "crossed_swords", "station", "clock730", "man", "banana", "flag-mv", "shaved_ice", "eyes", "shell", "waving_white_flag", "gear", "flag-hn", "radio_button", "memo", "hotel", "small_red_triangle_down", "broken_heart", "suspension_railway", "railway_track", "nut_and_bolt", "aerial_tramway", "flag-hr", "seat", "latin_cross", "flag-hu", "panda_face", "middle_finger", "minibus", "b", "unamused", "flag-af", "flag-ae", "flag-ad", "evergreen_tree", "flag-ao", "mailbox_with_mail", "bee", "scream_cat", "smile_cat", "flag-aq", "flag-ve", "flag-aw", "hourglass_flowing_sand", "clock11", "round_pushpin", "tophat", "six_pointed_star", "dog2", "grinning", "tractor", "flag-vc", "u6709", "u6708", "flag-za", "crying_cat_face", "angel", "nail_care", "runner", "table_tennis_paddle_and_ball", "ram", "writing_hand", "bathtub", "ant", "rat", "flag-hk", "information_desk_person", "flag-ir", "rice_scene", "bookmark_tabs", "milky_way", "pencil2", "mountain", "microphone", "koala", "necktie", "atm", "bullettrain_side", "kissing_cat", "relieved", "thermometer", "flag-xk", "u55b6", "globe_with_meridians", "snowflake", "woman-kiss-woman", "loudspeaker", "princess", "printer", "flag-sy", "flag-sx", "flag-sz", "tornado", "flag-st", "flag-sv", "chart", "flag-ss", "credit_card", "flag-sm", "checkered_flag", "flag-so", "flag-sn", "eight", "flag-sh", "flag-sk", "flag-sj", "handbag", "pensive", "flag-sg", "flag-py", "medal", "arrows_clockwise", "flag-sc", "ballot_box_with_check", "eject", "fried_shrimp", "mans_shoe", "card_index_dividers", "m", "dog", "dollar", "police_car", "new_moon_with_face", "shinto_shrine", "ideograph_advantage", "pineapple", "airplane_arriving", "link", "scream", "bell", "speak_no_evil", "walking", "flag-fm", "golf", "satellite_antenna", "flag-fj", "dromedary_camel", "flag-om", "horse_racing", "three_button_mouse", "lower_left_ballpoint_pen", "radio", "flag-cv", "partly_sunny_rain", "point_down", "chicken", "unicorn_face", "umbrella_on_ground", "flag-tm", "copyright", "arrow_lower_right", "city_sunset", "yen", "waning_crescent_moon", "cupid", "mens", "virgo", "libra", "busts_in_silhouette", "straight_ruler", "flag-fr", "two", "rice", "lips", "flag-gs", "flag-ge", "flag-ac", "alarm_clock", "couplekiss", "sagittarius", "flag-dz", "electric_plug", "circus_tent", "flag-gu", "watch", "arrow_up", "bear", "face_with_head_bandage", "frowning", "flag-dm", "incoming_envelope", "flag-do", "watermelon", "rotating_light", "flag-dj", "wedding", "flag-ag", "flag-dg", "flag-gd", "yellow_heart", "gem", "flag-to", "negative_squared_cross_mark", "girl", "rage", "calling", "flag-at", "microscope", "cheese_wedge", "whale2", "x", "interrobang", "japanese_ogre", "fuelpump", "oncoming_taxi", "man_with_turban", "flag-lk", "arrow_up_small", "art", "smiling_imp", "hear_no_evil", "star_and_crescent", "convenience_store", "up", "flag-ye", "flag-cw", "computer", "arrow_down", "vhs", "flag-ky", "parking", "flag-vn", "pisces", "calendar", "flag-al", "hammer", "hourglass", "hibiscus", "shower", "black_joker", "ferris_wheel", "flag-ar", "camping", "bicyclist", "no_mouth", "postbox", "large_blue_diamond", "non-potable_water", "label", "icecream", "admission_tickets", "lower_left_paintbrush", "flag-hm", "diamonds", "champagne", "email", "older_man", "tent", "flag-ax", "raising_hand", "wc", "bed", "zipper_mouth_face", "joy", "hot_pepper", "aquarius", "waving_black_flag", "couple_with_heart", "guitar", "four_leaf_clover", "key", "flag-az", "flag-tk", "dress", "surfer", "statue_of_liberty", "crystal_ball", "cop", "clock1230", "tropical_drink", "cow", "flag-cp", "no_pedestrians", "oncoming_bus", "moyai", "restroom", "white_large_square", "kaaba", "eggplant", "comet", "low_brightness", "flag-tf", "ok_woman", "space_invader", "pig_nose", "flag-kp", "cancer", "ice_skate", "battery", "man-kiss-man", "wastebasket", "jeans", "cd", "flag-ke", "carousel_horse", "hotsprings", "page_facing_up", "flag-mn", "church", "boar", "black_square_for_stop", "flag-dk", "flag-kn", "flag-ki", "flag-kh", "boat", "turkey", "flag-am", "person_with_blond_hair", "swimmer", "wavy_dash", "three", "oden", "secret", "woman-woman-girl-boy", "stadium", "chipmunk", "stuck_out_tongue_closed_eyes", "helicopter", "heavy_division_sign", "flag-mm", "passenger_ship", "u7981", "mushroom", "fire", "two_hearts", "revolving_hearts", "arrow_down_small", "tiger", "desktop_computer", "flag-de", "foggy", "skin-tone-2", "skin-tone-3", "skin-tone-4", "skin-tone-5", "skin-tone-6", "heart_eyes", "open_file_folder", "dash", "blowfish", "speech_balloon", "wind_chime", "arrow_right_hook", "seedling", "fearful", "envelope_with_arrow", "flag-yt", "closed_umbrella", "film_projector", "bikini", "warning", "taxi", "u5408", "newspaper", "card_index", "raised_hands", "anchor", "loop", "flag-zw", "potable_water", "seven", "pound", "two_women_holding_hands", "timer_clock", "flag-rs", "registered", "sushi", "purse", "monkey_face", "u5272", "rooster", "shamrock", "anger", "rain_cloud", "vs", "flag-ro", "flag-pl", "frame_with_picture", "arrow_forward", "violin", "name_badge", "orthodox_cross", "id", "helmet_with_white_cross", "flag-re", "shopping_bags", "synagogue", "house_buildings", "white_circle", "balloon", "flag-lc", "heart_decoration", "flag-mz", "joy_cat", "kimono", "speaker", "flag-my", "train2", "first_quarter_moon", "dragon_face", "left_luggage", "flag-mx", "meat_on_bone", "light_rail", "bellhop_bell", "satellite", "arrow_heading_up", "snail", "black_small_square", "u6307", "leopard", "hand", "flag-bi", "flag-pn", "badminton_racquet_and_shuttlecock", "barber", "christmas_tree", "cityscape", "slot_machine", "ice_cream", "flag-qa", "euro", "anguished", "crossed_flags", "burrito", "rolled_up_newspaper", "musical_score", "white_frowning_face", "triangular_ruler", "ballot_box_with_ballot", "ocean", "flag-kr", "signal_strength", "flags", "the_horns", "hearts", "joystick", "muscle", "love_hotel", "hotdog", "snowman", "eyeglasses", "flag-lr", "rocket", "camel", "flag-gq", "boot", "u7533", "racehorse", "sleepy", "flag-gt", "heart_eyes_cat", "green_apple", "flag-gi", "flag-gh", "racing_motorcycle", "flag-gm", "flag-gl", "flag-gn", "flag-ga", "bridge_at_night", "flag-pe", "flag-gb", "face_with_thermometer", "clock130", "flag-gg", "flag-gf", "flashlight", "womans_hat", "flag-mf", "sandal", "white_medium_square", "snowboarder", "sunflower", "grey_exclamation", "person_frowning", "rose", "cl", "flag-cf", "cherries", "innocent", "arrow_up_down", "stopwatch", "left_speech_bubble", "ski", "pill", "musical_keyboard", "skier", "full_moon", "hugging_face", "flag-bs", "orange_book", "flag-wf", "flag-ug", "mount_fuji", "couple", "yin_yang", "japanese_goblin", "flag-as", "dart", "clock1", "clock2", "clock3", "clock4", "clock5", "clock6", "clock7", "clock8", "clock9", "doughnut", "flag-kz", "disappointed", "grin", "place_of_worship", "womans_clothes", "flag-vg", "gift", "prayer_beads", "bangbang", "stuck_out_tongue_winking_eye", "flag-kg", "candy", "lightning", "arrows_counterclockwise", "two_men_holding_hands", "dancer", "skull_and_crossbones", "trolleybus", "woman-woman-girl-girl", "bamboo", "flag-um", "trackball", "clap", "outbox_tray", "left_right_arrow", "japanese_castle", "door", "waning_gibbous_moon", "crown", "back", "flag-et", "flag-pw", "flag-us", "sparkling_heart", "clubs", "repeat", "candle", "crab", "man-woman-girl-boy", "smoking", "flag-pk", "man-man-boy-boy", "bento", "robot_face", "moon", "thunder_cloud_and_rain", "tanabata_tree", "fog", "cloud", "large_orange_diamond", "heavy_minus_sign", "o2", "nose", "flag-no", "point_up", "smiley", "facepunch", "zzz", "flag-ni", "flag-nf", "flag-ng", "corn", "flag-ne", "flag-nc", "loud_sound", "kissing_smiling_eyes", "fish_cake", "flag-ms", "flag-nz", "stew", "santa", "kissing_heart", "flag-nu", "tropical_fish", "eight_spoked_asterisk", "trophy", "on", "ok", "city_sunrise", "package", "arrow_right", "school_satchel", "o", "film_frames", "chart_with_downwards_trend", "clock10", "hammer_and_pick", "wolf", "sweat", "ox", "flag-rw", "mountain_railway", "tongue", "speaking_head_in_silhouette", "curry", "angry", "alembic", "baby_chick", "double_vertical_bar", "underage", "do_not_litter", "man-man-boy", "field_hockey_stick_and_ball", "waxing_crescent_moon", "full_moon_with_face", "bath", "flag-se", "sos", "red_circle", "flag-sd", "syringe", "last_quarter_moon", "tada", "ok_hand", "custard", "rowboat", "compression", "clock530", "heavy_multiplication_x", "white_check_mark", "tennis", "question", "beer", "flag-jo", "flag-tw", "lion_face", "flag-ru", "stars", "flag-jm", "stuck_out_tongue", "woman-woman-boy", "iphone", "flag-cm", "sweat_smile", "flag-cl", "flag-uz", "bus", "relaxed", "fireworks", "flag-md", "right_anger_bubble", "level_slider", "construction", "black_circle", "fallen_leaf", "airplane_departure", "astonished", "flag-ci", "turtle", "ear", "black_left_pointing_double_triangle_with_vertical_bar", "bug", "penguin", "arrow_heading_down", "congratulations", "snow_capped_mountain", "flag-ck", "skull", "mobile_phone_off", "flag-ht", "control_knobs", "expressionless", "fries", "grey_question", "arrow_upper_left", "strawberry", "cat2", "athletic_shoe", "unlock", "star2", "cake", "gemini", "man-man-girl-boy", "arrow_double_up", "cricket_bat_and_ball", "flag-me", "ab", "hash", "sweet_potato", "mortar_board", "cinema", "flag-mo", "hatched_chick", "triangular_flag_on_post", "flag-ml", "flag-mk", "flag-ai", "black_nib", "pig", "flag-mw", "floppy_disk", "flag-mu", "black_large_square", "koko", "flag-mr", "flag-mq", "person_with_pouting_face", "flag-ea", "bow_and_arrow", "ship", "ice_hockey_stick_and_puck", "telephone_receiver", "performing_arts", "rainbow", "movie_camera", "lemon", "arrow_double_down", "peach", "arrow_upper_right", "ng", "mountain_bicyclist", "book", "clock1130", "boom", "spiral_calendar_pad", "clock1030", "flag-km", "beach_with_umbrella", "imp", "bust_in_silhouette", "star", "rabbit2", "man-man-girl", "footprints", "football", "pear", "taurus", "articulated_lorry", "no_entry_sign", "u6e80", "money_with_wings", "flag-lu", "bike", "black_medium_square", "closed_book", "desert", "woman-woman-girl", "oil_drum", "ghost", "droplet", "flag-co", "flag-cn", "spades", "flag-ch", "vibration_mode", "phone", "dvd", "flag-cg", "menorah_with_nine_branches", "mask", "flag-cc", "mag_right", "scorpion", "flag-cy", "flag-cx", "hushed", "desert_island", "sunrise_over_mountains", "partly_sunny", "spiral_note_pad", "heavy_dollar_sign", "scroll", "flag-cr"]} -- cgit From 92dd5a0b562b085c9582b1370d220579cb28be46 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 12 Apr 2017 16:52:28 +0000 Subject: direct link thread info --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ff47c98..1911d44 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A WeeChat native client for Slack.com. Provides supplemental features only avail Features -------- - * **New** Threads support! + * **New** [Threads](#threads) support! * Slash commands (including custom ones!) * Upload to slack capabilities! * Emoji reactions! @@ -191,6 +191,8 @@ Debug mode: /slack debug ``` +#### Threads + Start a new thread on the most recent message The number indicates which message in the buffer to reply to, in reverse time order: ``` /reply 1 here is a threaded reply to the most recent message! -- cgit From 08964dafbcf0a061f4cc04e907992ca6fe3fb6f2 Mon Sep 17 00:00:00 2001 From: Ryan Huber Date: Wed, 12 Apr 2017 16:55:09 +0000 Subject: stop claiming to do search, which we still don't --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1911d44..61f66dd 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ wee-slack **News:** 1.0-RC1 is here. It is a pretty massive refactor, and fixes many of the current issues listed on github. Because there was no good way to do this, it breaks some/many existing PRs. _(please report bugs in #wee-slack on freenode)_ -A WeeChat native client for Slack.com. Provides supplemental features only available in the web/mobile clients such as: synchronizing read markers, typing notification, search, (and more)! Connects via the Slack API, and maintains a persistent websocket for notification of events. +A WeeChat native client for Slack.com. Provides supplemental features only available in the web/mobile clients such as: synchronizing read markers, typing notification, threads (and more)! Connects via the Slack API, and maintains a persistent websocket for notification of events. ![animated screenshot](https://github.com/rawdigits/wee-slack/raw/master/docs/slack.gif) -- cgit