mattermostautodriver documentation
See https://github.com/embl-bio-it/python-mattermost-autodriver for the github repository.
You interact with this module mainly by using the TypedDriver class.
If you want to access information about the logged in user, like the user id,
you can access them by using TypedDriver.client.userid.
Installation
pip install mattermostautodriver
Usage
from mattermostautodriver import TypedDriver
foo = TypedDriver({
"""
Required options
Instead of the login/password, you can also use a personal access token.
If you have a token, you don't need to pass login/pass.
It is also possible to use 'auth' to pass a auth header in directly,
for an example, see:
https://embl-bio-it.github.io/python-mattermost-autodriver/#authentication
"""
'url': 'mattermost.server.com',
'login_id': 'user.name',
'password': 'verySecret',
'token': 'YourPersonalAccessToken',
"""
Optional options
These options already have useful defaults or are just not needed in every case.
In most cases, you won't need to modify these.
If you can only use a self signed/insecure certificate, you should set
verify to your CA file or to False. Please double check this if you have any errors while
using a self signed certificate!
"""
'scheme': 'https',
'port': 8065,
'verify': True, # Or /path/to/file.pem
'mfa_token': 'YourMFAToken',
"""
Setting this will pass the your auth header directly to
the request libraries 'auth' parameter.
You probably only want that, if token or login/password is not set or
you want to set a custom auth header.
"""
'auth': None,
"""
If for some reasons you get regular timeouts after a while, try to decrease
this value. The websocket will ping the server in this interval to keep the connection
alive.
If you have access to your server configuration, you can of course increase the timeout
there.
"""
'timeout': 30,
"""
This value controls the request timeout.
See https://python-requests.org/en/master/user/advanced/#timeouts
for more information.
The default value is None here, because it is the default in the
request library, too.
"""
'request_timeout': None,
"""
To keep the websocket connection alive even if it gets disconnected for some reason you
can set the keepalive option to True. The keepalive_delay defines how long to wait in seconds
before attempting to reconnect the websocket.
"""
'keepalive': False,
'keepalive_delay': 5,
"""
This option allows you to provide additional keyword arguments when calling websockets.connect()
By default it is None, meaning we will not add any additional arguments. An example of an
additional argument you can pass is one used to disable the client side pings:
'websocket_kw_args': {"ping_interval": None},
"""
'websocket_kw_args': None,
"""
Setting debug to True, will activate a very verbose logging.
This also activates the logging for the requests package,
so you can see every request you send.
Be careful. This SHOULD NOT be active in production, because this logs a lot!
Even the password for your account when doing driver.login()!
"""
'debug': False
})
"""
Most of the requests need you to be logged in, so calling login()
should be the first thing you do after you created your TypedDriver instance.
login() returns the raw response.
If using a personal access token, you still need to run login().
In this case, does not make a login request, but a `get_user('me')`
and sets everything up in the client.
"""
foo.login()
"""
You can make api calls by calling `TypedDriver.endpointofchoice`.
The endpoints and their arguments closely follow the official Mattermost
API documentation at https://developers.mattermost.com/api-documentation/ .
API calls return the JSON the server sent as a response.
"""
foo.users.get_user_by_username('another.name')
"""
All request parameters - path parameters, query parameters and request
body fields - are passed as explicit keyword arguments, named exactly as
in the Mattermost API documentation.
"""
# Path parameter
foo.users.get_user(user_id='me')
# Query parameters
foo.users.get_users(page=0, per_page=60)
# Request body fields
foo.channels.create_channel(
team_id='some_team_id',
name='awesome-channel',
display_name='Awesome Channel',
type='O',
)
"""
If you want to make a websocket connection to the mattermost server
you can call the init_websocket method, passing an event_handler.
Every Websocket event send by mattermost will be send to that event_handler.
See the API documentation for which events are available.
"""
foo.init_websocket(event_handler)
# Use `disconnect()` to disconnect the websocket
foo.disconnect()
# To upload a file, pass the opened file object as the `files` argument
channel_id = foo.channels.get_channel_by_name_for_team_name('team', 'channel')['id']
file_id = foo.files.upload_file(
channel_id=channel_id,
files=open(filename, 'rb'),
)['file_infos'][0]['id']
# Track the file id and pass it to `create_post` to attach the file
foo.posts.create_post(
channel_id=channel_id,
message='This is the important file',
file_ids=[file_id],
)
# If needed, you can make custom requests by calling `make_request`
foo.client.make_request('post', '/endpoint', options=None, params=None, data=None, files=None)
# If you want to call a webhook/execute it use the `call_webhook` method.
# This method does not exist on the mattermost api AFAIK, I added it for ease of use.
foo.client.call_webhook('myHookId', options) # Options are optional
# Finally, logout the user if using login/password authentication.
foo.logout()
# And close the client once done with it.
foo.close()
Authentication
Yuu can either use the normal ways for login, by token and or username, or authenticate using the .netrc file. This passes the credentials to the requests library, where you can also read more about that.
A nice example for authentication with .netrc by @apfeiffer.
# A simple example to retrieve all users for a team while using a _token_
# from the .netrc file instead of a password (as requests assumes by default)
import logging
import requests
import netrc
from mattermostautodriver import TypedDriver
logging.basicConfig( format='%(levelname)s - %(name)s - %(asctime)s - %(message)s' )
logger = logging.getLogger( 'MattermostManager' )
logger.setLevel( logging.INFO )
# requests overrides the simple authentication token header if it finds the entry in
# the ~/.netrc file. Since we want to use ~/.netrc to retrieve the _token_, we need
# to provide our own Authenticator class:
class TokenAuth( requests.auth.AuthBase ) :
def __call__( self, r ) :
# Implement my authentication
mmHost = 'mattermost.host.in.netrc'
(login, account, password) = netrc.netrc().authenticators( mmHost )
r.headers[ 'Authorization' ] = "Bearer %s" % password
return r
class MattermostManager( object ) :
def __init__( self ) :
# Get the _token_ (as "password") from the ~/.netrc file.
# the corresponding line in the file should look like:
# <mattermost.host.in.netrc> foo foo <long-string-of-token>
# The "login" and "account" (both set to "foo" in the example are ignored)
mmHost = 'mattermost.host.in.netrc'
(login, account, password) = netrc.netrc().authenticators( mmHost )
logger.debug( "Going to set up driver for connection to %s " % (mmHost,) )
self.mmDriver = TypedDriver( options={
'url' : mmHost,
'scheme' : 'https',
'port' : 443,
'auth' : TokenAuth, # use the new Authenticator class defined above
} )
self.mmDriver.users.get_user( user_id='me' )
def getTeamMembers( self, teamName ) :
# for restricted teams, we need to get the ID first, and
# for this, we need to have the "name" (as in the URL), not
# the "display name", as shown in the GUIs:
team0 = self.mmDriver.teams.get_team_by_name( teamName )
logger.debug( 'team by name %s : %s' % (teamName, team0) )
teamId = team0[ 'id' ]
team = self.mmDriver.teams.check_team_exists( teamName )
logger.debug( 'team %s - exists: %s' % (teamName, team[ 'exists' ]) )
if not team[ 'exists' ] :
logger.error( 'no team with name %s found' % teamName )
return
logger.debug( 'found team %s: %s' % (teamName, self.mmDriver.teams.get_team( teamId )) )
users = self._getAllUsersForTeam( teamId )
logger.debug( 'found %s users for team "%s"' % (len( users ), teamName) )
return users
def _getAllUsersForTeam( self, teamId ) :
# get all users for a team
# with the max of 200 per page, we need to iterate a bit over the pages
users = [ ]
pgNo = 0
teamUsers = self.mmDriver.users.get_users(in_team= teamId,
page= pgNo,
per_page = 200,
)
while teamUsers :
users += teamUsers
pgNo += 1
teamUsers = self.mmDriver.users.get_users(in_team=teamId,
per_page=200,
page=pgNo,
)
return users
if __name__ == '__main__' :
mmM = MattermostManager()
mmM.getTeamMembers( 'myTeam' )
Retries
Requests that fail due to server side rate limiting (HTTP 429) are retried
automatically, honoring the wait requested by the server. Connection errors
and 502/503/504 responses are also retried, but only for idempotent requests
(GET, PUT, DELETE and HEAD), since a POST may already
have been processed by the server. Requests carrying files or a streaming
request body are never retried automatically, as their content is consumed
when the request is first sent.
The wait is read from the Retry-After header if present, falling back to
X-RateLimit-Reset otherwise. Retry-After takes precedence because it
is the header standardized by the HTTP specification (RFC 9110), whereas the
X-RateLimit-* headers - which Mattermost also sends - are an unstandardized
convention. Values are accepted in the forms found in the wild: a delay in
seconds (120), an HTTP-date (Wed, 21 Oct 2026 07:28:00 GMT), and an
absolute unix timestamp (1794000000). An unparseable header is skipped in
favor of the next one; if no usable value remains, retries use exponential
backoff instead. The same parsed wait is exposed to callers as the
retry_after attribute of TooManyRequests, always as non-negative
seconds from now.
The full mapping of failure modes to exceptions and retry behavior:
Failure |
Exception |
Retry behavior |
|---|---|---|
HTTP 400 |
|
Not retried |
HTTP 401 |
|
Not retried |
HTTP 403 |
|
Not retried |
HTTP 404 |
|
Not retried |
HTTP 405 |
|
Not retried |
HTTP 413 |
|
Not retried |
HTTP 429 |
|
Retried for all methods, waiting as requested by the server |
HTTP 501 |
|
Not retried |
HTTP 502 / 503 / 504 |
|
Retried with exponential backoff, idempotent methods only |
Other HTTP error codes |
|
Not retried |
Connection errors and timeouts |
|
Retried with exponential backoff, idempotent methods only |
The listed exception is what a request raises once it fails for good - either
immediately for non-retried failures, or after retries are exhausted. Error
responses other than 429 whose body does not follow the standard Mattermost
JSON error schema raise InvalidMattermostError instead of the exception
listed above.
Two driver options control this behavior:
max_retries(default3) - maximum number of automatic retries per request. Set to0to disable retrying entirely.retry_max_sleep(default30) - upper bound in seconds for a single wait between retries. If the server requests a longer wait, the request fails immediately withTooManyRequestsand the requested wait time is available in itsretry_afterattribute.
Together these bound the total time a single call may spend waiting between
attempts at max_retries * retry_max_sleep - 90 seconds with the defaults,
reached only if the server repeatedly requests near-maximum waits. When no
usable wait is provided and exponential backoff applies, the default total is
3.5-7 seconds instead. Set max_retries to 0 if failing fast matters
more than resilience.
Classes
- class mattermostautodriver.TypedDriver(options=None, client_cls=<class 'mattermostautodriver.client.Client'>, *args, **kwargs)[source]
- init_websocket(event_handler, websocket_cls=<class 'mattermostautodriver.websocket.Websocket'>)[source]
Will initialize the websocket connection to the mattermost server.
This should be run after login(), because the websocket needs to make an authentification.
See https://developers.mattermost.com/api-documentation/#/websocket for which websocket events mattermost sends.
Example of a really simple event_handler function
async def my_event_handler(message): print(message)
- Parameters:
event_handler (Function(message)) – The function to handle the websocket events. Takes one argument.
- Returns:
The event loop
Constants
- mattermostautodriver.constants.MAX_PER_PAGE = 200
Maximum number of items per page accepted by the Mattermost API.
- mattermostautodriver.constants.DEFAULT_PER_PAGE = 60
Default number of items per page used by the Mattermost API when
per_pageis omitted.
Exceptions that api requests can throw
- class mattermostautodriver.exceptions.InvalidMattermostError(message: str, status_code: int)[source]
Raised when mattermost returns an invalid error
- class mattermostautodriver.exceptions.UnknownMattermostError(message: str, status_code: int, error_id: str, request_id: str, is_oauth_error: bool)[source]
Raised when mattermost returns a status code that is not known
- class mattermostautodriver.exceptions.InvalidOrMissingParameters(message: str, error_id: str, request_id: str, is_oauth_error: bool)[source]
Raised when mattermost returns a 400 Invalid or missing parameters in URL or request body
- class mattermostautodriver.exceptions.NoAccessTokenProvided(message: str, error_id: str, request_id: str, is_oauth_error: bool)[source]
Raised when mattermost returns a 401 No access token provided
- class mattermostautodriver.exceptions.NotEnoughPermissions(message: str, error_id: str, request_id: str, is_oauth_error: bool)[source]
Raised when mattermost returns a 403 Do not have appropriate permissions
- class mattermostautodriver.exceptions.ResourceNotFound(message: str, error_id: str, request_id: str, is_oauth_error: bool)[source]
Raised when mattermost returns a 404 Resource not found
- class mattermostautodriver.exceptions.MethodNotAllowed(message: str, error_id: str, request_id: str, is_oauth_error: bool)[source]
Raised when mattermost returns a 405 Method Not Allowed
- class mattermostautodriver.exceptions.ContentTooLarge(message: str, error_id: str, request_id: str, is_oauth_error: bool)[source]
Raised when mattermost returns a 413 Content too large
- class mattermostautodriver.exceptions.TooManyRequests(message: str, retry_after: float | None, error_id: str | None = None, request_id: str | None = None, is_oauth_error: bool = False)[source]
Raised when the server returns a 429 Too Many Requests
Mattermost’s rate limiter responds with a plain text body rather than the standard JSON error, so
error_idandrequest_idmay be None.retry_afterholds the server-provided wait time in seconds, taken from theRetry-AfterorX-RateLimit-Resetheader if present.