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.
Table of Contents
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:
Argument | Description |
---|---|
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 |
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:
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
- Mattermost webhooks: https://docs.mattermost.com/developer/webhooks-incoming.html
- Slack webhooks : https://api.slack.com/messaging/webhooks
- Microsoft Teams webhooks: https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#setting-up-a-custom-incoming-webhook
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: