From ad7d67eab30b9607a6503bafc2c289fb8d10fab0 Mon Sep 17 00:00:00 2001 From: Felix Van der Jeugt Date: Fri, 22 May 2015 20:09:59 +0200 Subject: [PATCH] add weeslack and remove plugins.conf as it contains a token now --- weechat/plugins.conf | 55 - weechat/python/autoload/wee_slack.py | 1 + weechat/python/wee_slack.py | 1696 ++++++++++++++++++++++++++ 3 files changed, 1697 insertions(+), 55 deletions(-) delete mode 100644 weechat/plugins.conf create mode 120000 weechat/python/autoload/wee_slack.py create mode 100644 weechat/python/wee_slack.py diff --git a/weechat/plugins.conf b/weechat/plugins.conf deleted file mode 100644 index 7fe994e..0000000 --- a/weechat/plugins.conf +++ /dev/null @@ -1,55 +0,0 @@ -# -# weechat -- plugins.conf -# - -[var] -fifo.fifo = "on" -guile.check_license = "off" -lua.check_license = "off" -perl.check_license = "off" -python.anotify.icon = "/usr/share/pixmaps/weechat.xpm" -python.anotify.show_channel_topic = "on" -python.anotify.show_dcc = "on" -python.anotify.show_highlighted_message = "on" -python.anotify.show_invite_message = "on" -python.anotify.show_notice_message = "off" -python.anotify.show_private_action_message = "on" -python.anotify.show_private_message = "on" -python.anotify.show_public_action_message = "off" -python.anotify.show_public_message = "off" -python.anotify.show_server = "on" -python.anotify.show_upgrade_ended = "on" -python.anotify.sticky = "off" -python.anotify.sticky_away = "on" -python.check_license = "off" -python.go.auto_jump = "off" -python.go.color_name = "black,cyan" -python.go.color_name_highlight = "red,cyan" -python.go.color_name_highlight_selected = "red,brown" -python.go.color_name_selected = "black,brown" -python.go.color_number = "yellow,magenta" -python.go.color_number_selected = "yellow,red" -python.go.message = "Go to: " -python.go.short_name = "off" -python.go.sort = "number,beginning" -python.go.use_core_instead_weechat = "off" -python.shortenurl.color = "red" -python.shortenurl.ignore_list = "http://is.gd,http://tinyurl.com" -python.shortenurl.short_own = "off" -python.shortenurl.shortener = "isgd" -python.shortenurl.urllength = "80" -ruby.check_license = "off" -tcl.check_license = "off" - -[desc] -python.go.auto_jump = "automatically jump to buffer when it is uniquely selected (default: "off")" -python.go.color_name = "color for buffer name (not selected) (default: "black,cyan")" -python.go.color_name_highlight = "color for highlight in buffer name (not selected) (default: "red,cyan")" -python.go.color_name_highlight_selected = "color for highlight in a selected buffer name (default: "red,brown")" -python.go.color_name_selected = "color for a selected buffer name (default: "black,brown")" -python.go.color_number = "color for buffer number (not selected) (default: "yellow,magenta")" -python.go.color_number_selected = "color for selected buffer number (default: "yellow,red")" -python.go.message = "message to display before list of buffers (default: "Go to: ")" -python.go.short_name = "display and search in short names instead of buffer name (default: "off")" -python.go.sort = "comma-separated list of keys to sort buffers (the order is important, sorts are performed in the given order): name = sort by name (or short name), (default: "number,beginning")" -python.go.use_core_instead_weechat = "use name "core" instead of "weechat" for core buffer (default: "off")" diff --git a/weechat/python/autoload/wee_slack.py b/weechat/python/autoload/wee_slack.py new file mode 120000 index 0000000..a77c3bb --- /dev/null +++ b/weechat/python/autoload/wee_slack.py @@ -0,0 +1 @@ +../wee_slack.py \ No newline at end of file diff --git a/weechat/python/wee_slack.py b/weechat/python/wee_slack.py new file mode 100644 index 0000000..cd51444 --- /dev/null +++ b/weechat/python/wee_slack.py @@ -0,0 +1,1696 @@ +# -*- coding: utf-8 -*- +# + +from functools import wraps +import time +import json +import pickle +import sha +import re +import urllib +import urlparse +import HTMLParser +import sys +from websocket import create_connection + +# hack to make tests possible.. better way? +try: + import weechat as w +except: + pass + +SCRIPT_NAME = "slack_extension" +SCRIPT_AUTHOR = "Ryan Huber " +SCRIPT_VERSION = "0.97.24" +SCRIPT_LICENSE = "MIT" +SCRIPT_DESC = "Extends weechat for typing notification/search/etc on slack.com" + +BACKLOG_SIZE = 200 + +SLACK_API_TRANSLATOR = { + "channel": { + "history": "channels.history", + "join": "channels.join", + "leave": "channels.leave", + "mark": "channels.mark", + "info": "channels.info", + }, + "im": { + "history": "im.history", + "leave": "im.close", + "mark": "im.mark", + }, + "group": { + "history": "groups.history", + "join": "channels.join", + "leave": "groups.leave", + "mark": "groups.mark", + } + +} + +def dbg(message, fout=False, main_buffer=False): + 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("", message) + else: + if slack_debug is not None: + w.prnt(slack_debug, message) + +# hilarious, i know + + +class Meta(list): + + def __init__(self, attribute, search_list): + self.attribute = attribute + self.search_list = search_list + + def __str__(self): + string = '' + for each in self.search_list.get_all(self.attribute): + string += "{} ".format(each) + return string + + def __repr__(self): + self.search_list.get_all(self.attribute) + + def __getitem__(self, index): + things = self.get_all() + return things[index] + + def __iter__(self): + things = self.get_all() + for channel in things: + yield channel + + def get_all(self): + items = [] + items += self.search_list.get_all(self.attribute) + return items + + def find(self, name): + items = self.search_list.find_deep(name, self.attribute) + items = [x for x in items if x is not None] + if len(items) == 1: + return items[0] + elif len(items) == 0: + pass + else: + dbg("probably something bad happened with meta items: {}".format(items)) + return items + #raise AmbiguousProblemError + + def find_first(self, name): + items = self.find(name) + if items.__class__ == list: + return items[0] + else: + return False + + def find_by_class(self, class_name): + items = self.search_list.find_by_class_deep(class_name, self.attribute) + return items + + +class SearchList(list): + + def find(self, name): + items = [] + for child in self: + if child.__class__ == self.__class__: + items += child.find(name) + else: + if child == name: + items.append(child) + if len(items) == 1: + return items[0] + elif items != []: + return items + + def find_deep(self, name, attribute): + items = [] + for child in self: + if child.__class__ == self.__class__: + if items is not None: + items += child.find_deep(name, attribute) + elif dir(child).count('find') == 1: + if items is not None: + items.append(child.find(name, attribute)) + if items != []: + return items + + def get_all(self, attribute): + items = [] + for child in self: + if child.__class__ == self.__class__: + items += child.get_all(attribute) + else: + items += (eval("child." + attribute)) + return items + + 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 + + +class SlackServer(object): + + def __init__(self, token): + self.nick = None + self.name = None + self.domain = None + self.login_data = None + self.buffer = None + self.token = token + self.ws = None + self.ws_hook = None + self.users = SearchList() + self.channels = SearchList() + self.connecting = False + self.connected = False + self.communication_counter = 0 + self.message_buffer = {} + self.ping_hook = None + self.failed_message = None + + self.identifier = None + self.connect_to_slack() + + def __eq__(self, compare_str): + if compare_str == self.identifier or compare_str == self.token or compare_str == self.buffer: + return True + else: + return False + + def __str__(self): + return "{}".format(self.identifier) + + def __repr__(self): + return "{}".format(self.identifier) + + def find(self, name, attribute): + attribute = eval("self." + attribute) + return attribute.find(name) + + def get_communication_id(self): + if self.communication_counter > 999: + self.communication_counter = 0 + self.communication_counter += 1 + return self.communication_counter + + def send_to_websocket(self, data): + data["id"] = self.get_communication_id() + message = json.dumps(data) + try: + self.message_buffer[data["id"]] = data + self.ws.send(message) + dbg("Sent {}...".format(message[:100])) + except: + self.failed_message = data + dbg("Unexpected error: {}\nSent: {}".format(sys.exc_info()[0], self.failed_message)) + self.connected = False + + def ping(self): + request = {"type": "ping"} + self.send_to_websocket(request) + + def connect_to_slack(self): + t = time.time() + if not self.connecting: + async_slack_api_request("slack.com", self.token, "rtm.start", {"ts": t}) + self.connecting = True + + def connected_to_slack(self, login_data): + if login_data["ok"]: + self.domain = login_data["team"]["domain"] + ".slack.com" + dbg("connected to {}".format(self.domain)) + self.identifier = 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) and 0 or len(self.channels) == 0: + self.create_slack_mappings(login_data) + + self.connected = True + self.connecting = False + + self.print_connection_info(login_data) + if self.failed_message: + dbg("Resent failed message.") + self.send_to_websocket(self.failed_message) + self.failed_message = None + return True + else: + w.prnt("", "\n!! slack.com login error: " + login_data["error"] + "\n Please check your API token with\n \"/set plugins.var.python.slack_extension.slack_api_token (token)\"\n\n ") + self.connected = False + + def print_connection_info(self, login_data): + self.buffer_prnt('Connected to Slack', backlog=True) + self.buffer_prnt('{:<20} {}'.format("Websocket URL", login_data["url"]), backlog=True) + self.buffer_prnt('{:<20} {}'.format("User name", login_data["self"]["name"]), backlog=True) + self.buffer_prnt('{:<20} {}'.format("User ID", login_data["self"]["id"]), backlog=True) + self.buffer_prnt('{:<20} {}'.format("Team name", login_data["team"]["name"]), backlog=True) + self.buffer_prnt('{:<20} {}'.format("Team domain", login_data["team"]["domain"]), backlog=True) + self.buffer_prnt('{:<20} {}'.format("Team id", login_data["team"]["id"]), backlog=True) + + def create_local_buffer(self): + if not w.buffer_search("", self.domain): + self.buffer = w.buffer_new(self.domain, "buffer_input_cb", "", "", "") + 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) + 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: + return False + + def create_slack_mappings(self, data): + + for item in data["users"]: + self.users.append(User(self, item["name"], item["id"], item["presence"])) + + for item in data["channels"]: + if "last_read" not in item: + item["last_read"] = 0 + if "members" not in item: + item["members"] = [] + if "topic" not in item: + item["topic"] = {} + item["topic"]["value"] = "" + if not item["is_archived"]: + self.channels.append(Channel(self, item["name"], item["id"], item["is_member"], item["last_read"], "#", item["members"], item["topic"]["value"])) + for item in data["groups"]: + if "last_read" not in item: + item["last_read"] = 0 + if not item["is_archived"]: + self.channels.append(GroupChannel(self, item["name"], item["id"], item["is_open"], item["last_read"], "#", item["members"], item["topic"]["value"])) + for item in data["ims"]: + if "last_read" not in item: + item["last_read"] = 0 + name = self.users.find(item["user"]).name + self.channels.append(DmChannel(self, name, item["id"], item["is_open"], item["last_read"])) + + 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" + else: + tags = "" + if self.buffer: + w.prnt_date_tags(self.buffer, 0, tags, "{}\t{}".format(user, message)) + else: + pass + #w.prnt("", "%s\t%s" % (user, message)) + + +class SlackThing(object): + + def __init__(self, name, identifier): + self.name = name + self.identifier = identifier + self.channel_buffer = None + + def __str__(self): + return self.name + + def __repr__(self): + return self.name + + +def buffer_input_cb(b, buffer, data): + if not data.startswith('s/'): + channel = channels.find(buffer) + channel.send_message(data) + channel.buffer_prnt(channel.server.nick, data) + elif data.count('/') == 3: + old, new = data.split('/')[1:3] + channel = channels.find(buffer) + channel.change_previous_message(old, new) + channel.mark_read(True) + return w.WEECHAT_RC_ERROR + + +class Channel(SlackThing): + + def __init__(self, server, name, identifier, active, last_read=0, prepend_name="", members=[], topic=""): + super(Channel, self).__init__(name, identifier) + self.type = "channel" + self.server = server + self.name = prepend_name + self.name + self.typing = {} + self.active = active + self.opening = False + self.members = set(members) + self.topic = topic + self.last_read = float(last_read) + self.last_received = None + if active: + self.create_buffer() + self.attach_buffer() + self.update_nicklist() + self.set_topic(self.topic) + buffer_list_update_next() + + 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 create_buffer(self): + channel_buffer = w.buffer_search("", "{}.{}".format(self.server.domain, self.name)) + if channel_buffer: + self.channel_buffer = channel_buffer + else: + self.channel_buffer = w.buffer_new("{}.{}".format(self.server.domain, 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') + w.buffer_set(self.channel_buffer, "short_name", 'loading..') + + def attach_buffer(self): + channel_buffer = w.buffer_search("", "{}.{}".format(self.server.domain, self.name)) + if channel_buffer != main_weechat_buffer: + self.channel_buffer = channel_buffer +# w.buffer_set(self.channel_buffer, "highlight_words", self.server.nick) + else: + self.channel_buffer = None + + def detach_buffer(self): + if self.channel_buffer is not None: + w.buffer_close(self.channel_buffer) + self.channel_buffer = None + + def update_nicklist(self): + if self.channel_buffer: + w.buffer_set(self.channel_buffer, "nicklist", "1") + w.nicklist_remove_all(self.channel_buffer) + try: + for user in self.members: + user = self.server.users.find(user) + if user.presence == 'away': + w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, " ", "", 1) + else: + w.nicklist_add_nick(self.channel_buffer, "", user.name, user.color_name, "+", "", 1) + except: + print "DEBUG: {} {}".format(self.identifier,self.name) + + def fullname(self): + return "{}.{}".format(self.server.domain, self.name) + + def has_user(self, name): + return name in self.members + + def user_join(self, name): + self.members.add(name) + self.update_nicklist() + + def user_leave(self, name): + if name in self.members: + self.members.remove(name) + self.update_nicklist() + + def set_active(self): + self.active = True + + def set_inactive(self): + self.active = False + + def set_typing(self, user): + 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 + + def send_message(self, message): + message = self.linkify_text(message) + dbg(message) + request = {"type": "message", "channel": self.identifier, "text": message, "myserver": self.server.domain} + self.server.send_to_websocket(request) + + def linkify_text(self, message): + message = message.split(' ') + for item in enumerate(message): + if item[1].startswith('@'): + named = re.match('.*[@#](\w+)(\W*)', item[1]).groups() + if named[0] in ["group", "channel"]: + message[item[0]] = "".format(named[0]) + if self.server.users.find(named[0]): + message[item[0]] = "<@{}>{}".format(self.server.users.find(named[0]).identifier, named[1]) + if item[1].startswith('#') and self.server.channels.find(item[1]): + named = re.match('.*[@#](\w+)(\W*)', item[1]).groups() + if self.server.channels.find(named[0]): + message[item[0]] = "<#{}>{}".format(self.server.channels.find(named[0]).identifier, named[1]) + dbg(message) + return " ".join(message) + + def set_topic(self, topic): + topic = topic.encode('ascii', 'ignore') + 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() + 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("#")}) + self.opening = False + + def close(self, update_remote=True): + #remove from cache so messages don't reappear when reconnecting + if self.active: + self.active = False + self.detach_buffer() + if update_remote: + t = time.time() + async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["leave"], {"channel": self.identifier}) + + def closed(self): + try: + message_cache.pop(self.identifier) + except KeyError: + pass + self.channel_buffer = None + self.last_received = None + self.close() + + 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 get_typing_list(self): + typing = [] + for user in self.typing.keys(): + if self.typing[user] + 4 > time.time(): + typing.append(user) + return typing + + def mark_read(self, update_remote=True): + t = time.time() + + 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}) + + def rename(self): + if current_domain_name() != self.server.domain and channels_not_on_current_server_color: + color = w.color(channels_not_on_current_server_color) + else: + color = "" + if self.is_someone_typing(): + new_name = ">{}".format(self.name[1:]) + else: + new_name = self.name + if self.channel_buffer: + if w.buffer_get_string(self.channel_buffer, "short_name") != (color + new_name): + w.buffer_set(self.channel_buffer, "short_name", color + new_name) + + def buffer_prnt_changed(self, user, text, time, append=""): + 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) + return False + + def buffer_prnt(self, user='unknown user', message='no message', time=0): + set_read_marker = False + time_float = float(time) + 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" + elif user != self.server.nick and self.name in self.server.users: + tags = "notify_private,notify_message" + else: + tags = "notify_message" + time_int = int(time_float) + if self.channel_buffer: + if self.server.users.find(user): + name = self.server.users.find(user).formatted_name() + else: + name = user + name = name.decode('utf-8') + #colorize nicks in each line + chat_color = w.config_string(w.config_get('weechat.color.chat')) + message = message.decode('UTF-8', 'replace') + for user in self.server.users: + if user.name in message: + message = user.name_regex.sub( + r'\1\2{}\3'.format(user.formatted_name() + w.color(chat_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) + else: + self.open(False) + self.last_received = time + self.unset_typing(user) + + def change_previous_message(self, old, new): + 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']}) + else: + new_message = message["text"].replace(old, new) + async_slack_api_request(self.server.domain, self.server.token, 'chat.update', {"channel": self.identifier, "ts": message['ts'], "text": new_message}) + + def my_last_message(self): + for message in reversed(message_cache[self.identifier]): + if "user" in message and "text" in message and message["user"] == self.server.users.find(self.server.nick).identifier: + return message + + def get_history(self): + if self.active: + if self.identifier in message_cache.keys(): + for message in message_cache[self.identifier]: + process_message(message) + 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}) + else: + async_slack_api_request(self.server.domain, self.server.token, SLACK_API_TRANSLATOR[self.type]["history"], {"channel": self.identifier, "count": BACKLOG_SIZE}) + + +class GroupChannel(Channel): + + def __init__(self, server, name, identifier, active, last_read=0, prepend_name="", members=[], topic=""): + super(GroupChannel, self).__init__(server, name, identifier, active, last_read, prepend_name, members, topic) + self.type = "group" + + +class DmChannel(Channel): + + def __init__(self, server, name, identifier, active, last_read=0, prepend_name=""): + super(DmChannel, self).__init__(server, name, identifier, active, last_read, prepend_name) + self.type = "im" + + def rename(self): + if current_domain_name() != self.server.domain and channels_not_on_current_server_color: + force_color = w.color(channels_not_on_current_server_color) + else: + force_color = None + + if self.server.users.find(self.name).presence == "active": + new_name = self.server.users.find(self.name).formatted_name('+', force_color) + else: + new_name = self.server.users.find(self.name).formatted_name(' ', force_color) + + if self.channel_buffer: + w.buffer_set(self.channel_buffer, "short_name", new_name) + + +class User(SlackThing): + + def __init__(self, server, name, identifier, presence="away"): + super(User, self).__init__(name, identifier) + self.channel_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, self.name)) + self.presence = presence + self.server = server + self.update_color() + self.name_regex = re.compile(r"([\W]|\A)(@{0,1})" + self.name + "('s|[^'\w]|\Z)") + if self.presence == 'away': + self.nicklist_pointer = w.nicklist_add_nick(server.buffer, "", self.name, self.color_name, " ", "", 0) + else: + self.nicklist_pointer = w.nicklist_add_nick(server.buffer, "", self.name, self.color_name, "+", "", 1) +# w.nicklist_add_nick(server.buffer, "", self.formatted_name(), "", "", "", 1) + + def __eq__(self, compare_str): + if compare_str == self.name or compare_str == "@" + self.name or compare_str == self.identifier: + return True + else: + return False + + def set_active(self): + self.presence = "active" + for channel in self.server.channels: + if channel.has_user(self.identifier): + channel.update_nicklist() + w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "prefix", "+") + w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "1") + 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() + w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "prefix", " ") + w.nicklist_nick_set(self.server.buffer, self.nicklist_pointer, "visible", "0") + buffer_list_update_next() + + def update_color(self): + if colorize_nicks: + self.color = w.info_get('irc_nick_color', self.name) + self.color_name = w.info_get('irc_nick_color_name', self.name) + else: + self.color = "" + self.color_name = "" + + def formatted_name(self, prepend="", force_color=None): + if colorize_nicks: + if self.name == self.server.nick: + return prepend + self.name + elif not force_color: + print_color = self.color + else: + print_color = force_color + return print_color + prepend + self.name + else: + return 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}) + + +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: + command = cmds[function_name](current_buffer, args) + except KeyError: + w.prnt("", "Command not found: " + function_name) + + return w.WEECHAT_RC_OK + + +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) + message = message.encode('utf-8') + buffer_input_cb("", current_buffer, message) + return w.WEECHAT_RC_OK + + +def join_command_cb(data, current_buffer, args): + if channels.find(current_buffer) or servers.find(current_buffer): + channel = args.split()[1] + servers.find(current_domain_name()).channels.find(channel).open() + return w.WEECHAT_RC_OK_EAT + else: + return w.WEECHAT_RC_OK + + +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 + return f(current_buffer, *args, **kwargs) + return wrapper + + +@slack_buffer_required +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() + + +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 != None: + servers.find(domain).channels.find(args).open() + else: + w.prnt(current_buffer, "Channel not found.") + + +@slack_buffer_required +def command_channels(current_buffer, args): + """ + List all the channels for the slack instance (name, id, active) + /slack channels + """ + 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 distracting_channels[0] != "": + for channel in distracting_channels: + try: + w.buffer_set(channels.find(channel).channel_buffer, "hidden", str(int(hide_distractions))) + except: + dbg("Can't hide channel {}".format(channel), main_buffer=True) + + +@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 command_setallreadmarkers(current_buffer, args): + """ + Sets the read marker for all channels + /slack setallreadmarkers + """ + 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!") + + +@slack_buffer_required +def command_away(current_buffer, args): + """ + Sets your status as 'away' + /slack away + """ + server = servers.find(current_domain_name()) + async_slack_api_request(server.domain, server.token, 'presence.set', {"presence": "away"}) + + +@slack_buffer_required +def command_back(current_buffer, args): + """ + Sets your status as 'back' + /slack back + """ + 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() + +def command_cacheinfo(current_buffer, args): + for channel in message_cache.keys(): + c = channels.find(channel) + w.prnt("", "{} {}".format(channels.find(channel), len(message_cache[channel]))) +# server.buffer_prnt("{} {}".format(channels.find(channel), len(message_cache[channel]))) + +def command_flushcache(current_buffer, args): + global message_cache + message_cache = {} + cache_write_cb("","") + +def command_uncache(current_buffer, args): + identifier = channels.find(current_buffer).identifier + message_cache.pop(identifier) + 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 = w.config_get_plugin('trigger_value') + if trigger != "0": + if args is None: + channel = channels.find(current_buffer) + url = "{}/messages/{}".format(channel.server.domain, 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})) + 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 + +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['myserver'] = server + except: + 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) + # dbg(function_name) + except KeyError: + if function_name: + dbg("Function not implemented: {}\n{}".format(function_name, message_json)) + else: + dbg("Function not implemented\n{}".format(message_json)) + w.bar_item_update("slack_typing_notice") + return w.WEECHAT_RC_OK + +def process_reply(message_json): + server = servers.find(message_json["myserver"]) + identifier = message_json["reply_to"] + item = server.message_buffer.pop(identifier) + if "type" in item: + if item["type"] == "message": + item["ts"] = message_json["ts"] + cache_message(item, from_me=True) + dbg("REPLY {}".format(item)) + +def process_pong(message_json): + pass + + +def process_team_join(message_json): + server = servers.find(message_json["myserver"]) + item = message_json["user"] + server.users.append(User(server, item["name"], item["id"], item["presence"])) + server.buffer_prnt(server.buffer, "New user joined: {}".format(item["name"])) + + +def process_presence_change(message_json): + buffer_name = "{}.{}".format(domain, message_json["user"]) + buf_ptr = w.buffer_search("", buffer_name) + if message_json["presence"] == 'active': + users.find(message_json["user"]).set_active() + else: + users.find(message_json["user"]).set_inactive() + + +def process_channel_marked(message_json): + channel = channels.find(message_json["channel"]) + channel.mark_read(False) + if not legacy_mode: + w.buffer_set(channel.channel_buffer, "hotlist", "-1") + + +def process_group_marked(message_json): + channel = channels.find(message_json["channel"]) + channel.mark_read(False) + if not legacy_mode: + w.buffer_set(channel.channel_buffer, "hotlist", "-1") + + +def process_channel_created(message_json): + server = servers.find(message_json["myserver"]) + item = message_json["channel"] + if server.channels.find(message_json["channel"]["name"]): + server.channels.find(message_json["channel"]["name"]).open(False) + else: + item = message_json["channel"] + server.channels.append(Channel(server, item["name"], item["id"], item["is_open"], item["last_read"], "#", item["members"], item["topic"]["value"])) + server.buffer_prnt("New channel created: {}".format(item["name"])) + + +def process_channel_left(message_json): + server = servers.find(message_json["myserver"]) + server.channels.find(message_json["channel"]).close(False) + + +def process_channel_join(message_json): + server = servers.find(message_json["myserver"]) + channel = server.channels.find(message_json["channel"]) + channel.user_join(message_json["user"]) + + +def process_channel_topic(message_json): + server = servers.find(message_json["myserver"]) + channel = server.channels.find(message_json["channel"]) + channel.set_topic(message_json["topic"]) + + +def process_channel_joined(message_json): + server = servers.find(message_json["myserver"]) + if server.channels.find(message_json["channel"]["name"]): + server.channels.find(message_json["channel"]["name"]).open(False) + else: + item = message_json["channel"] + server.channels.append(Channel(server, item["name"], item["id"], item["is_open"], item["last_read"], "#", item["members"], item["topic"]["value"])) + + +def process_channel_leave(message_json): + server = servers.find(message_json["myserver"]) + channel = server.channels.find(message_json["channel"]) + channel.user_leave(message_json["user"]) + + +def process_group_left(message_json): + server = servers.find(message_json["myserver"]) + server.channels.find(message_json["channel"]).close(False) + + +def process_group_joined(message_json): + server = servers.find(message_json["myserver"]) + if server.channels.find(message_json["channel"]["name"]): + server.channels.find(message_json["channel"]["name"]).open(False) + else: + item = message_json["channel"] + server.channels.append(GroupChannel(server, item["name"], item["id"], item["is_open"], item["last_read"], "#", item["members"], item["topic"]["value"])) + + +def process_im_close(message_json): + server = servers.find(message_json["myserver"]) + 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) + + +def process_im_marked(message_json): + channel = channels.find(message_json["channel"]) + channel.mark_read(False) + if not legacy_mode: + w.buffer_set(channel.channel_buffer, "hotlist", "-1") + + +def process_im_created(message_json): + server = servers.find(message_json["myserver"]) + 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) + else: + item = message_json["channel"] + server.channels.append(DmChannel(server, channel_name, item["id"], item["is_open"], item["last_read"])) + server.buffer_prnt("New channel created: {}".format(item["name"])) + + +def process_user_typing(message_json): + server = servers.find(message_json["myserver"]) + server.channels.find(message_json["channel"]).set_typing(server.users.find(message_json["user"]).name) + +# todo: does this work? + + +def process_error(message_json): + pass + #connected = False + +# def process_message_changed(message_json): +# process_message(message_json) + +def cache_message(message_json, from_me=False): + global message_cache + if from_me: + server = channels.find(message_json["channel"]).server + message_json["user"] = server.users.find(server.nick).identifier + channel = message_json["channel"] + if channel not in message_cache: + message_cache[channel] = [] + if message_json not in message_cache[channel]: + message_cache[channel].append(message_json) + if len(message_cache[channel]) > BACKLOG_SIZE: + message_cache[channel] = message_cache[channel][-BACKLOG_SIZE:] + + +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 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 process_message(message_json): + 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 + + cache_message(message_json) + + time = message_json['ts'] + if "fallback" in message_json: + text = message_json["fallback"] + elif "text" in message_json: + text = message_json["text"] + else: + text = "" + + text = unfurl_refs(text) + if "attachments" in message_json: + text += u" --- {}".format(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 "subtype" in message_json and message_json["subtype"] == "message_changed": + if "edited" in message_json["message"]: + append = " (edited)" + else: + append = '' + channel.buffer_prnt_changed(message_json["message"]["user"], text, message_json["message"]["ts"], append) + elif "subtype" in message_json and message_json["subtype"] == "message_deleted": + append = "(deleted)" + text = "" + channel.buffer_prnt_changed(None, text, message_json["deleted_ts"], append) + else: + channel.buffer_prnt(name, text, time) + except: + dbg("cannot process message {}".format(message_json)) + + +def unwrap_message(message_json): + 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 "text" in message_json: + message_json["text"] += message_json["message"]["text"] + dbg("added text!") + else: + message_json["text"] = message_json["message"]["text"] + if "fallback" in message_json["message"]: + if "fallback" in message_json: + message_json["fallback"] += message_json["message"]["fallback"] + else: + message_json["fallback"] = message_json["message"]["fallback"] + return message_json + + +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') + return attachment_text + + +def unfurl_refs(text): + if text.find('<') > -1: + newtext = [] + text = text.split(" ") + for item in text: + # dbg(item) + start = item.find('<') + end = item.find('>') + if start > -1 and end > -1: + item = item[start + 1:end] + if item.find('|') > -1: + item = item.split('|')[0] + if item.startswith('@U'): + if users.find(item[1:]): + try: + item = "@{}".format(users.find(item[1:]).name) + except: + dbg("NAME: {}".format(item)) + if item.startswith('#C'): + if channels.find(item[1:]): + item = "{}".format(channels.find(item[1:]).name) + newtext.append(item) + text = " ".join(newtext) + return text + else: + return text + + +def get_user(message_json, server): + if 'user' in message_json: + name = server.users.find(message_json['user']).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"]) + elif 'bot_id' in message_json: + name = u"-{}-".format(message_json["bot_id"]) + else: + name = u"" + return name + +# END Websocket handling methods + + +def typing_bar_item_cb(data, buffer, args): + typers = [x for x in channels.get_all() 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.find_by_class(Channel) + channels.find_by_class(GroupChannel): + 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 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) + 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 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() + + channel_name = current_buffer_name() + previous_buffer = data + return w.WEECHAT_RC_OK + + +def typing_notification_cb(signal, sig_type, data): + if len(w.buffer_get_string(data, "input")) > 8: + 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) + typing_timer = now + return w.WEECHAT_RC_OK + +# NOTE: figured i'd do this because they do + + +def slack_ping_cb(data, remaining): + servers.find(data).ping() + return w.WEECHAT_RC_OK + + +def slack_connection_persistence_cb(data, remaining_calls): + 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 + + +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) + return w.WEECHAT_RC_OK + +# Slack specific requests + +# NOTE: switched to async/curl because sync slowed down the UI + + +def async_slack_api_request(domain, token, request, post_data, priority=False): + if not STOP_TALKING_TO_SLACK: + post_data["token"] = token + url = 'https://{}/api/{}'.format(domain, request) + command = 'curl -A "wee_slack {}" -s --data "{}" {}'.format(SCRIPT_VERSION, urllib.urlencode(post_data), url) + context = pickle.dumps({"request": request, "token": token, "post_data": post_data}) + w.hook_process(command, 20000, "url_processor_cb", context) + +# funny, right? +big_data = {} + +def url_processor_cb(data, command, return_code, out, err): + global big_data, message_cache + 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("curl failed, doing again...") + dbg("curl length: {} identifier {}\n{}".format(len(big_data[identifier]), identifier, data)) + my_json = False + + big_data.pop(identifier, None) + + if my_json: + if data["request"] == 'rtm.start': + servers.find(data["token"]).connected_to_slack(my_json) + + else: + if "channel" in data["post_data"]: + channel = data["post_data"]["channel"] + token = data["token"] + if "messages" in my_json: + messages = my_json["messages"].reverse() + for message in my_json["messages"]: + message["myserver"] = 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"]) + elif return_code != -1: + big_data.pop(identifier, None) + dbg("return code: {}".format(return_code)) + + return w.WEECHAT_RC_OK + +def cache_write_cb(data, remaining): + open("{}/{}".format(WEECHAT_HOME, CACHE_NAME), 'w').write(json.dumps(message_cache)) + return w.WEECHAT_RC_OK + + + +# END Slack specific requests + +# Utility Methods + + +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 + + +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 + + +def closed_slack_buffer_cb(data, buffer): + global slack_buffer + slack_buffer = None + return w.WEECHAT_RC_OK + + +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 + + +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") + + +def config_changed_cb(data, option, value): + global slack_api_token, distracting_channels, channels_not_on_current_server_color, colorize_nicks, slack_debug, debug_mode + slack_api_token = w.config_get_plugin("slack_api_token") + + if slack_api_token.startswith('${sec.data'): + slack_api_token = w.string_eval_expression(slack_api_token, {}, {}, {}) + + distracting_channels = [x.strip() for x in w.config_get_plugin("distracting_channels").split(',')] + channels_not_on_current_server_color = w.config_get_plugin("channels_not_on_current_server_color") + if channels_not_on_current_server_color == "0": + channels_not_on_current_server_color = False + colorize_nicks = w.config_get_plugin('colorize_nicks') == "1" + debug_mode = w.config_get_plugin("debug_mode").lower() + if debug_mode != '' and debug_mode != 'false': + create_slack_debug_buffer() + return w.WEECHAT_RC_OK + +def quit_notification_cb(signal, sig_type, data): + global STOP_TALKING_TO_SLACK + STOP_TALKING_TO_SLACK = True + cache_write_cb("", "") + return w.WEECHAT_RC_OK + +def script_unloaded(): + global STOP_TALKING_TO_SLACK + STOP_TALKING_TO_SLACK = True + cache_write_cb("", "") + return w.WEECHAT_RC_OK + +# END Utility Methods + +# Main +if __name__ == "__main__": + if w.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, + SCRIPT_DESC, "script_unloaded", ""): + + WEECHAT_HOME = w.info_get("weechat_dir", "") + CACHE_NAME = "slack.cache" + STOP_TALKING_TO_SLACK = False + + if not w.config_get_plugin('slack_api_token'): + w.config_set_plugin('slack_api_token', "INSERT VALID KEY HERE!") + if not w.config_get_plugin('distracting_channels'): + w.config_set_plugin('distracting_channels', "") + if not w.config_get_plugin('channels_not_on_current_server_color'): + w.config_set_plugin('channels_not_on_current_server_color', "0") + if not w.config_get_plugin('debug_mode'): + w.config_set_plugin('debug_mode', "") + if not w.config_get_plugin('colorize_nicks'): + w.config_set_plugin('colorize_nicks', "1") + if not w.config_get_plugin('trigger_value'): + w.config_set_plugin('trigger_value', "0") + + version = w.info_get("version_number", "") or 0 + if int(version) >= 0x00040400: + legacy_mode = False + else: + legacy_mode = True + + # Global var section + slack_debug = None + config_changed_cb("", "", "") + + 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 + + buffer_list_update = False + previous_buffer_list_update = 0 + + #name = None + never_away = False + hide_distractions = False + hotlist = w.infolist_get("hotlist", "", "") + main_weechat_buffer = w.info_get("irc_buffer", "{}.{}".format(domain, "DOESNOTEXIST!@#$")) + + try: + cache_file = open("{}/{}".format(WEECHAT_HOME, CACHE_NAME), 'r') + message_cache = json.loads(cache_file.read()) + except (IOError, ValueError): + message_cache = {} + # End global var section + + #channels = SearchList() + servers = SearchList() + for token in slack_api_token.split(','): + servers.append(SlackServer(token)) + channels = Meta('channels', servers) + users = Meta('users', servers) + + 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, 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_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_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.bar_item_new('slack_typing_notice', 'typing_bar_item_cb', '') + # END attach to the weechat hooks we need