@@ -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"""
0 commit comments