Commit 1d64a723 authored by Trygve Aaberge's avatar Trygve Aaberge Committed by Sébastien Helleu

slack.py 2.6.0: Merge with mainline

This is version 2.6.0 of slack.py (currently the most recent), copied
over from the wee-slack repo.
parent f86360d9
......@@ -11,6 +11,7 @@ from functools import partial, wraps
from io import StringIO
from itertools import chain, count, islice
import copy
import errno
import textwrap
import time
......@@ -41,9 +42,9 @@ except NameError: # Python 3
basestring = unicode = str
try:
from urllib.parse import urlencode
from urllib.parse import quote, urlencode
except ImportError:
from urllib import urlencode
from urllib import quote, urlencode
try:
from json import JSONDecodeError
......@@ -58,7 +59,7 @@ except ImportError:
SCRIPT_NAME = "slack"
SCRIPT_AUTHOR = "Ryan Huber <[email protected]>"
SCRIPT_VERSION = "2.5.0"
SCRIPT_VERSION = "2.6.0"
SCRIPT_LICENSE = "MIT"
SCRIPT_DESC = "Extends weechat for typing notification/search/etc on slack.com"
REPO_URL = "https://github.com/wee-slack/wee-slack"
......@@ -114,12 +115,14 @@ SLACK_API_TRANSLATOR = {
"history": None,
"join": None,
"leave": None,
"mark": None,
"mark": "subscriptions.thread.mark",
}
}
CONFIG_PREFIX = "plugins.var.python." + SCRIPT_NAME
###### Decorators have to be up here
......@@ -338,8 +341,14 @@ def handle_socket_error(exception, team, caller_name):
team.set_disconnected()
EMOJI_NAME_REGEX = re.compile(':([^: ]+):')
EMOJI_REGEX_STRING = '[\U00000080-\U0010ffff]+'
MESSAGE_ID_REGEX_STRING = r'(?P<msg_id>\d+|\$[0-9a-fA-F]{3,})'
REACTION_PREFIX_REGEX_STRING = r'{}?(?P<reaction_change>\+|-)'.format(MESSAGE_ID_REGEX_STRING)
EMOJI_CHAR_REGEX_STRING = '(?P<emoji_char>[\U00000080-\U0010ffff]+)'
EMOJI_NAME_REGEX_STRING = ':(?P<emoji_name>[a-z0-9_+-]+):'
EMOJI_CHAR_OR_NAME_REGEX_STRING = '({}|{})'.format(EMOJI_CHAR_REGEX_STRING, EMOJI_NAME_REGEX_STRING)
EMOJI_NAME_REGEX = re.compile(EMOJI_NAME_REGEX_STRING)
EMOJI_CHAR_OR_NAME_REGEX = re.compile(EMOJI_CHAR_OR_NAME_REGEX_STRING)
def regex_match_to_emoji(match, include_name=False):
......@@ -363,7 +372,12 @@ def replace_string_with_emoji(text):
def replace_emoji_with_string(text):
return EMOJI_WITH_SKIN_TONES_REVERSE.get(text, text)
emoji = None
key = text
while emoji is None and len(key):
emoji = EMOJI_WITH_SKIN_TONES_REVERSE.get(key)
key = key[:-1]
return emoji or text
###### New central Event router
......@@ -409,7 +423,7 @@ class EventRouter(object):
if not os.path.exists(RECORD_DIR):
os.makedirs(RECORD_DIR)
def record_event(self, message_json, file_name_field, subdir=None):
def record_event(self, message_json, team, file_name_field, subdir=None):
"""
complete
Called each time you want to record an event.
......@@ -417,10 +431,19 @@ class EventRouter(object):
file_name_field is the json key whose value you want to be part of the file name
"""
now = time.time()
if subdir:
directory = "{}/{}".format(RECORD_DIR, subdir)
if team:
team_subdomain = team.subdomain
else:
directory = RECORD_DIR
team_json = message_json.get('team')
if team_json:
team_subdomain = team_json.get('domain')
else:
team_subdomain = 'unknown_team'
directory = "{}/{}".format(RECORD_DIR, team_subdomain)
if subdir:
directory = "{}/{}".format(directory, subdir)
if not os.path.exists(directory):
os.makedirs(directory)
mtype = message_json.get(file_name_field, 'unknown')
......@@ -515,9 +538,9 @@ class EventRouter(object):
return w.WEECHAT_RC_OK
message_json = json.loads(data.decode('utf-8'))
message_json["wee_slack_metadata_team"] = team
if self.recording:
self.record_event(message_json, 'type', 'websocket')
self.record_event(message_json, team, 'type', 'websocket')
message_json["wee_slack_metadata_team"] = team
self.receive(message_json)
return w.WEECHAT_RC_OK
......@@ -546,7 +569,7 @@ class EventRouter(object):
try:
j["wee_slack_process_method"] = request_metadata.request_normalized
if self.recording:
self.record_event(j, 'wee_slack_process_method', 'http')
self.record_event(j, request_metadata.team, 'wee_slack_process_method', 'http')
j["wee_slack_request_metadata"] = request_metadata
self.reply_buffer.pop(request_metadata.response_id)
self.receive(j)
......@@ -570,8 +593,8 @@ class EventRouter(object):
retry_text = ('retrying' if request_metadata.should_try() else
'will not retry after too many failed attempts')
w.prnt('', ('Failed connecting to slack team with token {}, {}. ' +
'If this persists, try increasing slack_timeout. Error: {}')
.format(token_for_print(request_metadata.token), retry_text, err))
'If this persists, try increasing slack_timeout. Error (code {}): {}')
.format(token_for_print(request_metadata.token), retry_text, return_code, err))
dbg('rtm.start failed with return_code {}. stack:\n{}'
.format(return_code, ''.join(traceback.format_stack())), level=5)
self.receive(request_metadata)
......@@ -798,6 +821,7 @@ def buffer_input_callback(signal, buffer_ptr, data):
this includes add/remove reactions, modifying messages, and
sending messages.
"""
data = data.replace('\r', '\n')
eventrouter = eval(signal)
channel = eventrouter.weechat_controller.get_channel_from_buffer_ptr(buffer_ptr)
if not channel:
......@@ -811,22 +835,21 @@ def buffer_input_callback(signal, buffer_ptr, data):
else:
return int(message_id)
message_id_regex = r"(\d*|\$[0-9a-fA-F]{3,})"
reaction = re.match(r"^{}(\+|-)(:(.+):|{})\s*$".format(message_id_regex, EMOJI_REGEX_STRING), data)
substitute = re.match("^{}s/".format(message_id_regex), data)
reaction = re.match(r"{}{}\s*$".format(REACTION_PREFIX_REGEX_STRING, EMOJI_CHAR_OR_NAME_REGEX_STRING), data)
substitute = re.match("{}?s/".format(MESSAGE_ID_REGEX_STRING), data)
if reaction:
emoji_match = reaction.group(4) or reaction.group(3)
emoji = replace_emoji_with_string(emoji_match)
if reaction.group(2) == "+":
channel.send_add_reaction(get_id(reaction.group(1)), emoji)
elif reaction.group(2) == "-":
channel.send_remove_reaction(get_id(reaction.group(1)), emoji)
emoji = reaction.group("emoji_char") or reaction.group("emoji_name")
if reaction.group("reaction_change") == "+":
channel.send_add_reaction(get_id(reaction.group("msg_id")), emoji)
elif reaction.group("reaction_change") == "-":
channel.send_remove_reaction(get_id(reaction.group("msg_id")), emoji)
elif substitute:
msg_id = get_id(substitute.group(1))
msg_id = get_id(substitute.group("msg_id"))
try:
old, new, flags = re.split(r'(?<!\\)/', data)[1:]
except ValueError:
pass
print_error('Incomplete regex for changing a message, '
'it should be in the form s/old text/new text/')
else:
# Replacement string in re.sub() is a string, not a regex, so get
# rid of escapes.
......@@ -843,15 +866,13 @@ def buffer_input_callback(signal, buffer_ptr, data):
# Workaround for supporting multiline messages. It intercepts before the input
# callback is called, as this is called with the whole message, while it is
# normally split on newline before being sent to buffer_input_callback
# normally split on newline before being sent to buffer_input_callback.
# WeeChat only splits on newline, so we replace it with carriage return, and
# replace it back in buffer_input_callback.
def input_text_for_buffer_cb(data, modifier, current_buffer, string):
if current_buffer not in EVENTROUTER.weechat_controller.buffers:
return string
message = decode_from_utf8(string)
if not message.startswith("/") and "\n" in message:
buffer_input_callback("EVENTROUTER", current_buffer, message)
return ""
return string
return re.sub('\r?\n', '\r', decode_from_utf8(string))
@utf8_decode
......@@ -1056,9 +1077,8 @@ def emoji_completion_cb(data, completion_item, current_buffer, completion):
return w.WEECHAT_RC_OK
base_word = w.hook_completion_get_string(completion, "base_word")
if ":" not in base_word:
return w.WEECHAT_RC_OK
prefix = base_word.split(":")[0] + ":"
reaction = re.match(REACTION_PREFIX_REGEX_STRING + ":", base_word)
prefix = reaction.group(0) if reaction else ":"
for emoji in current_channel.team.emoji_completions:
w.hook_completion_list_add(completion, prefix + emoji + ":", 0, w.WEECHAT_LIST_POS_SORT)
......@@ -1170,9 +1190,10 @@ def stop_talking_to_slack():
which triggers leaving the channel because of how close
buffer is handled
"""
EVENTROUTER.shutdown()
for team in EVENTROUTER.teams.values():
team.ws.shutdown()
if 'EVENTROUTER' in globals():
EVENTROUTER.shutdown()
for team in EVENTROUTER.teams.values():
team.ws.shutdown()
return w.WEECHAT_RC_OK
##### New Classes
......@@ -1249,6 +1270,7 @@ class SlackTeam(object):
"""
def __init__(self, eventrouter, token, team_hash, websocket_url, team_info, subteams, nick, myidentifier, my_manual_presence, users, bots, channels, **kwargs):
self.slack_api_translator = copy.deepcopy(SLACK_API_TRANSLATOR)
self.identifier = team_info["id"]
self.active = True
self.team_hash = team_hash
......@@ -1494,7 +1516,7 @@ class SlackChannelCommon(object):
def send_change_reaction(self, method, msg_id, reaction):
if type(msg_id) is not int:
if msg_id in self.hashed_messages:
timestamp = str(self.hashed_messages[msg_id])
timestamp = self.hashed_messages[msg_id]
else:
return
elif 0 < msg_id <= len(self.messages):
......@@ -1502,7 +1524,17 @@ class SlackChannelCommon(object):
timestamp = next(islice(keys, msg_id - 1, None))
else:
return
data = {"channel": self.identifier, "timestamp": timestamp, "name": reaction}
reaction_name = replace_emoji_with_string(reaction)
if method == "toggle":
message = self.messages.get(timestamp)
reaction = message.get_reaction(reaction_name)
if reaction and self.team.myidentifier in reaction["users"]:
method = "reactions.remove"
else:
method = "reactions.add"
data = {"channel": self.identifier, "timestamp": timestamp, "name": reaction_name}
s = SlackRequest(self.team, method, data, channel=self, metadata={'reaction': reaction})
self.eventrouter.receive(s)
......@@ -1524,6 +1556,8 @@ class SlackChannelCommon(object):
s = SlackRequest(self.team, "chat.update",
{"channel": self.identifier, "ts": message['ts'], "text": new_message}, channel=self)
self.eventrouter.receive(s)
else:
print_error("The regex didn't match any part of the message")
def my_last_message(self, msg_id):
if type(msg_id) is not int:
......@@ -1597,6 +1631,23 @@ class SlackChannelCommon(object):
elif ts in self.messages:
return self.messages[ts].hash
def mark_read(self, ts=None, update_remote=True, force=False, post_data={}):
if self.new_messages or force:
if self.channel_buffer:
w.buffer_set(self.channel_buffer, "unread", "")
w.buffer_set(self.channel_buffer, "hotlist", "-1")
if not ts:
ts = next(reversed(self.messages), SlackTS())
if ts > self.last_read:
self.last_read = ts
if update_remote:
args = {"channel": self.identifier, "ts": ts}
args.update(post_data)
mark_method = self.team.slack_api_translator[self.type].get("mark")
if mark_method:
s = SlackRequest(self.team, mark_method, args, channel=self)
self.eventrouter.receive(s)
self.new_messages = False
class SlackChannel(SlackChannelCommon):
......@@ -1716,9 +1767,9 @@ class SlackChannel(SlackChannelCommon):
def open(self, update_remote=True):
if update_remote:
if "join" in SLACK_API_TRANSLATOR[self.type]:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["join"],
{"channel": self.identifier}, channel=self)
join_method = self.team.slack_api_translator[self.type].get("join")
if join_method:
s = SlackRequest(self.team, join_method, {"channel": self.identifier}, channel=self)
self.eventrouter.receive(s)
self.create_buffer()
self.active = True
......@@ -1795,15 +1846,15 @@ class SlackChannel(SlackChannelCommon):
w.buffer_set(self.channel_buffer, "localvar_set_server", self.team.preferred_name)
self.update_nicklist()
if "info" in SLACK_API_TRANSLATOR[self.type]:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["info"],
{"channel": self.identifier}, channel=self)
info_method = self.team.slack_api_translator[self.type].get("info")
if info_method:
s = SlackRequest(self.team, info_method, {"channel": self.identifier}, channel=self)
self.eventrouter.receive(s)
if self.type == "im":
if "join" in SLACK_API_TRANSLATOR[self.type]:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["join"],
{"users": self.user, "return_im": True}, channel=self)
join_method = self.team.slack_api_translator[self.type].get("join")
if join_method:
s = SlackRequest(self.team, join_method, {"users": self.user, "return_im": True}, channel=self)
self.eventrouter.receive(s)
def clear_messages(self):
......@@ -1816,7 +1867,7 @@ class SlackChannel(SlackChannelCommon):
self.channel_buffer = None
self.active = False
if update_remote and not self.eventrouter.shutting_down:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["leave"],
s = SlackRequest(self.team, self.team.slack_api_translator[self.type]["leave"],
{"channel": self.identifier}, channel=self)
self.eventrouter.receive(s)
......@@ -1893,7 +1944,7 @@ class SlackChannel(SlackChannelCommon):
self.clear_messages()
w.prnt_date_tags(self.channel_buffer, SlackTS().major,
tag(backlog=True, no_log=True), '\tgetting channel history...')
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["history"],
s = SlackRequest(self.team, self.team.slack_api_translator[self.type]["history"],
{"channel": self.identifier, "count": BACKLOG_SIZE}, channel=self, metadata={'clear': True})
if not slow_queue:
self.eventrouter.receive(s)
......@@ -1943,21 +1994,6 @@ class SlackChannel(SlackChannelCommon):
del self.typing[user]
return typing
def mark_read(self, ts=None, update_remote=True, force=False):
if self.new_messages or force:
if self.channel_buffer:
w.buffer_set(self.channel_buffer, "unread", "")
w.buffer_set(self.channel_buffer, "hotlist", "-1")
if not ts:
ts = next(reversed(self.messages), SlackTS())
if ts > self.last_read:
self.last_read = ts
if update_remote:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["mark"],
{"channel": self.identifier, "ts": ts}, channel=self)
self.eventrouter.receive(s)
self.new_messages = False
def user_joined(self, user_id):
# ugly hack - for some reason this gets turned into a list
self.members = set(self.members)
......@@ -2094,14 +2130,14 @@ class SlackDMChannel(SlackChannel):
def open(self, update_remote=True):
self.create_buffer()
self.get_history()
if "info" in SLACK_API_TRANSLATOR[self.type]:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["info"],
{"name": self.identifier}, channel=self)
info_method = self.team.slack_api_translator[self.type].get("info")
if info_method:
s = SlackRequest(self.team, info_method, {"name": self.identifier}, channel=self)
self.eventrouter.receive(s)
if update_remote:
if "join" in SLACK_API_TRANSLATOR[self.type]:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["join"],
{"users": self.user, "return_im": True}, channel=self)
join_method = self.team.slack_api_translator[self.type].get("join")
if join_method:
s = SlackRequest(self.team, join_method, {"users": self.user, "return_im": True}, channel=self)
self.eventrouter.receive(s)
def rename(self):
......@@ -2170,14 +2206,15 @@ class SlackMPDMChannel(SlackChannel):
self.create_buffer()
self.active = True
self.get_history()
if "info" in SLACK_API_TRANSLATOR[self.type]:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]["info"],
{"channel": self.identifier}, channel=self)
self.eventrouter.receive(s)
if update_remote and 'join' in SLACK_API_TRANSLATOR[self.type]:
s = SlackRequest(self.team, SLACK_API_TRANSLATOR[self.type]['join'],
{'users': ','.join(self.members)}, channel=self)
info_method = self.team.slack_api_translator[self.type].get("info")
if info_method:
s = SlackRequest(self.team, info_method, {"channel": self.identifier}, channel=self)
self.eventrouter.receive(s)
if update_remote:
join_method = self.team.slack_api_translator[self.type].get("join")
if join_method:
s = SlackRequest(self.team, join_method, {'users': ','.join(self.members)}, channel=self)
self.eventrouter.receive(s)
def set_name(self, slack_name):
self.name = slack_name
......@@ -2240,6 +2277,15 @@ class SlackThreadChannel(SlackChannelCommon):
self.members = self.parent_message.channel.members
self.team = self.parent_message.team
self.last_line_from = None
self.new_messages = False
@property
def last_read(self):
return self.parent_message.last_read
@last_read.setter
def last_read(self, ts):
self.parent_message.last_read = ts
@property
def identifier(self):
......@@ -2265,33 +2311,40 @@ class SlackThreadChannel(SlackChannelCommon):
def refresh(self):
self.rename()
def mark_read(self, ts=None, update_remote=True, force=False):
if self.channel_buffer:
w.buffer_set(self.channel_buffer, "unread", "")
w.buffer_set(self.channel_buffer, "hotlist", "-1")
def mark_read(self, ts=None, update_remote=True, force=False, post_data={}):
args = {"thread_ts": self.parent_message.ts}
args.update(post_data)
super(SlackThreadChannel, self).mark_read(ts=ts, update_remote=update_remote, force=force, post_data=args)
def buffer_prnt(self, nick, text, timestamp, tag_nick=None):
def buffer_prnt(self, nick, text, timestamp, history_message=False, tag_nick=None):
data = "{}\t{}".format(format_nick(nick, self.last_line_from), text)
self.last_line_from = nick
ts = SlackTS(timestamp)
if self.channel_buffer:
# backlog messages - we will update the read marker as we print these
backlog = ts <= self.last_read
if not backlog:
self.new_messages = True
if self.parent_message.channel.type in ["im", "mpim"]:
tagset = "dm"
else:
tagset = "channel"
no_log = history_message and backlog
self_msg = tag_nick == self.team.nick
tags = tag(tagset, user=tag_nick, self_msg=self_msg)
tags = tag(tagset, user=tag_nick, self_msg=self_msg, backlog=backlog, no_log=no_log)
w.prnt_date_tags(self.channel_buffer, ts.major, tags, data)
modify_last_print_time(self.channel_buffer, ts.minor)
if self_msg:
if backlog or self_msg:
self.mark_read(ts, update_remote=False, force=True)
def get_history(self):
self.got_history = True
for message in chain([self.parent_message], self.parent_message.submessages):
text = self.render(message)
self.buffer_prnt(message.sender, text, message.ts, tag_nick=message.sender_plain)
self.buffer_prnt(message.sender, text, message.ts, history_message=True, tag_nick=message.sender_plain)
if len(self.parent_message.submessages) < self.parent_message.number_of_replies():
s = SlackRequest(self.team, "conversations.replies",
{"channel": self.identifier, "ts": self.parent_message.ts},
......@@ -2432,6 +2485,8 @@ class SlackMessage(object):
senders = self.get_sender()
self.sender, self.sender_plain = senders[0], senders[1]
self.ts = SlackTS(message_json['ts'])
self.subscribed = message_json.get("subscribed", False)
self.last_read = message_json.get("last_read", SlackTS())
def __hash__(self):
return hash(self.ts)
......@@ -2488,8 +2543,8 @@ class SlackMessage(object):
if self.number_of_replies():
self.channel.hash_message(self.ts)
text += " " + colorize_string(get_thread_color(self.hash), "[ Thread: {} Replies: {} ]".format(
self.hash, self.number_of_replies()))
text += " " + colorize_string(get_thread_color(self.hash), "[ Thread: {} Replies: {}{} ]".format(
self.hash, self.number_of_replies(), " Subscribed" if self.subscribed else ""))
text = replace_string_with_emoji(text)
......@@ -2526,43 +2581,44 @@ class SlackMessage(object):
name_plain = "{}".format(self.team.bots[self.message_json["bot_id"]].formatted_name(enable_color=False))
return (name, name_plain)
def add_reaction(self, reaction, user):
m = self.message_json.get('reactions')
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({"name": reaction, "users": [user]})
def get_reaction(self, reaction_name):
for reaction in self.message_json.get("reactions", []):
if reaction["name"] == reaction_name:
return reaction
return None
def add_reaction(self, reaction_name, user):
reaction = self.get_reaction(reaction_name)
if reaction:
if user not in reaction["users"]:
reaction["users"].append(user)
else:
self.message_json["reactions"] = [{"name": reaction, "users": [user]}]
if "reactions" not in self.message_json:
self.message_json["reactions"] = []
self.message_json["reactions"].append({"name": reaction_name, "users": [user]})
def remove_reaction(self, reaction, user):
m = self.message_json.get('reactions')
if m:
for r in m:
if r["name"] == reaction and user in r["users"]:
r["users"].remove(user)
def remove_reaction(self, reaction_name, user):
reaction = self.get_reaction(reaction_name)
if user in reaction["users"]:
reaction["users"].remove(user)
def has_mention(self):
return w.string_has_highlight(unfurl_refs(self.message_json.get('text')),
",".join(self.channel.highlights()))
def number_of_replies(self):
return max(len(self.submessages), len(self.message_json.get("replies", [])))
return max(len(self.submessages), self.message_json.get("reply_count", 0))
def notify_thread(self, action=None, sender_id=None):
if config.auto_open_threads:
self.open_thread()
elif sender_id != self.team.myidentifier:
if sender_id != self.team.myidentifier and (config.notify_subscribed_threads == True or
config.notify_subscribed_threads == "auto" and not config.auto_open_threads and
not config.thread_messages_in_channel):
if action == "mention":
template = "You were mentioned in thread {hash}, channel {channel}"
elif action == "participant":
template = "New message in thread {hash}, channel {channel} in which you participated"
elif action == "response":
template = "New message in thread {hash} in response to own message in {channel}"
elif action == "subscribed":
template = "New message in thread {hash}, channel {channel} to which you are subscribed"
else:
template = "Notification for message in thread {hash}, channel {channel}"
message = template.format(hash=self.hash, channel=self.channel.formatted_name())
......@@ -2774,6 +2830,7 @@ def handle_channelsinfo(channel_json, eventrouter, team, channel, metadata):
def handle_groupsinfo(group_json, eventrouter, team, channel, metadatas):
channel.set_unread_count_display(group_json['group'].get('unread_count_display', 0))
channel.set_members(group_json['group']['members'])
def handle_conversationsopen(conversation_json, eventrouter, team, channel, metadata, object_name='channel'):
......@@ -2860,6 +2917,16 @@ def handle_chatcommand(json, eventrouter, team, channel, metadata):
.format(command, json['error'], response_text))
def handle_chatdelete(json, eventrouter, team, channel, metadata):
if not json['ok']:
print_error("Couldn't delete message: {}".format(json['error']))
def handle_chatupdate(json, eventrouter, team, channel, metadata):
if not json['ok']:
print_error("Couldn't change message: {}".format(json['error']))
def handle_reactionsadd(json, eventrouter, team, channel, metadata):
if not json['ok']:
print_error("Couldn't add reaction {}: {}".format(metadata['reaction'], json['error']))
......@@ -2870,6 +2937,30 @@ def handle_reactionsremove(json, eventrouter, team, channel, metadata):
print_error("Couldn't remove reaction {}: {}".format(metadata['reaction'], json['error']))
def handle_subscriptionsthreadmark(json, eventrouter, team, channel, metadata):
if not json["ok"]:
if json['error'] == 'not_allowed_token_type':
team.slack_api_translator['thread']['mark'] = None
else:
print_error("Couldn't set thread read status: {}".format(json['error']))
def handle_subscriptionsthreadadd(json, eventrouter, team, channel, metadata):
if not json["ok"]:
if json['error'] == 'not_allowed_token_type':
print_error("Can only subscribe to a thread when using a session token, see the readme: https://github.com/wee-slack/wee-slack#4-add-your-slack-api-tokens")
else:
print_error("Couldn't add thread subscription: {}".format(json['error']))
def handle_subscriptionsthreadremove(json, eventrouter, team, channel, metadata):
if not json["ok"]:
if json['error'] == 'not_allowed_token_type':
print_error("Can only unsubscribe from a thread when using a session token, see the readme: https://github.com/wee-slack/wee-slack#4-add-your-slack-api-tokens")
else:
print_error("Couldn't remove thread subscription: {}".format(json['error']))
###### New/converted process_ and subprocess_ methods
def process_hello(message_json, eventrouter, team, channel, metadata):
team.subscribe_users_presence()
......@@ -2934,7 +3025,7 @@ def process_pong(message_json, eventrouter, team, channel, metadata):
def process_message(message_json, eventrouter, team, channel, metadata, history_message=False):
if SlackTS(message_json["ts"]) in channel.messages:
if "ts" in message_json and SlackTS(message_json["ts"]) in channel.messages:
return
if "thread_ts" in message_json and "reply_count" not in message_json and "subtype" not in message_json:
......@@ -3008,6 +3099,7 @@ def download_files(message_json, team):
def subprocess_thread_message(message_json, eventrouter, team, channel, history_message):
dbg("THREAD MESSAGE {}".format(message_json))
parent_ts = message_json.get('thread_ts')
if parent_ts:
parent_message = channel.messages.get(SlackTS(parent_ts))
......@@ -3020,9 +3112,11 @@ def subprocess_thread_message(message_json, eventrouter, team, channel, history_
channel.change_message(parent_ts)
if parent_message.thread_channel and parent_message.thread_channel.active:
parent_message.thread_channel.buffer_prnt(message.sender, parent_message.thread_channel.render(message), message.ts, tag_nick=message.sender_plain)
parent_message.thread_channel.buffer_prnt(message.sender, parent_message.thread_channel.render(message), message.ts, history_message=history_message, tag_nick=message.sender_plain)
elif message.ts > channel.last_read and message.has_mention():
parent_message.notify_thread(action="mention", sender_id=message_json["user"])
elif message.ts > parent_message.last_read and parent_message.subscribed:
parent_message.notify_thread(action="subscribed", sender_id=message_json["user"])
if config.thread_messages_in_channel or message_json["subtype"] == "thread_broadcast":
thread_tag = "thread_broadcast" if message_json["subtype"] == "thread_broadcast" else "thread_message"
......@@ -3069,17 +3163,7 @@ subprocess_group_topic = subprocess_channel_topic
def subprocess_message_replied(message_json, eventrouter, team, channel, history_message):
parent_ts = message_json["message"].get("thread_ts")
parent_message = channel.messages.get(SlackTS(parent_ts))
# Thread exists but is not open yet
if parent_message is not None \
and not (parent_message.thread_channel and parent_message.thread_channel.active):
channel.hash_message(parent_ts)
last_message = max(message_json["message"]["replies"], key=lambda x: x["ts"])
if message_json["message"].get("user") == team.myidentifier:
parent_message.notify_thread(action="response", sender_id=last_message["user"])
elif any(team.myidentifier == r["user"] for r in message_json["message"]["replies"]):