Skip to content

Commit eeefe9d

Browse files
r3m0tgbin
authored andcommitted
Slack reactions and Message.extras['url'] (errbotio#985)
* add slack urls, emoji functions * add emoji functions to text backend * made add_reaction, remove_reaction idempotent
1 parent 28b6969 commit eeefe9d

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

docs/user_guide/plugin_development/backend_specifics.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@ Backend Mode value
4545
:class:`~errbot.backends.xmpp` xmpp
4646
============================================ ==========
4747

48+
Here's an example of using a backend-specific feature. In Slack, emoji reactions can be added to messages the bot
49+
receives using the `add_reaction` and `remove_reaction` methods. For example, you could add an hourglass to messages
50+
that will take a long time to reply fully to.
51+
52+
.. code-block:: python
53+
54+
from errbot import BotPlugin, botcmd
55+
56+
class PluginExample(BotPlugin):
57+
@botcmd
58+
def longcompute(self, mess, args):
59+
if self._bot.mode == "slack":
60+
self._bot.add_reaction(mess, "hourglass")
61+
else:
62+
yield "Finding the answer..."
63+
64+
time.sleep(10)
65+
66+
yield "The answer is: 42"
67+
if self._bot.mode == "slack":
68+
self._bot.remove_reaction(mess, "hourglass")
69+
4870
4971
Getting to the underlying client library
5072
----------------------------------------

errbot/backends/slack.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ def _message_event_handler(self, event):
503503
msg.frm = SlackPerson(self.sc, user, event['channel'])
504504
msg.to = SlackPerson(self.sc, self.username_to_userid(self.sc.server.username),
505505
event['channel'])
506+
channel_link_name = event['channel']
506507
else:
507508
if subtype == "bot_message":
508509
msg.frm = SlackRoomBot(
@@ -515,6 +516,13 @@ def _message_event_handler(self, event):
515516
else:
516517
msg.frm = SlackRoomOccupant(self.sc, user, event['channel'], bot=self)
517518
msg.to = SlackRoom(channelid=event['channel'], bot=self)
519+
channel_link_name = msg.to.name
520+
521+
msg.extras['url'] = 'https://{domain}.slack.com/archives/{channelid}/p{ts}'.format(
522+
domain=self.sc.server.domain,
523+
channelid=channel_link_name,
524+
ts=self._ts_for_message(msg).replace('.', '')
525+
)
518526

519527
self.callback_message(msg)
520528

@@ -632,14 +640,18 @@ def send_message(self, mess):
632640
limit = min(self.bot_config.MESSAGE_SIZE_LIMIT, SLACK_MESSAGE_LIMIT)
633641
parts = self.prepare_message_body(body, limit)
634642

643+
timestamps = []
635644
for part in parts:
636-
self.api_call('chat.postMessage', data={
645+
result = self.api_call('chat.postMessage', data={
637646
'channel': to_channel_id,
638647
'text': part,
639648
'unfurl_media': 'true',
640649
'link_names': '1',
641650
'as_user': 'true',
642651
})
652+
timestamps.append(result['ts'])
653+
654+
mess.extras['ts'] = timestamps
643655
except Exception:
644656
log.exception(
645657
"An exception occurred while trying to send the following message "
@@ -849,6 +861,53 @@ def build_reply(self, mess, text=None, private=False):
849861
response.to = mess.frm.room if isinstance(mess.frm, RoomOccupant) else mess.frm
850862
return response
851863

864+
def add_reaction(self, mess: Message, reaction: str) -> None:
865+
"""
866+
Add the specified reaction to the Message if you haven't already.
867+
:param mess: A Message.
868+
:param reaction: A str giving an emoji, without colons before and after.
869+
:raises: ValueError if the emoji doesn't exist.
870+
"""
871+
return self._react('reactions.add', mess, reaction)
872+
873+
def remove_reaction(self, mess: Message, reaction: str) -> None:
874+
"""
875+
Remove the specified reaction from the Message if it is currently there.
876+
:param mess: A Message.
877+
:param reaction: A str giving an emoji, without colons before and after.
878+
:raises: ValueError if the emoji doesn't exist.
879+
"""
880+
return self._react('reactions.remove', mess, reaction)
881+
882+
def _react(self, method: str, mess: Message, reaction: str) -> None:
883+
try:
884+
# this logic is from send_message
885+
if mess.is_group:
886+
to_channel_id = mess.to.id
887+
else:
888+
to_channel_id = mess.to.channelid
889+
890+
ts = self._ts_for_message(mess)
891+
892+
self.api_call(method, data={'channel': to_channel_id,
893+
'timestamp': ts,
894+
'name': reaction})
895+
except SlackAPIResponseError as e:
896+
if e.error == 'invalid_name':
897+
raise ValueError(e.error, 'No such emoji', reaction)
898+
elif e.error in ('no_reaction', 'already_reacted'):
899+
# This is common if a message was edited after you reacted to it, and you reacted to it again.
900+
# Chances are you don't care about this. If you do, call api_call() directly.
901+
pass
902+
else:
903+
raise SlackAPIResponseError(error=e.error)
904+
905+
def _ts_for_message(self, mess):
906+
try:
907+
return mess.extras['slack_event']['message']['ts']
908+
except KeyError:
909+
return mess.extras['slack_event']['ts']
910+
852911
def shutdown(self):
853912
super().shutdown()
854913

@@ -982,6 +1041,8 @@ def id(self):
9821041
self._id = self._channel.id
9831042
return self._id
9841043

1044+
channelid = id
1045+
9851046
@property
9861047
def name(self):
9871048
"""Return the name of this room"""

errbot/backends/text.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,16 @@ def send_message(self, mess):
177177
print(self.md_borderless_ansi.convert(mess.body))
178178
print('\n\n')
179179

180+
def add_reaction(self, mess: Message, reaction: str) -> None:
181+
# this is like the Slack backend's add_reaction
182+
self._react('+', mess, reaction)
183+
184+
def remove_reaction(self, mess: Message, reaction: str) -> None:
185+
self._react('-', mess, reaction)
186+
187+
def _react(self, sign, mess, reaction):
188+
self.send(mess.frm, 'reaction {}:{}:'.format(sign, reaction), in_reply_to=mess)
189+
180190
def change_presence(self, status: str = ONLINE, message: str = '') -> None:
181191
log.debug("*** Changed presence to [%s] %s", (status, message))
182192

0 commit comments

Comments
 (0)