From 0b26d1c884922f183acd0ea860a6e1045e41ba19 Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Sun, 8 Aug 2021 11:44:12 -0400 Subject: [PATCH] Rocket.Chat basic message structure fix (#419) You only can send alias and avatar properties if your user has the bot role. We implement this rule to avoid users to impersonate other users. - quoted from Rocket.Chat website This update accounts for this restriction --- apprise/plugins/NotifyRocketChat.py | 67 ++++++++++++++++++----------- test/test_rest_plugins.py | 4 +- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/apprise/plugins/NotifyRocketChat.py b/apprise/plugins/NotifyRocketChat.py index 9beda256..9131ceef 100644 --- a/apprise/plugins/NotifyRocketChat.py +++ b/apprise/plugins/NotifyRocketChat.py @@ -174,14 +174,17 @@ class NotifyRocketChat(NotifyBase): 'avatar': { 'name': _('Use Avatar'), 'type': 'bool', - 'default': True, + 'default': False, + }, + 'webhook': { + 'alias_of': 'webhook', }, 'to': { 'alias_of': 'targets', }, }) - def __init__(self, webhook=None, targets=None, mode=None, avatar=True, + def __init__(self, webhook=None, targets=None, mode=None, avatar=None, **kwargs): """ Initialize Notify Rocket.Chat Object @@ -209,9 +212,6 @@ class NotifyRocketChat(NotifyBase): # Assign our webhook (if defined) self.webhook = webhook - # Place an avatar image to associate with our content - self.avatar = avatar - # Used to track token headers upon authentication (if successful) # This is only used if not on webhook mode self.headers = {} @@ -278,6 +278,22 @@ class NotifyRocketChat(NotifyBase): self.logger.warning(msg) raise TypeError(msg) + # Prepare our avatar setting + # - if specified; that trumps all + # - if not specified and we're dealing with a basic setup, the Avatar + # is disabled by default. This is because if the account doesn't + # have the bot flag set on it it won't work as documented here: + # https://developer.rocket.chat/api/rest-api/endpoints\ + # /team-collaboration-endpoints/chat/postmessage + # - Otherwise if we're a webhook, we enable the avatar by default + # (if not otherwise specified) since it will work nicely. + # Place an avatar image to associate with our content + if self.mode == RocketChatAuthMode.BASIC: + self.avatar = False if avatar is None else avatar + + else: # self.mode == RocketChatAuthMode.WEBHOOK: + self.avatar = True if avatar is None else avatar + return def url(self, privacy=False, *args, **kwargs): @@ -367,11 +383,6 @@ class NotifyRocketChat(NotifyBase): # Initiaize our error tracking has_error = False - headers = { - 'User-Agent': self.app_id, - 'Content-Type': 'application/json', - } - while len(targets): # Retrieve our target target = targets.pop(0) @@ -380,8 +391,7 @@ class NotifyRocketChat(NotifyBase): payload['channel'] = target if not self._send( - dumps(payload), notify_type=notify_type, path=path, - headers=headers, **kwargs): + payload, notify_type=notify_type, path=path, **kwargs): # toggle flag has_error = True @@ -400,21 +410,24 @@ class NotifyRocketChat(NotifyBase): return False # prepare JSON Object - payload = self._payload(body, title, notify_type) + _payload = self._payload(body, title, notify_type) # Initiaize our error tracking has_error = False + # Build our list of channels/rooms/users (if any identified) + channels = ['@{}'.format(u) for u in self.users] + channels.extend(['#{}'.format(c) for c in self.channels]) + # Create a copy of our channels to notify against - channels = list(self.channels) - _payload = payload.copy() + payload = _payload.copy() while len(channels) > 0: # Get Channel channel = channels.pop(0) - _payload['channel'] = channel + payload['channel'] = channel if not self._send( - _payload, notify_type=notify_type, headers=self.headers, + payload, notify_type=notify_type, headers=self.headers, **kwargs): # toggle flag @@ -422,11 +435,11 @@ class NotifyRocketChat(NotifyBase): # Create a copy of our room id's to notify against rooms = list(self.rooms) - _payload = payload.copy() + payload = _payload.copy() while len(rooms): # Get Room room = rooms.pop(0) - _payload['roomId'] = room + payload['roomId'] = room if not self._send( payload, notify_type=notify_type, headers=self.headers, @@ -451,13 +464,13 @@ class NotifyRocketChat(NotifyBase): # apply our images if they're set to be displayed image_url = self.image_url(notify_type) - if self.avatar: + if self.avatar and image_url: payload['avatar'] = image_url return payload def _send(self, payload, notify_type, path='api/v1/chat.postMessage', - headers=None, **kwargs): + headers={}, **kwargs): """ Perform Notify Rocket.Chat Notification """ @@ -468,13 +481,19 @@ class NotifyRocketChat(NotifyBase): api_url, self.verify_certificate)) self.logger.debug('Rocket.Chat Payload: %s' % str(payload)) + # Apply minimum headers + headers.update({ + 'User-Agent': self.app_id, + 'Content-Type': 'application/json', + }) + # Always call throttle before any remote server i/o is made self.throttle() try: r = requests.post( api_url, - data=payload, + data=dumps(payload), headers=headers, verify=self.verify_certificate, timeout=self.request_timeout, @@ -691,8 +710,8 @@ class NotifyRocketChat(NotifyBase): NotifyRocketChat.unquote(results['qsd']['mode']) # avatar icon - results['avatar'] = \ - parse_bool(results['qsd'].get('avatar', True)) + if 'avatar' in results['qsd'] and len(results['qsd']['avatar']): + results['avatar'] = parse_bool(results['qsd'].get('avatar', True)) # The 'to' makes it easier to use yaml configuration if 'to' in results['qsd'] and len(results['qsd']['to']): diff --git a/test/test_rest_plugins.py b/test/test_rest_plugins.py index 1b5aeca7..d96d181b 100644 --- a/test/test_rest_plugins.py +++ b/test/test_rest_plugins.py @@ -3748,7 +3748,7 @@ TEST_URLS = ( }, }), # Several channels - ('rocket://user:pass@localhost/#channel1/#channel2/?avatar=No', { + ('rocket://user:pass@localhost/#channel1/#channel2/?avatar=Yes', { 'instance': plugins.NotifyRocketChat, # The response text is expected to be the following on a success 'requests_response_text': { @@ -3772,7 +3772,7 @@ TEST_URLS = ( }, }), # A room and channel - ('rocket://user:pass@localhost/room/#channel?mode=basic', { + ('rocket://user:pass@localhost/room/#channel?mode=basic&avatar=Yes', { 'instance': plugins.NotifyRocketChat, # The response text is expected to be the following on a success 'requests_response_text': {