]> git.somenet.org - pub/jan/mattermost-api-python.git/blob - mattermost/__init__.py
Setup python package + fork off MM-API stuff from other repo.
[pub/jan/mattermost-api-python.git] / mattermost / __init__.py
1 #!/usr/bin/env python3
2 """
3 Someone's Mattermost API v4 bindings.
4   Copyright (c) 2016-2021 by Someone <someone@somenet.org> (aka. Jan Vales <jan@jvales.net>)
5   published under MIT-License
6 """
7
8 import logging
9 import json
10 import random
11 import warnings
12
13 import requests
14
15 from .version import __version__
16
17 logger = logging.getLogger("mattermost")
18
19
20 class ApiException(Exception):
21     """ if exc == True: Thrown when any status_code >=400 gets returned """
22
23
24 class MMApi:
25     """Mattermost API v4 bindings."""
26
27     def __init__(self, url):
28         self._url = url
29         self._bearer = None
30
31         # temp/retrieved data.
32         self._my_user_id = None
33
34         # the only way to detect our session :/
35         self._my_user_agent = "SomeMMApi-"+__version__+"-"+str(random.randrange(100000000000, 999999999999))
36         self._headers = requests.utils.default_headers()
37         self._headers.update({"User-Agent": self._headers["User-Agent"]+" "+self._my_user_agent})
38
39
40     def _get(self, endpoint, params=None, raw=False, exc=True):
41         """
42         Do a get-request.
43
44         Args:
45             endpoint (string): API-Endpoint to call, including /v4/..., excluding /api/
46             params (dict, optional): url-parameters-dict
47             raw (bool, optional): return result raw (useful if it is a file download)
48             exc (bool, optional): Throw exceptions if error code >= 400 (default=True. Can be disabled for backward-compatability for a while)
49
50         Returns:
51             dict: requested data.
52
53         Raises:
54             ApiException: If exc==True and a non-OK HTTP-Statuscode is received.
55         """
56         res = requests.get(self._url + endpoint, headers=self._headers, params=params)
57         if raw:
58             return res
59
60         logger.info("[GET] %s --> %d", endpoint, res.status_code)
61         logger.debug(json.dumps(json.loads(res.text), indent=4))
62         if exc and res.status_code >= 400:
63             raise ApiException(json.loads(res.text))
64
65         return json.loads(res.text)
66
67
68     def _put(self, endpoint, params=None, data=None, exc=True):
69         """
70         Do a put-request.
71
72         Args:
73             endpoint (string): API-Endpoint to call, including /v4/..., excluding /api/
74             params (dict, optional): url-parameters-dict
75             data (dict, optional): json-data to put, if any
76             raw (bool, optional): return result raw (useful if it is a file download)
77             exc (bool, optional): Throw exceptions if error code >= 400 (default=True. Can be disabled for backward-compatability for a while)
78
79         Returns:
80             dict: requested data.
81
82         Raises:
83             ApiException: If exc==True and a non-OK HTTP-Statuscode is received.
84         """
85         logger.debug(json.dumps(data, indent=4))
86         res = requests.put(self._url + endpoint, headers=self._headers, params=params, data=json.dumps(data))
87         logger.info("[PUT] %s --> %d", endpoint, res.status_code)
88         logger.debug(json.dumps(json.loads(res.text), indent=4))
89         if exc and res.status_code >= 400:
90             raise ApiException(json.loads(res.text))
91
92         return json.loads(res.text)
93
94
95     def _post(self, endpoint, params=None, data=None, multipart_formdata=None, exc=True):
96         """
97         Do a post-request.
98
99         Args:
100             endpoint (string): API-Endpoint to call, including /v4/..., excluding /api/
101             params (dict, optional): url-parameters-dict
102             data (dict, optional): json-data to post, if any
103             exc (bool, optional): Throw exceptions if error code >= 400 (default=True. Can be disabled for backward-compatability for a while)
104
105         Returns:
106             dict: requested data.
107
108         Raises:
109             ApiException: If exc==True and a non-OK HTTP-Statuscode is received.
110         """
111         logger.debug(json.dumps(data, indent=4))
112         if data is not None:
113             data = json.dumps(data)
114
115         res = requests.post(self._url + endpoint, headers=self._headers, params=params, data=data, files=multipart_formdata)
116         logger.info("[POST] %s --> %d", endpoint, res.status_code)
117         logger.debug(json.dumps(json.loads(res.text), indent=4))
118         if exc and res.status_code >= 400:
119             raise ApiException(json.loads(res.text))
120
121         return json.loads(res.text)
122
123
124     def _delete(self, endpoint, params=None, data=None, exc=True):
125         """
126         Do a delete-request.
127
128         Args:
129             endpoint (string): API-Endpoint to call, including /v4/..., excluding /api/
130             params (dict, optional): url-parameters-dict
131             data (dict, optional): json-data to delete, if any
132             exc (bool, optional): Throw exceptions if error code >= 400 (default=True. Can be disabled for backward-compatability for a while)
133
134         Returns:
135             dict: requested data.
136
137         Raises:
138             ApiException: If exc==True and a non-OK HTTP-Statuscode is received.
139         """
140         logger.debug(json.dumps(data, indent=4))
141         res = requests.delete(self._url + endpoint, headers=self._headers, params=params, data=json.dumps(data))
142         logger.info("[DELETE] %s --> %d", endpoint, res.status_code)
143         logger.debug(json.dumps(json.loads(res.text), indent=4))
144         if exc and res.status_code >= 400:
145             raise ApiException(json.loads(res.text))
146
147         return json.loads(res.text)
148
149
150 ################################################
151 #+ **LOGIN/LOGOUT**
152
153
154     # login is special - dont use helpers above. We also need login to be called, even if bearer is used, so we know out user-id and session-id.
155     def login(self, login_id=None, password=None, token=None, bearer=None):
156         """
157         Login to the corresponding (self._url) mattermost instance.
158         Use login_id and password, optionally token (totp)
159         If bearer is passed this token is used instead.
160
161         Unlike the MM-api this package requires to login() with bearer-tokens, because otherwise we cannot reliably and/or non-ugly get stuff like the user's id.
162         """
163         if self._bearer:
164             logger.warning("Already logged in. Ignoring new attempt.")
165             return None
166
167         # note: bearer vs self._bearer
168         if not bearer:
169             props = {"login_id": login_id, "password": password, "token":token}
170             res = requests.post(self._url + "/v4/users/login", headers=self._headers, data=json.dumps(props))
171
172             if res.status_code != 200:
173                 logger.critical("User-Login failed: %d", res.status_code)
174                 return None
175
176             self._bearer = str(res.headers["Token"])
177
178         else:
179             self._bearer = bearer
180
181         logger.info("Token Bearer: %s", self._bearer)
182         self._headers.update({"Authorization": "Bearer "+self._bearer})
183
184         if bearer:
185             res = requests.get(self._url + "/v4/users/me", headers=self._headers)
186             if res.status_code != 200:
187                 logger.critical("Bearer-Login failed: %d", res.status_code)
188                 return None
189
190         # also store our user_id
191         ret = json.loads(res.text)
192         self._my_user_id = ret["id"]
193         return ret
194
195
196     def logout(self, **kwargs):
197         """
198         This will end the session at the server and invalidate this MMApi-object.
199         """
200         return self._post("/v4/users/logout", **kwargs)
201
202
203
204 ################################################
205 #+ **USERS**
206
207
208     #def create_user() #NOT_IMPLEMENTED
209
210
211
212     def get_users(self, in_team=None, not_in_team=None, in_channel=None, not_in_channel=None, group_constrained=None, without_team=None, sort=None, **kwargs):
213         """
214         Generator: iterates over all users. Results can be restricted based on other parameters.
215
216         Args:
217             in_team (string, optional): see MM-API doc.
218             not_in_team (string, optional): see MM-API doc.
219             in_channel (string, optional): see MM-API doc.
220             not_in_channel (string, optional): see MM-API doc.
221             group_constrained (bool, optional): see MM-API doc.
222             without_team (bool, optional): see MM-API doc.
223             sort (string, optional): see MM-API doc.
224
225         Returns:
226             generates: One User at a time.
227
228         Raises:
229             ApiException: Passed on from lower layers.
230         """
231         page = 0
232         while True:
233             data_page = self._get("/v4/users", params={
234                 "page":str(page),
235                 "per_page":"200",
236                 **({"in_team": in_team} if in_team else {}),
237                 **({"not_in_team": not_in_team} if not_in_team else {}),
238                 **({"not_in_team": not_in_team} if not_in_team else {}),
239                 **({"in_channel": in_channel} if in_channel else {}),
240                 **({"not_in_channel": not_in_channel} if not_in_channel else {}),
241                 **({"group_constrained": group_constrained} if group_constrained else {}),
242                 **({"without_team": without_team} if without_team else {}),
243                 **({"sort": sort} if sort else {}),
244             }, **kwargs)
245
246             if data_page == []:
247                 break
248             page += 1
249
250             for data in data_page:
251                 yield data
252
253
254
255     def get_users_by_ids_list(self, user_ids_list, **kwargs): #UNTESTED
256         """
257         Get a list of users based on a provided list of user ids.
258
259         Args:
260             user_ids_list (list): see MM-API doc.
261
262         Returns:
263             list: of Users.
264
265         Raises:
266             ApiException: Passed on from lower layers.
267         """
268         return self._post("/v4/users/ids", data=user_ids_list, **kwargs)
269
270
271
272     def get_users_by_group_channel_ids_list(self, group_channel_ids_list, **kwargs): #UNTESTED
273         """
274         Get an object containing a key per group channel id in the query and its value as a list of users members of that group channel.
275
276         Args:
277             group_channel_ids_list (list): see MM-API doc.
278
279         Returns:
280             list: of channel_ids: list of Users.
281
282         Raises:
283             ApiException: Passed on from lower layers.
284         """
285         return self._post("/v4/users/group_channels", data=group_channel_ids_list, **kwargs)
286
287
288
289     def get_users_by_usernames_list(self, usernames_list, **kwargs):
290         """
291         Get a list of users based on a provided list of usernames.
292
293         Args:
294             usernames_list (list): see MM-API doc.
295
296         Returns:
297             list: of Users.
298
299         Raises:
300             ApiException: Passed on from lower layers.
301         """
302         return self._post("/v4/users/usernames", data=usernames_list, **kwargs)
303
304
305
306     #def search_users() #NOT_IMPLEMENTED
307     #def autocomplete_users() #NOT_IMPLEMENTED
308     #def get_user_ids_of_known_users() #NOT_IMPLEMENTED
309     #def get_total_count_of_users_in_system() #NOT_IMPLEMENTED
310
311
312
313     def get_user(self, user_id=None, **kwargs):
314         """
315         Get a list of users based on a provided list of usernames.
316
317         Args:
318             user_id (string, optional): if not given, returns Data on calling user. (/me)
319
320         Returns:
321             dict: User.
322
323         Raises:
324             ApiException: Passed on from lower layers.
325         """
326         if user_id is None:
327             return self._get("/v4/users/me", **kwargs)
328
329         return self._get("/v4/users/"+user_id, **kwargs)
330
331
332
333     #def update_user() #NOT_IMPLEMENTED: # use patch_user
334     #def deactivate_user() #NOT_IMPLEMENTED
335
336
337
338     def patch_user(self, user_id, props=None, **kwargs):
339         """
340         Partially update a user by providing only the fields you want to update.
341
342         Args:
343             user_id (string): User to patch.
344             props (dict, optional): fields you want to update.
345
346         Returns:
347             dict: User.
348
349         Raises:
350             ApiException: Passed on from lower layers.
351         """
352         return self._put("/v4/users/"+user_id+"/patch", data=props, **kwargs)
353
354
355
356     #def update_user_roles() #NOT_IMPLEMENTED
357     #def update_user_active_status() #NOT_IMPLEMENTED
358     #def get_user_profile_image() #NOT_IMPLEMENTED
359     #def set_user_profile_image() #NOT_IMPLEMENTED
360     #def delete_user_profile_image() #NOT_IMPLEMENTED
361     #def get_user_default_profile_image() #NOT_IMPLEMENTED
362
363
364
365     def get_user_by_username(self, username, **kwargs):
366         """
367         Get a user object by providing a username.
368         Sensitive information will be sanitized out.
369
370         Args:
371             username (string): User's username.
372
373         Returns:
374             dict: User.
375
376         Raises:
377             ApiException: Passed on from lower layers.
378         """
379         return self._get("/v4/users/username/"+username, **kwargs)
380
381
382
383     #def reset_user_password() #NOT_IMPLEMENTED
384     #def update_user_mfa() #NOT_IMPLEMENTED
385     #def generate_user_mfa_secret() #NOT_IMPLEMENTED
386
387
388
389     def demote_a_user(self, user_id, **kwargs):
390         """
391         Convert a regular user into a guest.
392         This will convert the user into a guest for the whole system while retaining their existing team and channel memberships.
393
394         Args:
395             user_id (string): User to demote.
396
397         Returns:
398             string: Status.
399
400         Raises:
401             ApiException: Passed on from lower layers.
402         """
403         return self._post("/v4/users/"+user_id+"/demote", **kwargs)
404
405
406
407     def promote_a_guest(self, user_id, **kwargs):
408         """
409         Convert a guest into a regular user.
410         This will convert the guest into a user for the whole system while retaining any team and channel memberships and automatically joining them to the default channels.
411
412         Args:
413             user_id (string): User to promote   .
414
415         Returns:
416             string: Status.
417
418         Raises:
419             ApiException: Passed on from lower layers.
420         """
421         return self._post("/v4/users/"+user_id+"/promote", **kwargs)
422
423
424
425     #def check_user_mfa() #NOT_IMPLEMENTED
426     #def update_user_password() #NOT_IMPLEMENTED
427     #def send_user_password_reset_mail() #NOT_IMPLEMENTED
428     #def get_user_by_email() #NOT_IMPLEMENTED
429
430
431
432     def get_user_sessions(self, user_id=None, **kwargs):
433         """
434         Get a list of sessions by providing the user GUID. Sensitive information will be sanitized out.
435
436         Args:
437             user_id (string, optional): if not given, returns Data on logged in user. (/me)
438
439         Returns:
440             list: of dicts of Sessions.
441
442         Raises:
443             ApiException: Passed on from lower layers.
444         """
445         if user_id is None:
446             user_id = self._my_user_id
447
448         return self._get("/v4/users/"+user_id+"/sessions", **kwargs)
449
450
451
452     def revoke_user_session(self, user_id=None, session_id=None, **kwargs):
453         """
454         Revokes a user session from the provided user id and session id strings.
455         Previously this was the only way to logout.
456         Please migrate to logout()
457
458         """
459         if not user_id and not session_id:
460             warnings.warn("revoke_user_session() without arguments is deprecated; use logout().", category=DeprecationWarning)
461
462         if user_id is None:
463             user_id = self._my_user_id
464
465         if session_id is None:
466             session_id = self._bearer
467
468         return self._post("/v4/users/"+user_id+"/sessions/revoke", data={"session_id": session_id}, **kwargs)
469
470
471
472     #def revoke_all_user_sessions() #NOT_IMPLEMENTED
473     #def attach_mobile_device_to_user_session() #NOT_IMPLEMENTED
474     #def get_user_audit() #NOT_IMPLEMENTED
475     #def admin_verify_user_email_() #NOT_IMPLEMENTED
476     #def verify_user_email_() #NOT_IMPLEMENTED
477     #def send_user_email_verification() #NOT_IMPLEMENTED
478     #def switch_user_login_method() #NOT_IMPLEMENTED
479     #def create_user_access_token() #NOT_IMPLEMENTED
480     #def get_user_access_tokens() #NOT_IMPLEMENTED
481     #def revoke_user_access_token() #NOT_IMPLEMENTED
482     #def get_user_access_token() #NOT_IMPLEMENTED
483     #def disable_user_access_token() #NOT_IMPLEMENTED
484     #def enable_user_access_token() #NOT_IMPLEMENTED
485     #def search_user_access_tokens() #NOT_IMPLEMENTED
486     #def update_user_auth_method() #NOT_IMPLEMENTED
487     #def record_user_action_custom_tos() #NOT_IMPLEMENTED
488     #def fetch_user_latest_accepted_custom_tos() #NOT_IMPLEMENTED
489     #def revoke_all_users_all_sessions() #NOT_IMPLEMENTED #MM, ARE YOU INSANE?!
490
491
492
493 ################################################
494 #+ **BOTS** #NOT_IMPLEMENTED
495
496
497
498 ################################################
499 #+ **TEAMS**
500
501
502
503     #def create_team() #NOT_IMPLEMENTED
504
505
506
507     def get_teams(self, include_total_count=None, **kwargs):
508         """
509         Generator: Get regular users only returns open teams. Users with the "manage_system" permission will return teams regardless of type.
510
511         Args:
512             include_total_count (bool, optional): see MM-API doc.
513
514         Returns:
515             generates: One Team at a time.
516
517         Raises:
518             ApiException: Passed on from lower layers.
519         """
520         page = 0
521         while True:
522             data_page = self._get("/v4/teams", params={
523                 "page":str(page),
524                 **({"include_total_count": include_total_count} if include_total_count else {}),
525             }, **kwargs)
526
527             if data_page == []:
528                 break
529             page += 1
530
531             for data in data_page:
532                 yield data
533
534
535
536     def get_team(self, team_id, **kwargs):
537         """
538         Get a team on the system.
539
540         Args:
541             team_id (string): team_id.
542
543         Returns:
544             dict: Team.
545
546         Raises:
547             ApiException: Passed on from lower layers.
548         """
549         return self._get("/v4/teams/"+team_id, **kwargs)
550
551
552
553     #def update_team() #NOT_IMPLEMENTED
554     #def delete_team() #NOT_IMPLEMENTED
555     #def patch_team() #NOT_IMPLEMENTED
556     #def update_team_privacy() #NOT_IMPLEMENTED
557     #def restore_team() #NOT_IMPLEMENTED
558     #def get_team_by_name() #NOT_IMPLEMENTED
559     #def search_teams() #NOT_IMPLEMENTED
560     #def exists_team() #NOT_IMPLEMENTED
561     #def get_teams_for_user() #NOT_IMPLEMENTED
562
563
564
565     def get_team_members(self, team_id, **kwargs):
566         """
567         Generator: Get a page team members list.
568
569         Args:
570             team_id (string): team_id.
571
572         Returns:
573             generates: One User at a time.
574
575         Raises:
576             ApiException: Passed on from lower layers.
577         """
578         page = 0
579         while True:
580             data_page = self._get("/v4/teams/"+team_id+"/members", params={"page":str(page)}, **kwargs)
581
582             if data_page == []:
583                 break
584             page += 1
585
586             for data in data_page:
587                 yield data
588
589
590
591     def add_user_to_team(self, team_id, user_id, **kwargs):
592         """
593         Add user to the team by user_id.
594
595         Args:
596             team_id (string): team_id to add the user to.
597             user_id (string): user_id to add to team.
598
599         Returns:
600             dict: Teammembership.
601
602         Raises:
603             ApiException: Passed on from lower layers.
604         """
605         return self._post("/v4/teams/"+team_id+"/members", data={
606             "team_id": team_id,
607             "user_id": user_id,
608         }, **kwargs)
609
610
611
612     #def add_user_to_team_from_invite() #NOT_IMPLEMENTED
613     #def add_multiple_users_to_team() #NOT_IMPLEMENTED WHY?!
614     #def get_team_members_for_a_user() #NOT_IMPLEMENTED WHY NOT NAMING STUFF USEFULLY?!
615
616
617
618     def get_team_member(self, team_id, user_id, **kwargs):
619         """
620         Add user to the team by user_id.
621
622         Args:
623             team_id (string): team_id to add the user to.
624             user_id (string): user_id to add to team.
625
626         Returns:
627             dict: Teammembership.
628
629         Raises:
630             ApiException: Passed on from lower layers.
631         """
632         return self._get("/v4/teams/"+team_id+"/members/"+user_id, **kwargs)
633
634
635
636     def remove_user_from_team(self, team_id, user_id, **kwargs):
637         """
638         Delete the team member object for a user, effectively removing them from a team.
639
640         Args:
641             team_id (string): team_id to remove the user from.
642             user_id (string): user_id to remove.
643
644         Returns:
645             dict: status.
646
647         Raises:
648             ApiException: Passed on from lower layers.
649         """
650         return self._delete("/v4/teams/"+team_id+"/members/"+user_id, **kwargs)
651
652
653
654     #def get_team_members_by_id() #NOT_IMPLEMENTED
655     #def get_team_stats() #NOT_IMPLEMENTED
656     #def regenerate_team_invite_id() #NOT_IMPLEMENTED
657     #def get_team_icon() #NOT_IMPLEMENTED
658     #def set_team_icon() #NOT_IMPLEMENTED
659     #def remove_team_icon() #NOT_IMPLEMENTED
660     #def update_team_members_roles() #NOT_IMPLEMENTED
661
662
663
664     def update_team_members_scheme_roles(self, team_id, user_id, props, **kwargs):
665         """
666         Update a team member's scheme_admin/scheme_user properties.
667         Typically this should either be {scheme_admin=false, scheme_user=true} for ordinary team member, or {scheme_admin=true, scheme_user=true} for a team admin.
668
669         Args:
670             team_id (string): obvious
671             user_id (string): obvious
672             props (dict): see MM-API docs.
673
674         Returns:
675             dict: status.
676
677         Raises:
678             ApiException: Passed on from lower layers.
679         """
680         return self._put("/v4/teams/"+team_id+"/members/"+user_id+"/schemeRoles", data=props, **kwargs)
681
682
683
684     #def get_team_unreads_for_user() #NOT_IMPLEMENTED
685     #def get_team_unreads() #NOT_IMPLEMENTED
686     #def invite_users_to_team_by_email() #NOT_IMPLEMENTED
687     #def invite_guests_to_team_by_email() #NOT_IMPLEMENTED
688     #def invalidate_invites_to_team_by_email() #NOT_IMPLEMENTED
689     #def import_team() #NOT_IMPLEMENTED
690     #def get_team_invite_info() #NOT_IMPLEMENTED
691     #def set_team_scheme() #NOT_IMPLEMENTED
692     #def get_team_members_minus_group_members() #NOT_IMPLEMENTED
693
694
695
696     def get_team_channels(self, team_id, **kwargs): #This belongs here, not to channels!
697         """
698         Generator: Get a page of public channels on a team.
699
700         Args:
701             team_id (string): team to get channels from.
702
703         Returns:
704             generates: Channel.
705
706         Raises:
707             ApiException: Passed on from lower layers.
708         """
709         page = 0
710         while True:
711             data_page = self._get("/v4/teams/"+team_id+"/channels", params={"page":str(page)}, **kwargs)
712
713             if data_page == []:
714                 break
715             page += 1
716
717             for data in data_page:
718                 yield data
719
720
721
722 ################################################
723 #+ **CHANNELS**
724
725
726
727     #def get_all_channels() #NOT_IMPLEMENTED NOT USEFUL AT ALL!
728
729
730
731     def create_channel(self, team_id, name, display_name, purpose=None, header=None, chan_type="O", **kwargs):
732         """
733         Create a new channel.
734
735         Args:
736             team_id (string): The team ID of the team to create the channel on.
737             name (string): The unique handle for the channel, will be present in the channel URL.
738             display_name (string): see MM-API docs.
739             purpose (string, optional): see MM-API docs.
740             header (string, optional): see MM-API docs.
741             chan_type (string, default: public): see MM-API docs.
742
743         Returns:
744             dict: created Channel.
745
746         Raises:
747             ApiException: Passed on from lower layers.
748         """
749         return self._post("/v4/channels", data={
750             "team_id": team_id,
751             "name": name,
752             "display_name": display_name,
753             **({"purpose": purpose} if purpose else {}),
754             **({"header": header} if header else {}),
755             "type": chan_type,
756         }, **kwargs)
757
758
759
760     def create_dm_channel_with(self, other_user_id, **kwargs):
761         """
762         Create a new direct message channel between two users.
763
764         Args:
765             other_user_id (string): The other user_id to create the cannel with.
766
767         Returns:
768             dict: created Channel.
769
770         Raises:
771             ApiException: Passed on from lower layers.
772         """
773         return self._post("/v4/channels/direct", data=[self._my_user_id, other_user_id], **kwargs)
774
775
776
777     def create_group_channel_with(self, other_user_ids_list, **kwargs): #UNTESTED
778         """
779         Create a new direct message channel between two users.
780
781         Args:
782             other_user_ids_list (list): List of user_ids to create the cannel with.
783
784         Returns:
785             dict: created Channel.
786
787         Raises:
788             ApiException: Passed on from lower layers.
789         """
790         return self._post("/v4/channels/group", data=other_user_ids_list, **kwargs)
791
792
793
794     #def search_all_private_and_public_channels() #NOT_IMPLEMENTED
795     #def search_all_users_group_channels() #NOT_IMPLEMENTED
796     #def get_team_channels_by_id() #NOT_IMPLEMENTED
797     #def get_timezones_of_users_in_channel() #NOT_IMPLEMENTED
798
799
800
801     def get_channel(self, channel_id, **kwargs):
802         """
803         Get channel from the provided channel id string.
804
805         Args:
806             channel_id (string): channel_id to get.
807
808         Returns:
809             dict: Channel.
810
811         Raises:
812             ApiException: Passed on from lower layers.
813         """
814         return self._get("/v4/channels/"+channel_id, **kwargs)
815
816
817
818     def update_channel(self, channel_id, props, **kwargs):
819         """
820         Update a channel. The fields that can be updated are listed as parameters. Omitted fields will be treated as blanks.
821
822         Args:
823             channel_id (string): channel_id to get.
824             props (dict, optional): fields you want to update.
825
826         Returns:
827             dict: Channel.
828
829         Raises:
830             ApiException: Passed on from lower layers.
831         """
832         return self._put("/v4/channels/"+channel_id, data=props, **kwargs)
833
834
835
836     def patch_channel(self, channel_id, props, **kwargs):
837         """
838         Partially update a channel by providing only the fields you want to update. Omitted fields will not be updated. The fields that can be updated are defined in the request body, all other provided fields will be ignored.
839
840         Args:
841             channel_id (string): channel_id to get.
842             props (dict, optional): fields you want to update.
843
844         Returns:
845             dict: Channel.
846
847         Raises:
848             ApiException: Passed on from lower layers.
849         """
850         return self._put("/v4/channels/"+channel_id+"/patch", data=props, **kwargs)
851
852
853
854     def get_channel_posts_pinned(self, channel_id, **kwargs):
855         """
856         Get a list of pinned posts for channel.
857
858         Args:
859             channel_id (string): channel_id to get pinned posts for.
860
861         Returns:
862             dict: Results.
863
864         Raises:
865             ApiException: Passed on from lower layers.
866         """
867         return self._get("/v4/channels/"+channel_id+"/pinned", **kwargs)
868
869
870
871     def search_channel(self, team_id, term, **kwargs):
872         """
873         Search public channels on a team based on the search term provided in the request body.
874
875         Args:
876             team_id (string): team_id to search in.
877             term (string): The search term to match against the name or display name of channels.
878
879         Returns:
880             list: of Channels.
881
882         Raises:
883             ApiException: Passed on from lower layers.
884         """
885         return self._post("/v4/teams/"+team_id+"/channels/search", data={"term": term}, **kwargs)
886
887
888
889     def get_channel_by_name(self, team_id, channel_name, include_deleted=None, **kwargs):
890         """
891         Gets channel from the provided team id and channel name strings.
892
893         Args:
894             team_id (string): team_id to search in.
895             term (string): The search term to match against the name or display name of channels.
896             include_deleted (bool, optional): see MM-API doc.
897
898         Returns:
899             dict: Channel.
900
901         Raises:
902             ApiException: Passed on from lower layers.
903         """
904         return self._get("/v4/teams/"+team_id+"/channels/name/"+channel_name, params={
905             **({"include_deleted": include_deleted} if include_deleted else {}),
906         }, **kwargs)
907
908
909
910     def get_channel_members(self, channel_id, **kwargs):
911         """
912         Generator: Members for a channel.
913
914         Args:
915             channel_id (string): channel_id to get the members for.
916
917         Returns:
918             generates: One Member at a time.
919
920         Raises:
921             ApiException: Passed on from lower layers.
922         """
923         page = 0
924         while True:
925             data_page = self._get("/v4/channels/"+channel_id+"/members", params={"page":str(page)}, **kwargs)
926
927             if data_page == []:
928                 break
929             page += 1
930
931             for data in data_page:
932                 yield data
933
934
935
936     def add_user_to_channel(self, channel_id, user_id, **kwargs):
937         """
938         Add a user to a channel by creating a channel member object.
939
940         Args:
941             channel_id (string): channel_id to add the user to.
942             user_id (string): user_id to add.
943
944         Returns:
945             dict: Membership.
946
947         Raises:
948             ApiException: Passed on from lower layers.
949         """
950         return self._post("/v4/channels/"+channel_id+"/members", data={"user_id": user_id}, **kwargs)
951
952
953
954     def get_channel_member(self, channel_id, user_id, **kwargs):
955         """
956         Gets channel from the provided team id and channel name strings.
957
958         Args:
959             channel_id (string): channel_id to get the members for.
960             user_id (string): user_id to get the member-data for.
961
962         Returns:
963             dict: Membership.
964
965         Raises:
966             ApiException: Passed on from lower layers.
967         """
968         return self._get("/v4/channels/"+channel_id+"/members/"+user_id, **kwargs)
969
970
971
972     def remove_user_from_channel(self, channel_id, user_id, **kwargs):
973         """
974         Add a user to a channel by creating a channel member object.
975
976         Args:
977             channel_id (string): channel_id to remove the user from.
978             user_id (string): user_id to remove.
979
980         Returns:
981             dict: status.
982
983         Raises:
984             ApiException: Passed on from lower layers.
985         """
986         return self._delete("/v4/channels/"+channel_id+"/members/"+user_id, **kwargs)
987
988
989
990     def update_channel_members_scheme_roles(self, channel_id, user_id, props, **kwargs):
991         """
992         Update a channel member's scheme_admin/scheme_user properties. Typically this should either be scheme_admin=false, scheme_user=true for ordinary channel member, or scheme_admin=true, scheme_user=true for a channel admin.
993
994         Args:
995             channel_id (string): see MM-API doc.
996             user_id (string): see MM-API doc.
997             props (dict): see MM-API doc.
998
999         Returns:
1000             list: of Channels.
1001
1002         Raises:
1003             ApiException: Passed on from lower layers.
1004         """
1005         return self._put("/v4/channels/"+channel_id+"/members/"+user_id+"/schemeRoles", data=props, **kwargs)
1006
1007
1008
1009     def get_channel_memberships_for_user(self, user_id, team_id, **kwargs):
1010         """
1011         Get all channel memberships and associated membership roles (i.e. channel_user, channel_admin) for a user on a specific team.
1012
1013         Args:
1014             user_id (string): see MM-API doc.
1015             team_id (string): see MM-API doc.
1016
1017         Returns:
1018             list: of Memberships.
1019
1020         Raises:
1021             ApiException: Passed on from lower layers.
1022         """
1023         return self._get("/v4/users/"+user_id+"/teams/"+team_id+"/channels/members", **kwargs)
1024
1025
1026
1027     def get_channels_for_user(self, user_id, team_id, **kwargs):
1028         """
1029         Get all the channels on a team for a user.
1030
1031         Args:
1032             user_id (string): see MM-API doc.
1033             team_id (string): see MM-API doc.
1034
1035         Returns:
1036             list: of Channels.
1037
1038         Raises:
1039             ApiException: Passed on from lower layers.
1040         """
1041         return self._get("/v4/users/"+user_id+"/teams/"+team_id+"/channels", **kwargs)
1042
1043
1044
1045 ################################################
1046 #+ **POSTS**
1047
1048
1049     def create_post(self, channel_id, message, props=None, filepaths=None, root_id=None, **kwargs):
1050         """
1051         Create a new post in a channel. To create the post as a comment on another post, provide root_id.
1052
1053         Args:
1054             channel_id (string): The channel ID to create the post in.
1055             message (string): The message text.
1056             props (string, optional): see MM-API docs.
1057             filepaths (list, optional): Paths to upload files from and attach to post.
1058             root_id (string, optional): see MM-API docs.
1059
1060         Returns:
1061             dict: created Post.
1062
1063         Raises:
1064             ApiException: Passed on from lower layers.
1065         """
1066         file_ids = []
1067         if filepaths:
1068             for filename in filepaths:
1069                 file_ids.append(self.upload_file(channel_id, filename, **kwargs)["id"])
1070
1071         return self._post("/v4/posts", data={
1072             "channel_id": channel_id,
1073             "message": message,
1074             **({"props": props} if props else {"props": {"from_webhook":"true"}}),
1075             "root_id":root_id,
1076             "file_ids": file_ids,
1077         }, **kwargs)
1078
1079
1080
1081     def create_ephemeral_post(self, channel_id, message, user_id, **kwargs):
1082         """
1083         Create a new ephemeral post in a channel.
1084
1085         Args:
1086             channel_id (string): The channel ID to create the post in.
1087             message (string): The message text.
1088             user_id (string): The user ID to display the post to.
1089
1090         Returns:
1091             dict: created Post.
1092
1093         Raises:
1094             ApiException: Passed on from lower layers.
1095         """
1096         return self._post("/v4/posts/ephemeral", data={
1097             "user_id": user_id,
1098             "post":{
1099                 "channel_id": channel_id,
1100                 "message": message,
1101             }
1102         }, **kwargs)
1103
1104
1105
1106     def get_post(self, post_id, **kwargs):
1107         """
1108         Get a single post.
1109
1110         Args:
1111             post_id (string): The post ID to get.
1112
1113         Returns:
1114             dict: Post.
1115
1116         Raises:
1117             ApiException: Passed on from lower layers.
1118         """
1119         return self._get("/v4/posts/"+post_id, **kwargs)
1120
1121
1122
1123     def delete_post(self, post_id, **kwargs):
1124         """
1125         Soft deletes a post, by marking the post as deleted in the database. Soft deleted posts will not be returned in post queries.
1126
1127         Args:
1128             post_id (string): The post ID to delete.
1129
1130         Returns:
1131             string: status.
1132
1133         Raises:
1134             ApiException: Passed on from lower layers.
1135         """
1136         return self._delete("/v4/posts/"+post_id, **kwargs)
1137
1138
1139
1140     def patch_post(self, post_id, message=None, is_pinned=None, props=None, **kwargs):
1141         """
1142         Partially update a post by providing only the fields you want to update. Omitted fields will not be updated. The fields that can be updated are defined in the request body, all other provided fields will be ignored.
1143
1144         Args:
1145             post_id (string): The post ID to patch.
1146             message (string, optional): see MM-API doc.
1147             is_pinned (bool, optional): see MM-API doc.
1148             props (dict, optional): see MM-API doc.
1149
1150         Returns:
1151             dict: Post.
1152
1153         Raises:
1154             ApiException: Passed on from lower layers.
1155         """
1156         return self._put("/v4/posts/"+post_id+"/patch", data={
1157             **({"message": message} if message else {}),
1158             **({"is_pinned": is_pinned} if is_pinned else {}),
1159             **({"props": props} if props else {}),
1160         }, **kwargs)
1161
1162
1163
1164     def get_posts_for_channel(self, channel_id, **kwargs):
1165         """
1166         Generator: Get a page of posts in a channel. Use the query parameters to modify the behaviour of this endpoint.
1167
1168         Args:
1169             channel_id (string): The channel ID to iterate over.
1170
1171         Returns:
1172             generates: Post.
1173
1174         Raises:
1175             ApiException: Passed on from lower layers.
1176         """
1177         page = 0
1178         while True:
1179             data_page = self._get("/v4/channels/"+channel_id+"/posts", params={"page":str(page)}, **kwargs)
1180
1181             if data_page["order"] == []:
1182                 break
1183             page += 1
1184
1185             for order in data_page["order"]:
1186                 yield data_page["posts"][order]
1187
1188
1189
1190 ################################################
1191 #+ **FILES**
1192
1193
1194     def upload_file(self, channel_id, filepath, **kwargs):
1195         """
1196         Uploads a file that can later be attached to a post.
1197
1198         Args:
1199             channel_id (string): The channel ID to upload to.
1200             filepath (string): The local path of the source.
1201
1202         Returns:
1203             dict: Uploaded File.
1204
1205         Raises:
1206             ApiException: Passed on from lower layers.
1207         """
1208         return self._post("/v4/files", multipart_formdata={'files':open(filepath, "rb"), "channel_id":(None, channel_id)}, **kwargs)["file_infos"][0]
1209
1210
1211
1212     def get_file(self, file_id, **kwargs):
1213         """
1214         Uploads a file that can later be attached to a post.
1215
1216         Args:
1217             file_id (string): The file ID to get.
1218
1219         Returns:
1220             binary: file-content.
1221
1222         Raises:
1223             ApiException: Passed on from lower layers.
1224         """
1225         return self._get("/v4/files/"+file_id, raw=True, **kwargs)
1226
1227
1228
1229 ################################################
1230 #+ **PREFERENCES** #NOT_IMPLEMENTED
1231
1232 ################################################
1233 #+ **STATUS** #NOT_IMPLEMENTED
1234
1235 ################################################
1236 #+ **EMOJI** #NOT_IMPLEMENTED
1237
1238 ################################################
1239 #+ **REACTIONS**
1240
1241
1242     def create_reaction(self, user_id, post_id, emoji_name, **kwargs):
1243         """
1244         Create a reaction.
1245
1246         Args:
1247             user_id (string): The ID of the user that made this reaction.
1248             post_id (string): The ID of the post to which this reaction was made.
1249             emoji_name (string): The name of the emoji that was used for this reaction.
1250
1251         Returns:
1252             dict: created Reaction.
1253
1254         Raises:
1255             ApiException: Passed on from lower layers.
1256         """
1257         return self._post("/v4/reactions", data={
1258             "user_id": user_id,
1259             "post_id": post_id,
1260             "emoji_name": emoji_name,
1261         }, **kwargs)
1262
1263
1264
1265 ################################################
1266 #+ **WEBHOOKS**
1267
1268
1269     def create_outgoing_hook(self, team_id, display_name, trigger_words, callback_urls, channel_id=None, description=None, trigger_when=0, **kwargs):
1270         """
1271         Create an outgoing webhook for a team.
1272
1273         Args:
1274             team_id (string): The ID of the team that the webhook watchs.
1275             display_name (string): The display name for this outgoing webhook.
1276             trigger_words (list): List of words for the webhook to trigger on.
1277             callback_urls (list): The URLs to POST the payloads to when the webhook is triggered.
1278             channel_id (string, optional): The ID of a public channel that the webhook watchs.
1279             description (string, optional): The description for this outgoing webhook.
1280             trigger_when (string, default int(0)): When to trigger the webhook, 0 when a trigger word is present at all and 1 if the message starts with a trigger word.
1281
1282         Returns:
1283             dict: created Webhook.
1284
1285         Raises:
1286             ApiException: Passed on from lower layers.
1287         """
1288         return self._post("/v4/hooks/outgoing", data={
1289             "team_id": team_id,
1290             "display_name": display_name,
1291             "trigger_words": trigger_words,
1292             "callback_urls": callback_urls,
1293             **({"channel_id": channel_id} if channel_id else {}),
1294             **({"description": description} if description else {}),
1295             "trigger_when": trigger_when,
1296             "content_type": "application/json",
1297         }, **kwargs)
1298
1299
1300
1301     def list_outgoing_hooks(self, team_id, channel_id=None, **kwargs):
1302         """
1303         Generator: Get a page of a list of outgoing webhooks. Optionally filter for a specific channel using query parameters.
1304
1305         Args:
1306             team_id (string): The ID of the team to get hooks for.
1307             channel_id (string, optional): The ID of the channel to get hooks for.
1308
1309         Returns:
1310             generates: One Webhook at a time.
1311
1312         Raises:
1313             ApiException: Passed on from lower layers.
1314         """
1315         return self._get("/v4/hooks/outgoing", params={
1316             "team_id":team_id,
1317             **({"channel_id": channel_id} if channel_id else {}),
1318         }, **kwargs)
1319
1320
1321
1322     def delete_outgoing_hook(self, hook_id, **kwargs):
1323         """
1324         Delete an outgoing webhook given the hook id.
1325
1326         Args:
1327             hook_id (string): The ID of the hook to delete.
1328
1329         Returns:
1330             string: status.
1331
1332         Raises:
1333             ApiException: Passed on from lower layers.
1334         """
1335         return self._delete("/v4/hooks/outgoing/"+hook_id, **kwargs)
1336
1337
1338
1339 ################################################
1340 #+ **COMMANDS**
1341
1342
1343     def create_slash_command(self, team_id, trigger, url, **kwargs):
1344         """
1345         Create a command for a team.
1346
1347         Args:
1348             team_id (string): Team ID to where the command should be created.
1349             trigger (string): Activation word to trigger the command.
1350             url (string): The URL that the command will make the request.
1351
1352         Returns:
1353             dict: created Command.
1354
1355         Raises:
1356             ApiException: Passed on from lower layers.
1357         """
1358         return self._post("/v4/commands", data={
1359             "team_id": team_id,
1360             "trigger": trigger,
1361             "url": url,
1362             "method": "P",
1363         }, **kwargs)
1364
1365
1366
1367     def list_custom_slash_commands_for_team(self, team_id, **kwargs):
1368         """
1369         List commands for a team.
1370
1371         Args:
1372             team_id (string): The ID of the team to get hooks for.
1373
1374         Returns:
1375             list: of Commands.
1376
1377         Raises:
1378             ApiException: Passed on from lower layers.
1379         """
1380         return self._get("/v4/commands", params={
1381             "team_id":team_id,
1382             "custom_only":True,
1383         }, **kwargs)
1384
1385
1386
1387     def update_slash_command(self, data, **kwargs):
1388         """
1389         Update a single command based on command id string and Command struct.
1390
1391         Args:
1392             data (dict): Command to update.
1393
1394         Returns:
1395             dict: updated Command.
1396
1397         Raises:
1398             ApiException: Passed on from lower layers.
1399         """
1400         return self._put("/v4/commands/"+data["id"], data=data, **kwargs)
1401
1402
1403
1404     def delete_slash_command(self, command_id, **kwargs):
1405         """
1406         Delete a command based on command id string.
1407
1408         Args:
1409             command_id (string): ID of the command to delete.
1410
1411         Returns:
1412             string: status.
1413
1414         Raises:
1415             ApiException: Passed on from lower layers.
1416         """
1417         return self._delete("/v4/commands/"+command_id, **kwargs)
1418
1419
1420
1421 ################################################
1422 #+ **OPENGRAPH** #NOT_IMPLEMENTED
1423
1424 ################################################
1425 #+ **SYSTEM** #NOT_IMPLEMENTED
1426
1427 ################################################
1428 #+ **BRAND** #NOT_IMPLEMENTED
1429
1430 ################################################
1431 #+ **OAUTH** #NOT_IMPLEMENTED
1432
1433 ################################################
1434 #+ **SAML** #NOT_IMPLEMENTED
1435
1436 ################################################
1437 #+ **LDAP** #NOT_IMPLEMENTED
1438
1439 ################################################
1440 #+ **GROUPS** #NOT_IMPLEMENTED
1441
1442 ################################################
1443 #+ **COMPLIANCE** #NOT_IMPLEMENTED
1444
1445 ################################################
1446 #+ **CLUSTER** #NOT_IMPLEMENTED
1447
1448 ################################################
1449 #+ **ELASTICSEARCH** #NOT_IMPLEMENTED
1450
1451 ################################################
1452 #+ **BLEVE** #NOT_IMPLEMENTED
1453
1454 ################################################
1455 #+ **DATARETENTION** #NOT_IMPLEMENTED
1456
1457 ################################################
1458 #+ **JOBS** #NOT_IMPLEMENTED
1459
1460 ################################################
1461 #+ **PLUGINS** #NOT_IMPLEMENTED
1462
1463 ################################################
1464 #+ **ROLES** #NOT_IMPLEMENTED
1465
1466 ################################################
1467 #+ **SCHEMES** #NOT_IMPLEMENTED
1468
1469 ################################################
1470 #+ **INTEGRATION_ACTIONS**
1471
1472
1473     def open_dialog(self, trigger_id, response_url, dialog, **kwargs):
1474         """
1475         Open an interactive dialog using a trigger ID provided by a slash command, or some other action payload. See https://docs.mattermost.com/developer/interactive-dialogs.html for more information on interactive dialogs.
1476
1477         Args:
1478             trigger_id (string): Trigger ID provided by other action.
1479             response_url (string): The URL to send the submitted dialog payload to.
1480             dialog (dict): Dialog definition.
1481
1482         Returns:
1483             string: status
1484
1485         Raises:
1486             ApiException: Passed on from lower layers.
1487         """
1488         return self._post("/v4/actions/dialogs/open", data={
1489             "trigger_id": trigger_id,
1490             "url": response_url,
1491             "dialog": dialog,
1492         }, **kwargs)
1493
1494
1495
1496 ################################################
1497 #+ **TERMS_OF_SERVICE** #NOT_IMPLEMENTED