diff options
author | Ryan Huber <rhuber@gmail.com> | 2017-02-01 11:14:06 -0800 |
---|---|---|
committer | Ryan Huber <rhuber@gmail.com> | 2017-02-01 11:14:06 -0800 |
commit | bcc2ac1bb5cf2750f27d73348888f60098ca3291 (patch) | |
tree | 3d707498b0013327906d030de14c75709cb376ac /wee_slack.py | |
parent | 47607a88c578f804d76c25dd2d68386556255b21 (diff) | |
download | wee-slack-bcc2ac1bb5cf2750f27d73348888f60098ca3291.tar.gz |
recorded test data added
Diffstat (limited to 'wee_slack.py')
-rw-r--r-- | wee_slack.py | 604 |
1 files changed, 454 insertions, 150 deletions
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'(?<!\\)/', data)[1:] @@ -328,6 +351,39 @@ def buffer_switch_callback(signal, sig_type, data): eventrouter.previous_buffer = data return w.WEECHAT_RC_OK +def buffer_list_update_callback(data, somecount): + 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_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) |