Sending messages to Mattermost or Slack Webhooks using Python 3

descriptionStandard

In this article I will demonstrate another method for posting messages to a Mattermost/Slack instance but this time from Python 3 by utilising an incoming webhook.

I previously covered how to make web requests and post messages from PowerShell.

Below is a custom class we will be utilising to send messages to Mattermost using Python 3.

class Matterhook:
    """
    A simple class for posting messages to Mattermost or Slack via incoming webhooks.
    """

    def __init__(self):
        self.status_code = None
        self.headers = {'content-type': 'application/json'}

    def post_message(self, url, message_payload, headers=None, timeout=None):
        """
        POST a message to an incoming webhook URL with a given payload and headers.

        :param url: The URL of the incoming webhook.
        :type url: str
        :param message_payload: The message payload to send to the webhook.
        :type message_payload: dict
        :param headers: Additional headers to send with the request.
        :type headers: dict or None
        :param timeout: Timeout in seconds for the request.
        :type timeout: float or None
        :raises ValueError: If the URL or message payload is invalid.
        :raises requests.exceptions.RequestException: If there was an error sending the request.
        :return: The response object if the request was successful.
        :rtype: requests.Response
        """
        if not message_payload:
            raise ValueError("Empty message payload provided.")
        if not isinstance(message_payload, dict):
            raise ValueError("Message payload should be a dictionary.")

        if not url:
            raise ValueError("Empty URL provided.")
        if not isinstance(url, str):
            raise ValueError("URL should be a string.")

        if headers is not None and not isinstance(headers, dict):
            raise ValueError("Headers should be a dictionary.")

        self.headers.update(headers or {})

        try:
            response = requests.post(url, data=json.dumps(message_payload), headers=self.headers, timeout=timeout)
            response.raise_for_status()
            self.status_code = response.status_code
            return response
        except requests.exceptions.RequestException as e:
            raise e

    def get_status_code(self):
        """
        Return the status code of the last request, or None if no request has been sent.

        :return: The HTTP status code of the last request.
        :rtype: int or None
        """
        return self.status_code

In the example above the function post_message has 4 arguments, lets review them below:

ArgumentDescription
self
(internal)
Is used internally within the class to access class methods attributes.
webhook
(required)
A url, example: https://server-url.com/hooks/xxxxxxxxxxxxxxxxx
The value should be that of the incoming webhook you have generated on your server.
message (required)A message, example: Test message sent from PowerShell
username
(optional)
A name, example: Anomandaris
Function post_message from class Matterhook argument explanation.

This function can be used in a few ways; below are some examples on how to call the function from Python 3 and pass in additional data.

Send a message to a webhook with a custom username:

# Init class to post message
notify = Matterhook()

payload = {
    'text': 'Has anyone seen Soleil?'
}

notify.post_message('https://oddity.oddineers.co.uk/hooks/1234567890qwertyuiop', payload)

Send a message to a webhook; but override the default username:

notify.post_message('https://oddity.oddineers.co.uk/hooks/1234567890qwertyuiop', payload, 'Poliel')

Using matterping.py

The Python 3 script linked above also includes an example which when called directly from the CLI accepts several arguments; see the table below:

Short argsLong argsDescription
-w–webhookA url, example: https://server-url.com/hooks/xxxxxxxxxxxxxxxxx
The value should be that of the incoming webhook you have generated on your server.
-m–message A message, example: Test message sent from PowerShell
-u–usernameA name, example: Anomandaris
Script matterping.py argument explanation.

In the script matterping.py if you run it directly it will execute the code contained within the if __name__ == '__main__': block as shown below. You can use this and adopt it to build and post your own payloads.


if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    # Adds optional arguments to the script to set the webhook url, message and username
    # All arguments are optional as we will handle required args in our basic class.
    parser.add_argument('-w', '--webhook', dest='webhook', help='Incoming Webhook address.', type=str)
    parser.add_argument('-m', '--message', dest='message', help='Body of message.', type=str)
    parser.add_argument('-u', '--username', dest='username', help='Username to post as.', type=str)
    args = parser.parse_args()

    payload = {
        'text': args.message
    }

    # If username is provided
    if args.username:
        payload['username'] = args.username

    # Init class to post message
    notify = Matterhook()
    notify.post_message(args.webhook, payload)

    # Print help if response was not successful; not 20x
    if notify.get_status_code() not in [200, 201]:

Example script usage:

# Short args
matterping.py -w https://<server-url>/hooks/1234asde4321edsa1234 -m "Test message"

# Long args
matterping.py --webhook https://<server-url>/hooks/1234asde4321edsa1234 --message "Test message"

# Short args + username
matterping.py -w https://<server-url>/hooks/1234asde4321edsa1234 -m "Test message" -u "Poliel"

# These hook urls are not real.

Additional information

Now depending upon which service you are using; Mattermost or Slack you may be required to adjust the contents of the payload sent to the server. To do this find the section below and adjusting the contents to suit your needs.


    payload = {
        'text': args.message
    }

    # If username is provided
    if args.username: