Git Product home page Git Product logo

python-mattermost-driver's Introduction

Python Mattermost Driver (APIv4)

Info

The repository will try to keep up with the master of https://github.com/mattermost/mattermost-api-reference

If something changes, it is most likely to change because the official mattermost api changed.

Python 3.6 or later is required.

Installation

pip install mattermostdriver

Documentation

Documentation can be found at https://vaelor.github.io/python-mattermost-driver/ .

Usage

from mattermostdriver import Driver

foo = Driver({
    """
    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://vaelor.github.io/python-mattermost-driver/#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, especially the basepath.
    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,
    'basepath': '/api/v4',
    '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 Driver 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 using calling `Driver.endpointofchoice`.
Using api[''] is deprecated for 5.0.0!

So, for example, if you used `Driver.api['users'].get_user('me')` before,
you now just do `Driver.users.get_user('me')`.
The names of the endpoints and requests are almost identical to
the names on the api.mattermost.com/v4 page.
API calls always return the json the server send as a response.
"""
foo.users.get_user_by_username('another.name')

"""
If the api request needs additional parameters
you can pass them to the function in the following way:
- Path parameters are always simple parameters you pass to the function
"""
foo.users.get_user(user_id='me')

# - Query parameters are always passed by passing a `params` dict to the function
foo.teams.get_teams(params={...})

# - Request Bodies are always passed by passing an `options` dict or array to the function
foo.channels.create_channel(options={...})

# See the mattermost api documentation to see which parameters you need to pass.
foo.channels.create_channel(options={
    '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 you will need to pass a `files` dictionary
channel_id = foo.channels.get_channel_by_name_and_team_name('team', 'channel')['id']
file_id = foo.files.upload_file(
    channel_id=channel_id,
    files={'files': (filename, open(filename, 'rb'))}
)['file_infos'][0]['id']


# track the file id and pass it in `create_post` options, to attach the file
foo.posts.create_post(options={
    '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, basepath=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.webhooks.call_webhook('myHookId', options) # Options are optional

python-mattermost-driver's People

Contributors

a3dho3yn avatar apfeiffer1 avatar attzonko avatar cooperlees avatar dan-klasson avatar dependabot[bot] avatar edwinschaap avatar fried avatar icedevml avatar iprok avatar jblinkenlights avatar jneeven avatar laimison avatar lrittel avatar luckydonald avatar mathse avatar maxbrunet avatar opalmer avatar oskapt avatar sahasrara62 avatar sakarah avatar sscherfke avatar ttuffin avatar unode avatar vaelor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-mattermost-driver's Issues

Get posts around oldest unread

I cant seem to find a function that does this API call.
https://api.mattermost.com/#tag/posts/paths/~1users~1{user_id}~1channels~1{channel_id}~1posts~1unread/get

looking at the source code something like the code below would work in mattermostdriver.endpoints.posts

def get_unread_posts_for_channel(self, user_id, channel_id, params=None):
        return self.client.get(
            Users.endpoint + '/' + user_id + '/' + Channels.endpoint + '/' + channel_id + '/posts/unread',
            params=params
)

I can open a PR if needed, just wanted to post an issue first to see if maybe I am missing a function that already accomplishes this.

Cheers

SSLError: ssl3_get_record - wrong version number

Using with Python 3.6.1
When trying to execute the code, get this error:

urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='domain.com', port=8065): Max retries exceeded with url: /api/v4/users/login (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'ssl3_get_record', 'wrong version number')],)",),))

Seems like this is related to:
https://stackoverflow.com/a/9963668/3090556

Configurable timeouts

Hi,

thanks for this awesome package!

My network isn't very reliable so I'd love to be able to set sensible timeouts for the requests made using requests.get (to be used in the client for example).

Thanks and best regards!

get_channel_by_name_and_team_name permission problem

The following code works as expected within Jupiter lab.

from mattermostdriver import Driver
from pprint import pprint

options = {}

options['url'] = '<url>'
options['token'] = '<token>'
options['port'] = 443

%env REQUESTS_CA_BUNDLE=/CB64_CERTS.pem

driver = Driver(options=options)
driver.login()

teamData = driver.teams.get_team_by_name('team')
teamID = teamData['id']
channelData = driver.channels.get_channel_by_name(teamID, 'channel')

postOptions = {'channel_id': channelData['id'], 'message': 'mattermost test'}

driver.posts.create_post(options=postOptions)

However, if I try to call:

driver.channels.get_channel_by_name_and_team_name('team', 'channel')

I get the error:

NotEnoughPermissions: You do not have the appropriate permissions

which seems strange. As near as I can tell, all of the appropriate permissions are there as should be evidenced by the ability to call alternative functions to obtain the required information to post a message to a channel.

I am not sure if the bug is in the library or if this is a bug in mattermost itself.

exception translation results in confusing backtraces

For example, with bogus terms in search_criteria:

>>> mm.users.search_users(search_criteria)
Invalid or missing term in request body.
Traceback (most recent call last):
  File "/home/paul/.local/lib/python3.8/site-packages/mattermostdriver/client.py", line 162, in make_request
    response.raise_for_status()
  File "/usr/lib/python3/dist-packages/requests/models.py", line 940, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://chat.example.com:443/api/v4/users/search

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/paul/.local/lib/python3.8/site-packages/mattermostdriver/endpoints/users.py", line 32, in search_users
    return self.client.post(
  File "/home/paul/.local/lib/python3.8/site-packages/mattermostdriver/client.py", line 206, in post
    return self.make_request('post', endpoint, options=options, params=params, data=data, files=files).json()
  File "/home/paul/.local/lib/python3.8/site-packages/mattermostdriver/client.py", line 173, in make_request
    raise InvalidOrMissingParameters(message)
mattermostdriver.exceptions.InvalidOrMissingParameters: Invalid or missing term in request body.
>>> _

This makes the traceback more difficult to read than necessary. I did a little research and it seems that translating exceptions as follows:

raise MyFriendlyException() from None

should suppress the original exception and yield a clean traceback.

Unable to create customer emojis

It is probably a silly tasks but I try to upload emojis and I miserably fail. 😁

Here's my non-working code:

emoji_name = "test"
image_path = "test.gif"

with open(image_path, "rb") as image:
    files = {"image": image}
    mm.emoji.create_custom_emoji(emoji_name=emoji_name, files=files)

which gives me:

Invalid or missing emoji in request body
Traceback (most recent call last):
  File "/Users/max/.pyenv/versions/mmemoji/lib/python3.7/site-packages/mattermostdriver/client.py", line 140, in make_request
    response.raise_for_status()
  File "/Users/max/.pyenv/versions/mmemoji/lib/python3.7/site-packages/requests/models.py", line 939, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://mattermost.example.org:443/api/v4/emoji

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "mmemoji_other.py", line 20, in <module>
    mm.emoji.create_custom_emoji(emoji_name=emoji_name, files=files)
  File "/Users/max/.pyenv/versions/mmemoji/lib/python3.7/site-packages/mattermostdriver/endpoints/emoji.py", line 11, in create_custom_emoji
    files=files
  File "/Users/max/.pyenv/versions/mmemoji/lib/python3.7/site-packages/mattermostdriver/client.py", line 174, in post
    return self.make_request('post', endpoint, options=options, params=params, data=data, files=files).json()
  File "/Users/max/.pyenv/versions/mmemoji/lib/python3.7/site-packages/mattermostdriver/client.py", line 145, in make_request
    raise InvalidOrMissingParameters(data['message'])
mattermostdriver.exceptions.InvalidOrMissingParameters: Invalid or missing emoji in request body

According to the Mattermost API Reference, this endpoint needs:

FormData Parameters

  • image file Required
    A file to be uploaded
  • emoji string Required
    A JSON object containing a name field with the name of the emoji and a creator_id field with the id of the authenticated user.

The driver only provide a name for the emoji object:

data={'emoji': {'name': emoji_name}},

I also checked a few other drivers, use cases of them and the server side code which seem to confirm the need of a creator_id (I can give details if it helps).

So I tried to work around it but I get the exact same error:

creator_id = mm.users.get_user(user_id="me")["id"]
with open(image_path, "rb") as image:
    data = {"emoji": {"name": emoji_name, "creator_id": creator_id}}
    files = {"image": image}
    mm.client.post("/emoji", data=data, files=files)

What else do I miss?

The creation of the emoji with same file and name via the Web UI perfectly works.

Passing a SSL Cert to the requests library

The following code works just fine in Jupyter:

from mattermostdriver import Driver
from pprint import pprint

options = {}

options[ 'url' ]   = '<url>'
options[ 'token' ] = '<your personal token here>'
options[ 'port' ]  = 443

%env REQUESTS_CA_BUNDLE=/path/to/cert.pem

driver = Driver( options = options )
driver.login()

teamData    = driver.teams.get_team_by_name( 'teamname' )
teamID      = teamData[ 'id' ]
channelData = driver.channels.get_channel_by_name( teamID, 'roomname' )

postOptions = { 'channel_id': channelData[ 'id' ], 'message': 'mattermost test' }

driver.posts.create_post( options = postOptions )

The reason why it works is that I can specify in the environment that path to the .pem file for the requests library to use. It would be nice if there was some way to pass this path directly down to the requests library through login() or just adding stuff to the options variable, but that does not appear to be possible...unless I have missed something.

Upload_file doesn`t work for image files?

This may be an API issue, but perhaps someone can help me:

I don' have no problem to upload quite any file type, but image files (jpg, png) are rejected:

Traceback (most recent call last):
  File "d:\Users\Markus\Documents\python\imagequote\mmtest.py", line 60, in <module>
    files={'files': (FILENAME, open(FILENAME))}
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\mattermostdriver\endpoints\files.py", line 11, in upload_file
    files=files
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\mattermostdriver\client.py", line 135, in post
    return self.make_request('post', endpoint, options=options, params=params, data=data, files=files).json()
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\mattermostdriver\client.py", line 109, in make_request
    files=files
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\requests\api.py", line 112, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\requests\api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\requests\sessions.py", line 494, in request
    prep = self.prepare_request(req)
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\requests\sessions.py", line 437, in prepare_request
    hooks=merge_hooks(request.hooks, self.hooks),
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\requests\models.py", line 308, in prepare
    self.prepare_body(data, files, json)
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\requests\models.py", line 496, in prepare_body
    (body, content_type) = self._encode_files(files, data)
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\requests\models.py", line 159, in _encode_files
    fdata = fp.read()
  File "C:\Program Files (x86)\Python36-32\lib\encodings\cp1252.py", line 23, in decode
    return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 172: character maps to <undefined>

I have no idea how to investigate the problem. Mattermost's API documentation doesn' t help either.

Thanks for help & for this project!

Markus

Generate api endpoints from mattermost openapi doc

Instead of manually adding each endpoint, a script to generate all endpoints from the openapi documentation would be nice.

This could also add more infos to every endpoint instead of only a options or params parameter.

Support Python 3.8

Python 3.8.0 has been released on October 14, 2019:
https://www.python.org/downloads/release/python-380/

I have been testing my project against 3.8-dev for a few weeks and it has reported only 1 warning (4 occurrences):

DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
Full output
/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/mattermostdriver/websocket.py:19

  /home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/mattermostdriver/websocket.py:19: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead

    def connect(self, event_handler):

/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/mattermostdriver/websocket.py:54

  /home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/mattermostdriver/websocket.py:54: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead

    def _start_loop(self, websocket, event_handler):

/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/mattermostdriver/websocket.py:73

  /home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/mattermostdriver/websocket.py:73: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead

    def _authenticate_websocket(self, websocket, event_handler):

/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/mattermostdriver/websocket.py:103

  /home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/mattermostdriver/websocket.py:103: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead

    def _wait_for_message(self, websocket, event_handler):

Documentation:

The new syntax async def is new in Python 3.5, meaning Python 3.4 support would have to be dropped to fix these warnings. Python 3.4 having reached EOL on March 18, 2019 (https://python.org/downloads/release/python-3410), I think this should be acceptable.

Improve logging

  • Add a debug option to activate debug messages
  • Actually add a few more useful log messages

JSON parsing works only on errors returned by mattermost

		try:
			response.raise_for_status()
		except requests.HTTPError as e:
			data = e.response.json()
			log.error(data['message'])
			if data['status_code'] == 400:
				raise InvalidOrMissingParameters(data['message'])
			elif data['status_code'] == 401:
				raise NoAccessTokenProvided(data['message'])
			elif data['status_code'] == 403:
				raise NotEnoughPermissions(data['message'])
			elif data['status_code'] == 413:
				raise ContentTooLarge(data['message'])
			elif data['status_code'] == 501:
				raise FeatureDisabled(data['message'])
			else:
				raise

The except block will throw if for example an 502 occurs, it shouldn't even try to parse json

Very first websockets.pong() call seems to fail

Hi, we are using this driver for our bot code, and are seeing some weird behavior (See details here: attzonko/mmpy_bot#171)

On the Mattermost server side, I am seeing an invalid character error from the first websockets.pong() future after the 20s timeout.

{"level":"debug","ts":1616195215.1219645,"caller":"app/web_conn.go:190","msg":"Error while reading message from websocket","error":"error during decoding websocket message: invalid character 'ð' looking for beginning of value","errorVerbose":"invalid character 'ð' looking for beginning of value\nerror during decoding websocket message\ngithub.com/mattermost/mattermost-server/v5/app.(*WebConn).ReadMsg\n\tgithub.com/mattermost/mattermost-server/v5/app/web_conn.go:257\ngithub.com/mattermost/mattermost-server/v5/app.(*WebConn).startPoller.func1.1\n\tgithub.com/mattermost/mattermost-server/v5/app/web_conn.go:188\nruntime.goexit\n\truntime/asm_amd64.s:1374"}

Any ideas on what we could be doing wrong? Or is this possibly a real issue?

Client._auth() -> TypeError: 'NoneType' object is not callable

There is a bug after last update. There is no way to use token auth.
In client.py:

def auth_header(self):
    if self._auth: return None
...

then:

    def make_request(self, method, endpoint, options=None, params=None, data=None,  files=None, basepath=None):
...
    response = request(
        url + endpoint,
        headers=self.auth_header(),
        auth = self._auth(),
        verify=self._verify,
        json=options,
        params=params,
        data=data,
        files=files,
        timeout=self.request_timeout
    )

So if you want use token self_auth should be None. But then None will call...

I'ts not posible add users with python driver when channel it's created from python driver

when i create a new channel from function mattermost_connection.channels.create_channel in python driver, the channel it's create correcly, but qhen i add user from function mattermost_connection.channels.add_user, the user not add in channel (the user i'ts in the team previously)

when add a new user from mattermost web, the number of members counter plus the "gost user add from python driver", when refresh the page, the "gost user add from python driver" i'ts less

i try to set the mattermost_connection.channels.update_channel_roles, but not work.

when i talk about counter, i'ts the "members" counter in the top of chat

Driver cannot update channel if user is not in this channel

Driver cannot update channel if user (whose token is used for authorization) is not in this channel. Server sends 404 status code for self.driver.channels.update_channel method.

  File "/usr/local/lib/python3.5/dist-packages/mattermostdriver/endpoints/channels.py", line 45, in update_channel
    options=options
  File "/usr/local/lib/python3.5/dist-packages/mattermostdriver/client.py", line 172, in put
    return self.make_request('put', endpoint, options=options, params=params, data=data).json()
  File "/usr/local/lib/python3.5/dist-packages/mattermostdriver/client.py", line 140, in make_request
    response.raise_for_status()
  File "/usr/local/lib/python3.5/dist-packages/requests/models.py", line 829, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found

Not able to authenticate while using Personal Access Token

Traceback (most recent call last):
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\connection.py", line 141, in _new_conn
    (self.host, self.port), self.timeout, **extra_kw)
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\util\connection.py", line 60, in create_connection
    for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
  File "C:\Users\ATHARVA\Anaconda3\lib\socket.py", line 745, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno 11001] getaddrinfo failed

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\connectionpool.py", line 600, in urlopen
    chunked=chunked)
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\connectionpool.py", line 345, in _make_request
    self._validate_conn(conn)
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\connectionpool.py", line 844, in _validate_conn
    conn.connect()
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\connection.py", line 284, in connect
    conn = self._new_conn()
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\connection.py", line 150, in _new_conn
    self, "Failed to establish a new connection: %s" % e)
urllib3.exceptions.NewConnectionError: <urllib3.connection.VerifiedHTTPSConnection object at 0x000002C648DF85F8>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\requests\adapters.py", line 449, in send
    timeout=timeout
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\connectionpool.py", line 649, in urlopen
    _stacktrace=sys.exc_info()[2])
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\urllib3\util\retry.py", line 388, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='https', port=443): Max retries exceeded with url: //bot-dev-hackathon-iitbbs.herokuapp.com:8065/api/v4/users/me (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x000002C648DF85F8>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed',))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "mattermostdevbot.py", line 14, in <module>
    driver.login()
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\mattermostdriver\driver.py", line 162, in login
    result = self.users.get_user('me')
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\mattermostdriver\endpoints\users.py", line 45, in get_user
    self.endpoint + '/' + user_id
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\mattermostdriver\client.py", line 193, in get
    response = self.make_request('get', endpoint, options=options, params=params)
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\mattermostdriver\client.py", line 159, in make_request
    **request_params
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\requests\api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\requests\api.py", line 60, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\requests\sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\requests\sessions.py", line 646, in send
    r = adapter.send(request, **kwargs)
  File "C:\Users\ATHARVA\Anaconda3\lib\site-packages\requests\adapters.py", line 516, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='https', port=443): Max retries exceeded with url: //bot-dev-hackathon-iitbbs.herokuapp.com:8065/api/v4/users/me (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x000002C648DF85F8>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed',))

I am getting the above error whenever I am trying to authenticate my bot.

This is the code that I am running :

from mattermostdriver import Driver

driver_options = {
        'url': "url",
        'token':'token',
        'scheme': 'https',
        'port': 8065,
        'basepath': '/api/v4',
        'verify': True
    }
driver = Driver(driver_options)

print("Authenticating...")
driver.login()
print("Successfully authenticated.")

Possible API improvements

I like that you divided mm-driver’s functionality into several classes that we can compose and customize.

However, I think that the actual usage of the diver when calling the enpoint’s methods could be improved, so that instead of doing

mm = Driver(...)
mm.login()
mm.api['users'].get_user_by_username('another.name')

we can just do:

mm = MatterMost(...)
mm.login()
mm.users.get_user_by_username('another.name')

This is relatively easy to implement in another wrapper class (maybe it can even be merged into Driver):

class MatterMost:
    def __init__(self):
        self._driver = Driver()
        # self._driver.login()

    @property
    def userid(self):
        return self._driver.client.userid

    @property
    def username(self):
        return self._driver.client.username

    def __getattr__(self, key):
        try:
            return self._driver.api[key]
        except KeyError:
            raise AttributeError(key) from None

@Vaelor What do you think about this?

Python 2.7 support

I'm getting this when trying to install it with pip on Python 2.7:

Collecting mattermostdriver
  Using cached mattermostdriver-4.2.0.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-hV1oTl/mattermostdriver/setup.py", line 11, in <module>
        with open(readme_file, encoding='utf-8') as f:
    TypeError: 'encoding' is an invalid keyword argument for this function

Will this library not support 2.7?

Create User Access Token

Mattermost API has 'Create a user access token' POST request menu.
Would you update python-mattermost-driver for this function??

I'm trying to post "create user access token" request like below. But, it doesn't work.
Do you have any idea ?
My mattermost server is version 4.6 .

/* MY CODE FOR POST REQUEST */
payload={"description": "some message"}

requests(
'https://<my_mattermost_URL>/api/v4/users/UserID from API/tokens',
cookies = foo.client.cookise,
json = payload
)
/* END OF CODE */

If you know something about this problem, please help me...

Driver provides SSL context to ws:// URI scheme

Connecting to an HTTP endpoint correctly sets ws:// but incorrectly provides ssl=context with context being set on line 26 of websocket.py

context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)

This is used on line 39, even if the URI scheme was set to ws:// on line 32.

Setting context=None correctly resolves this.

(Before you ask why I'm using HTTP instead of HTTPS, I have the bot running within an overlay network under Rancher in AWS, so it communicates locally to Mattermost. If I provide the HTTPS URL for mattermost, it will exit the VPC and connect to the public IP for the load balancer, generating unnecessary usage charges. I terminate SSL on the load balancer, so I need internal communication to be unencrypted.)

upload a file to a webhook incoming channel

not exactly a issue (not fimiliar with web requests)

i am trying to upload a file to webhook , so thi s is my code

    import requests, json
    import base64
    import os
    
    URL = ""
    file_path = r'C:\Users\prashra\PycharmProjects\personal_project\mattermost\requiremnets.txt'
    
    form_data = {
        "file": (os.path.basename(file_path),open(file_path, 'rb'))
    }
    
    payload = {"channel_id": "mattermost_test", "display_name": "i dont know", "text": "holy"}
    
    headers = {"Content-Type": "application/json"}
    
    
    r = requests.post(URL, data=json.dumps(payload),files = form_data)

this is giving me eror

    ValueError: Data must not be a string.

can you help me how to uploada file to channel.

Add proxy support

Hello everyone,

I would like to add proxy support, as I have come across a case where I need to explicitly add the proxy to the client (requests and websocket) and more people may have the same problem.

In some cases, adding http_proxy and https_proxy to the environment may be enough, but in some cases it may be explicit for the connection with mattermost

I can open a PR if you think it's a good idea

Support bot tokens

HI @Vaelor,
Are bot tokens supported ?

https://docs.mattermost.com/developer/bot-accounts.html

Because I cannot get it work it seems.
Created a bot account in MT and then

import os, sys
import urllib3
import requests
urllib3.disable_warnings()
from mattermostdriver import Driver

mt = Driver(
    {
        "url": "on prem MT",
        "token": "s1z1g6sqp...........w",
        "verify": False,
        "port": 443,
        "debug": True,
    }
)
mt.login()
# print(mt.users.get_user(user_id="me"))

mt.posts.create_post(
    options={
        "channel_id": "j9d13993o............mo",
        "message": "This is the important file",
    }
)

Then:

keep-alive\r\nAuthorization: Bearer s1xxxxxx7ahhmrw\r\nContent-Length: 2\r\nContent-Type: application/json\r\n\r\n'
send: b'{}'
reply: 'HTTP/1.1 401 Unauthorized\r\n'
header: Content-Length: 186
header: Content-Type: application/json
header: Date: Wed, 06 May 2020 12:34:05 GMT
header: Expires: 0
header: Set-Cookie: MMAUTHTOKEN=; Path=/; Max-Age=0; HttpOnly
header: Vary: Accept-Encoding
header: X-Request-Id: hw63wjjj7jyebf3m4zhrtsnssw
header: X-Version-Id: 5.22.0.5.22.1.
Invalid or expired session, please login again.
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/client.py", line 162, in make_request
    response.raise_for_status()
  File "/home/xxxxxxxxxxxx/.local/lib/python3.6/site-packages/requests/models.py", line 940, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: https://URL:443/api/v4/users/me

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "../update/mt.py", line 19, in <module>
    mt.login()
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/driver.py", line 162, in login
    result = self.users.get_user('me')
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/endpoints/users.py", line 45, in get_user
    self.endpoint + '/' + user_id
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/client.py", line 193, in get
    response = self.make_request('get', endpoint, options=options, params=params)
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/client.py", line 175, in make_request
    raise NoAccessTokenProvided(message)
mattermostdriver.exceptions.NoAccessTokenProvided: Invalid or expired session, please login again.

Any tips ?

Getting invalid URL error

Hi,
I am getting the following error while launching errbot with backend set to a mattermost instance.

15:20:12 ERROR    errbot.backends.base      Exception occurred in serve_once:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/errbot/backends/base.py", line 861, in serve_forever
    if self.serve_once():
  File "/root/errbot-root/errbot-mattermost-backend/mattermost.py", line 311, in serve_once
    self.driver.login()
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/driver.py", line 171, in login
    result = self.users.get_user('me')
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/endpoints/users.py", line 45, in get_user
    self.endpoint + '/' + user_id
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/client.py", line 193, in get
    response = self.make_request('get', endpoint, options=options, params=params)
  File "/usr/local/lib/python3.6/site-packages/mattermostdriver/client.py", line 162, in make_request
    response.raise_for_status()
  File "/usr/local/lib/python3.6/site-packages/requests/models.py", line 943, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 411 Client Error: Length Required for url: http://SERVER_IP_HERE:8065/mm:8065/api/v4/users/me
15:20:12 INFO     errbot.backends.base      Reconnecting in 1 seconds (0 attempted reconnections so far).
15:20:13 DEBUG    urllib3.connectionpool    Starting new HTTP connection (1): PROXY_IP_HERE
15:20:13 DEBUG    urllib3.connectionpool    http://PROXY_IP_HERE "GET http://SERVER_IP_HERE:8065/mm:8065/api/v4/users/me HTTP/1.1" 411 4390
15:20:13 ERROR    mattermostdriver.websocke <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>ERROR: The requested URL could not be retrieved</title>

This is followed by html data of a webpage with the message "The requested URL could not be retrieved"


I have configured Mattermost such that the URL (http://SERVER_IP_HERE:8065/mm:8065/api/v4/users/me) automatically redirects on Mattermost login page (tested the same on another server).

There should not be any access related issue as Mattermost and Errbot are installed on the same server (represented here with SERVER_IP_HERE).

Let me know if any additional information is required.
Please Help!

examples for various usage and add them in documentation

how is it sound like to create the examples code for various usage and add those sample codes in the documentation.

it is just like my usage (posting files to channels) , there is less support in stackoverflow , and documentation will help for those who don't know how web/rest communication works in web development.

Compliance.Create_Report Method Doesn't Take Data Variables

I am trying to create a compliance report via this project through compliance.py->create_report(). I have been able to create a Driver object, login, and run other commands in users.py and the get_reports method. Looking through the documentation here, mattermost is expecting data variables to specify how the report should be created such as time range of compliance report and description. However, the create_report method doesn't take any parameters and trying to pass any variables into it will throw a parameter error due to its method signature not allowing for them. Trying to run the method as is will raise InvalidOrMissingParameters(message):Invalid description.

I have tested that the endpoint works on my server, I ran curl commands as specified in the documentation above and was able to see new reports created when running the get_reports method.

Please don't use `basicConfig`.

Hello!

What?
Please change:

logging.basicConfig(level=logging.INFO)
log = logging.getLogger('mattermostdriver. ...')

to:

log = logging.getLogger('mattermostdriver. ...')
log.setLevel(logging.INFO)

i.e. do not configure the root logger, but only the current logger.

Why?

logging.basicConfig(**kwargs)
...
This function does nothing if the root logger already has handlers configured for it.

So this will not work as expected:

import logging
import mattermostdriver # calls logging.basicConfig
logging.basicConfig(level=logging.DEBUG) # does nothing
logging.debug("Test") # output suppressed

This would print the desired output but the import order is wrong:

import logging
logging.basicConfig(level=logging.DEBUG)
import mattermostdriver
logging.debug("Test")

Thank you for your work!

Asyncio Support

I like your library, and I'm thinking about integrating it with my application, but it currently only supports asyncio for the websockets part of the library. I would like to extend this to the rest of the library. I would be willing to investigate migrating all of the HTTP calls from requests to aiohttp, is async something that you'd like to see integrated into the mainline?

make port as optional when making the requests

facing issue when i am trying to log in the mattermost using the package.

in package port is defined and is used in when making requests to the mattermost server.

ps . i dont know my port of server.

if i make the request satement to the server with out port i am able to login into the system

so i am proposing to make port optional in url

ie https://server_url:port/basepath/endpoint to https://server_url/basepath/endpoint

Update Configuration API

Thank you for helping me for creating access token.

By the way, I also want to use update configuration API like below.
https://api.mattermost.com/#tag/system%2Fpaths%2F~1config%2Fget

According to the documentation, update_conf API needs argument including new configuration(json type).

But, it looks like that your "update_configuration" fuction doesn't have any argument.
How to use it ??

def update_configuration(self):
return self.client.put(
'/config'
)

Thank you.

`websocksets.ConnectionClosedError` may be raised in keepalive situations

See #86 (comment)

I believe websocksets.ConnectionClosedError may be raised here in situations where one would want the keepalive to persist. For instance, if prototyping a bot on a laptop that suspends sometimes, you may get something like:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/websockets-8.1-py3.9-linux-x86_64.egg/websockets/protocol.py", line 827, in transfer_data
    message = await self.read_message()
  File "/usr/local/lib/python3.9/dist-packages/websockets-8.1-py3.9-linux-x86_64.egg/websockets/protocol.py", line 895, in read_message
    frame = await self.read_data_frame(max_size=self.max_size)
  File "/usr/local/lib/python3.9/dist-packages/websockets-8.1-py3.9-linux-x86_64.egg/websockets/protocol.py", line 971, in read_data_frame
    frame = await self.read_frame(max_size)
  File "/usr/local/lib/python3.9/dist-packages/websockets-8.1-py3.9-linux-x86_64.egg/websockets/protocol.py", line 1047, in read_frame
    frame = await Frame.read(
  File "/usr/local/lib/python3.9/dist-packages/websockets-8.1-py3.9-linux-x86_64.egg/websockets/framing.py", line 105, in read
    data = await reader(2)
  File "/usr/lib/python3.9/asyncio/streams.py", line 723, in readexactly
    await self._wait_for_data('readexactly')
  File "/usr/lib/python3.9/asyncio/streams.py", line 517, in _wait_for_data
    await self._waiter
  File "/usr/lib/python3.9/asyncio/selector_events.py", line 856, in _read_ready__data_received
    data = self._sock.recv(self.max_size)
ConnectionResetError: [Errno 104] Connection reset by peer

The above exception was the direct cause of the following exception:

File "/home/pde/src/mmpy_bot/mmpy_bot/bot.py", line 88, in run
self.event_handler.start()
File "/home/pde/src/mmpy_bot/mmpy_bot/event_handler.py", line 44, in start
self.driver.init_websocket(self._handle_event)
File "/usr/local/lib/python3.9/dist-packages/mattermostdriver-7.2.0-py3.9.egg/mattermostdriver/driver.py", line 148, in init_websocket
File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/usr/local/lib/python3.9/dist-packages/mattermostdriver-7.2.0-py3.9.egg/mattermostdriver/websocket.py", line 51, in connect
File "/usr/local/lib/python3.9/dist-packages/mattermostdriver-7.2.0-py3.9.egg/mattermostdriver/websocket.py", line 63, in _start_loop
File "/usr/lib/python3.9/asyncio/tasks.py", line 481, in wait_for
return fut.result()
File "/usr/local/lib/python3.9/dist-packages/mattermostdriver-7.2.0-py3.9.egg/mattermostdriver/websocket.py", line 108, in _wait_for_message
File "/usr/local/lib/python3.9/dist-packages/websockets-8.1-py3.9-linux-x86_64.egg/websockets/protocol.py", line 509, in recv
await self.ensure_open()
File "/usr/local/lib/python3.9/dist-packages/websockets-8.1-py3.9-linux-x86_64.egg/websockets/protocol.py", line 803, in ensure_open
raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: code = 1006 (connection closed abnormally [internal]), no reason


_Originally posted by @pde in https://github.com/Vaelor/python-mattermost-driver/pull/86#discussion_r601958017_

Gitlab LDAP login

hello

Thank you for your giving great useful OSS for mattermost.

I'm using mattermost with Gitlab-LDAP authentication.
I'd like to use this driver with Gitlab-LDAP account instead of mattermost standard account.

Is there somehow I can use this driver through Gitlab-LDAP login ?

I can get tokens & cookies from python requests module as below.

#######################################
session = requests.session()

get_auth_token = session.get(mm+'/oauth/gitlab/login')

pq_auth_token = pq(get_auth_token.content)
csrf_token = pq_auth_token('#new_ldap_user input[name="authenticity_token"]')[0].value

payload = {'authenticity_token': csrf_token, 'username': userid, 'password': password}

ldap_login = session.post(
gitlab + '/users/auth/ldapmain/callback',
data=payload,
)

for i,ldap_history in enumerate(ldap_login.history):
if i == 2:
authenticated_response = ldap_history

mm_cookie = authenticated_response.cookies
#######################################

Thank you.

Docker environment to run tests

I really want to have tests in this project.

What I am thinking of is something simple for the start.

  • Mattermost docker instance
  • A test file that tests the basic things
    • Does it even start
    • Does the login work
    • Does the websocket work
    • Can I make successful post/put/get/delete request

This probably shouldn't test every api endpoint...

Setting debug to true sometimes causes hanging with issuing commands

Here is the code that I have:

# mattermost driver settings
driver = Driver({
        'url': '<url>',
        'login_id': '<email>',
        'password': '<password>',
        'port': <port>,
        'debug': True,
})

# log into mattermost
driver.login()

# make a post in mattermost
driver.posts.create_post(options={
        'channel_id': '<id>',
        'message': '<message>'
})

When using this code, the driver.posts.create_post method sometimes hangs indefinitely without any debug output. It is seemingly random as to when this happens.

errors are unconditionally logged to terminal

While writing some code against our Mattermost, I discovered that exceptions are being logged, even though my code is handling them. For example, ResourceNotFound wrote the following to my terminal:

Unable to find an existing account matching your username for this team. This team may require an invite from the team owner to join.

My script dealt with the situation, and so this logging is unwanted noise.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.