diff options
-rw-r--r-- | _pytest/conftest.py | 3 | ||||
-rw-r--r-- | _pytest/data/message-changed.json | 2 | ||||
-rw-r--r-- | _pytest/data/message-deleted.json | 2 | ||||
-rw-r--r-- | _pytest/data/message-normal.json | 2 | ||||
-rw-r--r-- | _pytest/data/message-normal2.json | 2 | ||||
-rw-r--r-- | _pytest/test_process_message.py | 2 | ||||
-rw-r--r-- | _pytest/test_unfurl.py | 4 | ||||
-rw-r--r-- | wee_slack.py | 447 |
8 files changed, 248 insertions, 216 deletions
diff --git a/_pytest/conftest.py b/_pytest/conftest.py index 2f90977..e9b7750 100644 --- a/_pytest/conftest.py +++ b/_pytest/conftest.py @@ -1,7 +1,8 @@ import pytest import sys -sys.path.append(str(pytest.config.rootdir)) +sys.path.append(".") +#sys.path.append(str(pytest.config.rootdir)) from wee_slack import SlackServer from wee_slack import Channel diff --git a/_pytest/data/message-changed.json b/_pytest/data/message-changed.json index 5a229dc..1f6f5a7 100644 --- a/_pytest/data/message-changed.json +++ b/_pytest/data/message-changed.json @@ -15,5 +15,5 @@ } }, - "myserver": "test.slack.com" + "_server": "test.slack.com" } diff --git a/_pytest/data/message-deleted.json b/_pytest/data/message-deleted.json index 91190b3..f162539 100644 --- a/_pytest/data/message-deleted.json +++ b/_pytest/data/message-deleted.json @@ -5,5 +5,5 @@ "channel": "C2147483705", "ts": "1358878755.000001", "deleted_ts": "1355517519.000005", - "myserver": "test.slack.com" + "_server": "test.slack.com" } diff --git a/_pytest/data/message-normal.json b/_pytest/data/message-normal.json index f3dc568..21610fd 100644 --- a/_pytest/data/message-normal.json +++ b/_pytest/data/message-normal.json @@ -4,5 +4,5 @@ "user": "U2147483697", "text": "Hello world", "ts": "1355517523.000005", - "myserver": "test.slack.com" + "_server": "test.slack.com" } diff --git a/_pytest/data/message-normal2.json b/_pytest/data/message-normal2.json index b67be66..d92a21f 100644 --- a/_pytest/data/message-normal2.json +++ b/_pytest/data/message-normal2.json @@ -4,5 +4,5 @@ "user": "U2147483697", "text": "A Second message!", "ts": "1355517524.000005", - "myserver": "test.slack.com" + "_server": "test.slack.com" } diff --git a/_pytest/test_process_message.py b/_pytest/test_process_message.py index a8cf9a2..1375d05 100644 --- a/_pytest/test_process_message.py +++ b/_pytest/test_process_message.py @@ -30,6 +30,8 @@ def test_process_message(slack_debug, monkeypatch, myservers, mychannels, myuser messages.append( json.loads(open('_pytest/data/message-deleted.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 diff --git a/_pytest/test_unfurl.py b/_pytest/test_unfurl.py index 9af7cf2..c79c0f3 100644 --- a/_pytest/test_unfurl.py +++ b/_pytest/test_unfurl.py @@ -25,7 +25,7 @@ unfurl_map = [ "output": "url: https://example.com (example with spaces) suffix", }, ] - + def test_unfurl_refs(myservers, mychannels, myusers): slack.servers = myservers @@ -34,7 +34,7 @@ def test_unfurl_refs(myservers, mychannels, myusers): slack.message_cache = {} slack.servers[0].users = myusers print mychannels[0].identifier - + for k in unfurl_map: if "ignore_alt_text" in k: assert slack.unfurl_refs(k["input"], ignore_alt_text=k["ignore_alt_text"]) == k["output"] diff --git a/wee_slack.py b/wee_slack.py index d55a50e..7198ba3 100644 --- a/wee_slack.py +++ b/wee_slack.py @@ -12,6 +12,7 @@ import urllib import HTMLParser import sys import traceback +import collections from websocket import create_connection # hack to make tests possible.. better way? @@ -22,12 +23,14 @@ except: SCRIPT_NAME = "slack_extension" SCRIPT_AUTHOR = "Ryan Huber <rhuber@gmail.com>" -SCRIPT_VERSION = "0.98.7" +SCRIPT_VERSION = "0.99.0" SCRIPT_LICENSE = "MIT" SCRIPT_DESC = "Extends weechat for typing notification/search/etc on slack.com" BACKLOG_SIZE = 200 -SCROLLBACK_SIZE = 2000 +SCROLLBACK_SIZE = 500 + +CACHE_VERSION = "3" SLACK_API_TRANSLATOR = { "channel": { @@ -39,6 +42,7 @@ SLACK_API_TRANSLATOR = { }, "im": { "history": "im.history", + "join": "im.open", "leave": "im.close", "mark": "im.mark", }, @@ -254,9 +258,6 @@ class SlackServer(object): self.buffer = w.buffer_new(self.server_buffer_name, "buffer_input_cb", "", "", "") w.buffer_set(self.buffer, "nicklist", "1") - w.nicklist_add_group(self.buffer, '', NICK_GROUP_HERE, "weechat.color.nicklist_group", 1) - w.nicklist_add_group(self.buffer, '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 1) - def create_slack_websocket(self, data): web_socket_url = data['url'] try: @@ -337,6 +338,7 @@ class Channel(object): """ def __init__(self, server, name, identifier, active, last_read=0, prepend_name="", members=[], topic=""): self.name = prepend_name + name + self.current_short_name = prepend_name + name self.identifier = identifier self.active = active self.last_read = float(last_read) @@ -348,7 +350,6 @@ class Channel(object): self.type = "channel" self.server = server self.typing = {} - self.opening = False self.last_received = None self.messages = [] self.scrolling = False @@ -394,7 +395,7 @@ class Channel(object): 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, "short_name", 'loading..') + w.buffer_set(self.channel_buffer, "short_name", self.name) buffer_list_update_next() def attach_buffer(self): @@ -411,15 +412,32 @@ class Channel(object): w.buffer_close(self.channel_buffer) self.channel_buffer = None - def update_nicklist(self): + def update_nicklist(self, user=None): if self.channel_buffer: w.buffer_set(self.channel_buffer, "nicklist", "1") - w.nicklist_remove_all(self.channel_buffer) - here = w.nicklist_add_group(self.channel_buffer, - '', NICK_GROUP_HERE, "weechat.color.nicklist_group", 1) - afk = w.nicklist_add_group(self.channel_buffer, - '', NICK_GROUP_AWAY, "weechat.color.nicklist_group", 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.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.. + if user.presence == 'away': + w.nicklist_add_nick(self.channel_buffer, afk, user.name, user.color_name, "", "", 1) + else: + 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: try: for user in self.members: user = self.members_table[user] @@ -456,20 +474,22 @@ class Channel(object): self.active = False def set_typing(self, user): - self.typing[user] = time.time() - buffer_list_update_next() + if w.buffer_get_integer(self.channel_buffer, "hidden") == 0: + self.typing[user] = time.time() + buffer_list_update_next() def unset_typing(self, user): - try: - del self.typing[user] - buffer_list_update_next() - except: - pass + if w.buffer_get_integer(self.channel_buffer, "hidden") == 0: + try: + del self.typing[user] + buffer_list_update_next() + except: + pass def send_message(self, message): message = self.linkify_text(message) dbg(message) - request = {"type": "message", "channel": self.identifier, "text": message, "myserver": self.server.domain} + request = {"type": "message", "channel": self.identifier, "text": message, "_server": self.server.domain} self.server.send_to_websocket(request) def linkify_text(self, message): @@ -493,7 +513,6 @@ class Channel(object): w.buffer_set(self.channel_buffer, "title", topic) def open(self, update_remote=True): - self.opening = True self.create_buffer() self.active = True self.get_history() @@ -502,12 +521,13 @@ class Channel(object): 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("#")}) - self.opening = False + async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["join"], {"user": users.find(self.name).identifier}) 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: t = time.time() @@ -552,22 +572,10 @@ class Channel(object): else: new_name = self.name if self.channel_buffer: - if w.buffer_get_string(self.channel_buffer, "short_name") != new_name: + if self.current_short_name != new_name: + self.current_short_name = new_name w.buffer_set(self.channel_buffer, "short_name", new_name) -# deprecated in favor of redrawing the entire buffer -# def buffer_prnt_changed(self, user, text, time, append=""): -# if self.channel_buffer: -# if user: -# if self.server.users.find(user): -# name = self.server.users.find(user).formatted_name() -# else: -# name = user -# name = name.decode('utf-8') -# modify_buffer_line(self.channel_buffer, name, text, time, append) -# else: -# modify_buffer_line(self.channel_buffer, None, text, time, append) - def buffer_prnt(self, user='unknown_user', message='no message', time=0): """ writes output (message) to a buffer (channel) @@ -635,30 +643,40 @@ class Channel(object): def unset_scrolling(self): self.scrolling = False - self.buffer_redraw() def has_message(self, ts): return self.messages.count(ts) > 0 - def change_message(self, ts, text): + def change_message(self, ts, text=None): if self.has_message(ts): message_index = self.messages.index(ts) - self.messages[message_index].change_text(text) - self.buffer_redraw() + + 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, int_time) + #otherwise redraw the whole buffer, which is expensive + else: + self.buffer_redraw() return True def add_reaction(self, ts, reaction): if self.has_message(ts): message_index = self.messages.index(ts) self.messages[message_index].add_reaction(reaction) - self.buffer_redraw() + self.change_message(ts) return True def remove_reaction(self, ts, reaction): if self.has_message(ts): message_index = self.messages.index(ts) self.messages[message_index].remove_reaction(reaction) - self.buffer_redraw() + self.change_message(ts) return True def change_previous_message(self, old, new): @@ -683,7 +701,7 @@ class Channel(object): def get_history(self): if self.active: - for message in cache_get(self.identifier): + for message in message_cache[self.identifier]: process_message(json.loads(message), True) if self.last_received != None: async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "oldest": self.last_received, "count": BACKLOG_SIZE}) @@ -713,9 +731,11 @@ class DmChannel(Channel): new_name = self.server.users.find(self.name).formatted_name(' ', colorize_private_chats) if self.channel_buffer: - w.buffer_set(self.channel_buffer, "short_name", new_name) + 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): + def update_nicklist(self, user=None): pass class User(object): @@ -724,8 +744,8 @@ class User(object): self.server = server self.name = name self.identifier = identifier - self.presence = presence self.deleted = deleted + self.presence = presence self.channel_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, self.name)) self.update_color() @@ -733,11 +753,11 @@ class User(object): if deleted: return + self.nicklist_pointer = w.nicklist_add_nick(server.buffer, "", self.name, self.color_name, "", "", 1) if self.presence == 'away': - ngroup = w.nicklist_search_group(server.buffer, "", NICK_GROUP_AWAY) + w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "0") else: - ngroup = w.nicklist_search_group(server.buffer, "", NICK_GROUP_HERE) - self.nicklist_pointer = w.nicklist_add_nick(server.buffer, ngroup, self.name, self.color_name, "", "", 1) + 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): @@ -762,17 +782,21 @@ class User(object): self.presence = "active" for channel in self.server.channels: if channel.has_user(self.identifier): - channel.update_nicklist() + channel.update_nicklist(self.identifier) w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "1") - buffer_list_update_next() + dm_channel = self.server.channels.find(self.name) + if dm_channel and dm_channel.active: + buffer_list_update_next() def set_inactive(self): self.presence = "away" for channel in self.server.channels: if channel.has_user(self.identifier): - channel.update_nicklist() + channel.update_nicklist(self.identifier) w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "0") - buffer_list_update_next() + dm_channel = self.server.channels.find(self.name) + if dm_channel and dm_channel.active: + buffer_list_update_next() def update_color(self): if colorize_nicks: @@ -792,11 +816,6 @@ class User(object): print_color = "" return print_color + prepend + self.name - def open(self): - t = time.time() + 1 - #reply = async_slack_api_request("im.open", {"channel":self.identifier,"ts":t}) - async_slack_api_request(self.server.domain, self.server.token, "im.open", {"user": self.identifier, "ts": t}) - class Bot(object): def __init__(self, server, name, identifier, deleted=False): @@ -884,15 +903,14 @@ def me_command_cb(data, current_buffer, args): def join_command_cb(data, current_buffer, args): - if channels.find(current_buffer) or servers.find(current_buffer): - channel = args.split()[1] - channel = servers.find(current_domain_name()).channels.find(channel) + server = servers.find(current_domain_name()) + arg = args.split()[-1] + channel = server.channels.find(arg) + if channel != None: channel.open() - if w.config_get_plugin('switch_buffer_on_join') != '0': - w.buffer_set(channel.channel_buffer, "display", "1") - return w.WEECHAT_RC_OK_EAT - else: - return w.WEECHAT_RC_OK + if w.config_get_plugin('switch_buffer_on_join') != '0': + w.buffer_set(channel.channel_buffer, "display", "1") + return w.WEECHAT_RC_OK_EAT def part_command_cb(data, current_buffer, args): @@ -946,7 +964,7 @@ def command_talk(current_buffer, args): Open a chat with the specified user /slack talk [user] """ - servers.find(current_domain_name()).users.find(args).open() + servers.find(current_domain_name()).channels.find(args).open() def command_join(current_buffer, args): @@ -1070,7 +1088,7 @@ def command_markread(current_buffer, args): def command_flushcache(current_buffer, args): global message_cache - message_cache = [] + message_cache = collections.defaultdict(list) cache_write_cb("","") def command_cachenow(current_buffer, args): @@ -1168,7 +1186,7 @@ def slack_websocket_cb(server, fd): data = servers.find(server).ws.recv() message_json = json.loads(data) # this magic attaches json that helps find the right dest - message_json['myserver'] = server + message_json['_server'] = server except: return w.WEECHAT_RC_OK # dispatch here @@ -1180,7 +1198,6 @@ def slack_websocket_cb(server, fd): function_name = "unknown" try: proc[function_name](message_json) - # dbg(function_name) except KeyError: if function_name: dbg("Function not implemented: {}\n{}".format(function_name, message_json)) @@ -1192,7 +1209,7 @@ def slack_websocket_cb(server, fd): def process_reply(message_json): global unfurl_ignore_alt_text - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) identifier = message_json["reply_to"] item = server.message_buffer.pop(identifier) if "type" in item: @@ -1209,7 +1226,7 @@ def process_pong(message_json): def process_pref_change(message_json): - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) if message_json['name'] == u'muted_channels': muted = message_json['value'].split(',') for c in server.channels: @@ -1218,11 +1235,11 @@ def process_pref_change(message_json): else: c.muted = False else: - dbg("Preference change not implemented: {}\n{}".format(message_json['name'])) + dbg("Preference change not implemented: {}\n".format(message_json['name'])) def process_team_join(message_json): - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) item = message_json["user"] server.add_user(User(server, item["name"], item["id"], item["presence"])) server.buffer_prnt(server.buffer, "New user joined: {}".format(item["name"])) @@ -1231,7 +1248,7 @@ def process_manual_presence_change(message_json): process_presence_change(message_json) def process_presence_change(message_json): - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) identifier = message_json.get("user", server.nick) if message_json["presence"] == 'active': server.users.find(identifier).set_active() @@ -1254,7 +1271,7 @@ def process_group_marked(message_json): def process_channel_created(message_json): - server = servers.find(message_json["myserver"]) + 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) @@ -1265,24 +1282,27 @@ def process_channel_created(message_json): def process_channel_left(message_json): - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) server.channels.find(message_json["channel"]).close(False) def process_channel_join(message_json): - server = servers.find(message_json["myserver"]) + 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_channel_topic(message_json): - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) channel = server.channels.find(message_json["channel"]) + channel.buffer_prnt(w.prefix("network").rstrip(), message_json["text"], message_json["ts"]) channel.set_topic(message_json["topic"]) def process_channel_joined(message_json): - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) if server.channels.find(message_json["channel"]["name"]): server.channels.find(message_json["channel"]["name"]).open(False) else: @@ -1291,24 +1311,26 @@ def process_channel_joined(message_json): def process_channel_leave(message_json): - server = servers.find(message_json["myserver"]) + 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("quit").rstrip(), text, message_json["ts"]) channel.user_leave(message_json["user"]) def process_channel_archive(message_json): - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) channel = server.channels.find(message_json["channel"]) channel.detach_buffer() def process_group_left(message_json): - server = servers.find(message_json["myserver"]) + 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["myserver"]) + server = servers.find(message_json["_server"]) if server.channels.find(message_json["channel"]["name"]): server.channels.find(message_json["channel"]["name"]).open(False) else: @@ -1322,13 +1344,13 @@ def process_group_archive(message_json): def process_im_close(message_json): - server = servers.find(message_json["myserver"]) + 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["myserver"]) - server.channels.find(message_json["channel"]).open(False) + server = servers.find(message_json["_server"]) + server.channels.find(message_json["channel"]).open() def process_im_marked(message_json): @@ -1339,7 +1361,7 @@ def process_im_marked(message_json): def process_im_created(message_json): - server = servers.find(message_json["myserver"]) + server = servers.find(message_json["_server"]) item = message_json["channel"] channel_name = server.users.find(item["user"]).name if server.channels.find(channel_name): @@ -1351,7 +1373,7 @@ def process_im_created(message_json): def process_user_typing(message_json): - server = servers.find(message_json["myserver"]) + 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) @@ -1361,22 +1383,27 @@ def process_user_typing(message_json): def process_error(message_json): pass - #connected = False def process_reaction_added(message_json): - channel = channels.find(message_json["item"]["channel"]) - channel.add_reaction(message_json["item"]["ts"], message_json["reaction"]) + if message_json["item"].get("type") == "message": + channel = channels.find(message_json["item"]["channel"]) + channel.add_reaction(message_json["item"]["ts"], message_json["reaction"]) + else: + dbg("Reaction to item type not supported: " + str(message_json)) def process_reaction_removed(message_json): - channel = channels.find(message_json["item"]["channel"]) - channel.remove_reaction(message_json["item"]["ts"], message_json["reaction"]) + if message_json["item"].get("type") == "message": + channel = channels.find(message_json["item"]["channel"]) + channel.remove_reaction(message_json["item"]["ts"], message_json["reaction"]) + else: + dbg("Reaction to item type not supported: " + str(message_json)) def create_reaction_string(reactions): + count = 0 if not isinstance(reactions, list): reaction_string = " [{}]".format(reactions) else: reaction_string = ' [' - count = 0 for r in reactions: if r["count"] > 0: count += 1 @@ -1387,141 +1414,148 @@ def create_reaction_string(reactions): return reaction_string # deprecated in favor of redrawing the entire buffer -#def modify_buffer_line(buffer, user, new_message, time, append): -# time = int(float(time)) -# own_lines = w.hdata_pointer(w.hdata_get('buffer'), buffer, 'own_lines') -# if own_lines: -# line = w.hdata_pointer(w.hdata_get('lines'), own_lines, 'last_line') -# hdata_line = w.hdata_get('line') -# hdata_line_data = w.hdata_get('line_data') -# -# while line: -# data = w.hdata_pointer(hdata_line, line, 'data') -# if data: -# date = w.hdata_time(hdata_line_data, data, 'date') -# prefix = w.hdata_string(hdata_line_data, data, 'prefix') -# if new_message == "": -# new_message = w.hdata_string(hdata_line_data, data, 'message') -# if user and (int(date) == int(time) and user == prefix): -# w.prnt("", "found matching time date is {}, time is {} ".format(date, time)) -# w.hdata_update(hdata_line_data, data, {"message": "{}{}".format(new_message, append)}) -# break -# elif not user and (int(date) == int(time)): -# w.hdata_update(hdata_line_data, data, {"message": "{}{}".format(new_message, append)}) -# else: -# pass -# line = w.hdata_move(hdata_line, line, -1) -# return w.WEECHAT_RC_OK - +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 -def process_message(message_json, cache=True): +def render_message(message_json, force=False): global unfurl_ignore_alt_text + #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"]) - try: - # send these messages elsewhere - known_subtypes = ['channel_join', 'channel_leave', 'channel_topic'] - if "subtype" in message_json and message_json["subtype"] in known_subtypes: - proc[message_json["subtype"]](message_json) - - # move message properties down to root of json object - message_json = unwrap_message(message_json) - - server = servers.find(message_json["myserver"]) - channel = channels.find(message_json["channel"]) - - #do not process messages in unexpected channels - if not channel.active: - channel.open(False) - dbg("message came for closed channel {}".format(channel.name)) - return - - time = message_json['ts'] 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 = "" + text = u"" else: - text = "" - - #text = text.decode('utf-8') + text = u"" text = unfurl_refs(text, ignore_alt_text=unfurl_ignore_alt_text) - if "attachments" in message_json: - text += u" --- {}".format(unwrap_attachments(message_json)) + text += unwrap_attachments(message_json) + text = text.lstrip() text = text.replace("\t", " ") - name = get_user(message_json, server) - text = text.encode('utf-8') - name = name.encode('utf-8') if "reactions" in message_json: text += create_reaction_string(message_json["reactions"]) + message_json["_rendered_text"] = text + return text + + +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'] + if "subtype" in message_json and message_json["subtype"] in known_subtypes: + proc[message_json["subtype"]](message_json) - if "subtype" in message_json and message_json["subtype"] == "message_changed": - if "edited" in message_json["message"]: - append = " (edited)" - else: - append = '' - channel.change_message(message_json["message"]["ts"], text + append) - cache=False - elif "subtype" in message_json and message_json["subtype"] == "message_deleted": - append = "(deleted)" - text = "" - channel.change_message(message_json["deleted_ts"], text + append) - cache = False - elif message_json.get("subtype", "") == "channel_leave": - channel.buffer_prnt(w.prefix("quit").rstrip(), text, time) - elif message_json.get("subtype", "") == "channel_join": - channel.buffer_prnt(w.prefix("join").rstrip(), text, time) - elif message_json.get("subtype", "") == "channel_topic": - channel.buffer_prnt(w.prefix("network").rstrip(), text, time) - elif 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) else: - channel.buffer_prnt(name, text, time) + server = servers.find(message_json["_server"]) + channel = channels.find(message_json["channel"]) + + #do not process messages in unexpected channels + if not channel.active: + channel.open(False) + dbg("message came for closed channel {}".format(channel.name)) + return + + time = message_json['ts'] + text = render_message(message_json) + name = get_user(message_json, server) + name = name.encode('utf-8') + + #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) + + else: + channel.buffer_prnt(name, text, time) - if cache: - channel.cache_message(message_json) + + if cache: + channel.cache_message(message_json) except Exception: if channel and ("text" in message_json) and message_json['text'] is not None: channel.buffer_prnt('unknown', message_json['text']) dbg("cannot process message {}\n{}".format(message_json, traceback.format_exc())) -def unwrap_message(message_json): + +def process_message_changed(message_json): + m = message_json["message"] if "message" in message_json: - if "attachments" in message_json["message"]: - message_json["attachments"] = message_json["message"]["attachments"] - if "text" in message_json["message"]: + if "attachments" in m: + message_json["attachments"] = m["attachments"] + if "text" in m: if "text" in message_json: - message_json["text"] += message_json["message"]["text"] + message_json["text"] += m["text"] dbg("added text!") else: - message_json["text"] = message_json["message"]["text"] - if "fallback" in message_json["message"]: + message_json["text"] = m["text"] + if "fallback" in m: if "fallback" in message_json: - message_json["fallback"] += message_json["message"]["fallback"] + message_json["fallback"] += m["fallback"] else: - message_json["fallback"] = message_json["message"]["fallback"] - return message_json + message_json["fallback"] = m["fallback"] + + m["text"] += unwrap_attachments(message_json) + channel = channels.find(message_json["channel"]) + if "edited" in m: + m["text"] += " (edited)" + 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)") def unwrap_attachments(message_json): - attachment_text = '' - for attachment in message_json["attachments"]: - if "fallback" in attachment: - attachment_text += attachment["fallback"] -# attachment_text = attachment_text.encode('ascii', 'ignore') + if "attachments" in message_json: + attachment_text = u' --- ' + for attachment in message_json["attachments"]: + if "fallback" in attachment: + attachment_text += attachment["fallback"] + else: + attachment_text = '' return attachment_text + def resolve_ref(ref): if ref.startswith('@U'): if users.find(ref[1:]): @@ -1550,7 +1584,7 @@ def unfurl_ref(ref, ignore_alt_text=False): display_text = ref.split('|')[1] else: url, desc = ref.split('|', 1) - display_text = "{} ({})".format(url, desc) + display_text = u"{} ({})".format(url, desc) else: display_text = resolve_ref(ref) return display_text @@ -1561,7 +1595,7 @@ def unfurl_refs(text, ignore_alt_text=False): """ if text and text.find('<') > -1: end = 0 - newtext = "" + newtext = u"" while text.find('<') > -1: # Prepend prefix newtext += text[:text.find('<')] @@ -1656,7 +1690,6 @@ def buffer_closing_cb(signal, sig_type, data): 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 - buffer_list_update_next() if channels.find(previous_buffer): channels.find(previous_buffer).mark_read() @@ -1816,7 +1849,7 @@ def url_processor_cb(data, command, return_code, out, err): if "messages" in my_json: messages = my_json["messages"].reverse() for message in my_json["messages"]: - message["myserver"] = servers.find(token).domain + message["_server"] = servers.find(token).domain message["channel"] = servers.find(token).channels.find(channel).identifier process_message(message) if "channel" in my_json: @@ -1831,6 +1864,7 @@ def url_processor_cb(data, command, return_code, out, err): 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: @@ -1841,22 +1875,17 @@ def cache_load(): global message_cache try: file_name = "{}/{}".format(WEECHAT_HOME, CACHE_NAME) - if sum(1 for line in open('myfile.txt')) > 2: - cache_file = open(file_name, 'r') + 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: - message_cache.append(line) + j = json.loads(line) + message_cache[j["channel"]].append(line) + dbg("Completed loading messages from cache.", main_buffer=True) except IOError: - #cache file didn't exist + w.prnt("", "cache file not found") pass -def cache_get(channel): - lines = [] - for line in message_cache: - j = json.loads(line) - if j["channel"] == channel: - lines.append(line) - return lines - # END Slack specific requests # Utility Methods @@ -2021,7 +2050,7 @@ if __name__ == "__main__": hotlist = w.infolist_get("hotlist", "", "") main_weechat_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, "DOESNOTEXIST!@#$")) - message_cache = [] + message_cache = collections.defaultdict(list) cache_load() servers = SearchList() |