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
15 from .version import __version__
17 logger = logging.getLogger("mattermost")
20 class ApiException(Exception):
21 """ if exc == True: Thrown when any status_code >=400 gets returned """
25 """Mattermost API v4 bindings."""
27 def __init__(self, url):
31 # temp/retrieved data.
32 self._my_user_id = None
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})
40 def _get(self, endpoint, params=None, raw=False, exc=True):
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)
54 ApiException: If exc==True and a non-OK HTTP-Statuscode is received.
56 res = requests.get(self._url + endpoint, headers=self._headers, params=params)
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))
65 return json.loads(res.text)
68 def _put(self, endpoint, params=None, data=None, exc=True):
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)
83 ApiException: If exc==True and a non-OK HTTP-Statuscode is received.
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))
92 return json.loads(res.text)
95 def _post(self, endpoint, params=None, data=None, multipart_formdata=None, exc=True):
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)
106 dict: requested data.
109 ApiException: If exc==True and a non-OK HTTP-Statuscode is received.
111 logger.debug(json.dumps(data, indent=4))
113 data = json.dumps(data)
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))
121 return json.loads(res.text)
124 def _delete(self, endpoint, params=None, data=None, exc=True):
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)
135 dict: requested data.
138 ApiException: If exc==True and a non-OK HTTP-Statuscode is received.
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))
147 return json.loads(res.text)
150 ################################################
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):
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.
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.
164 logger.warning("Already logged in. Ignoring new attempt.")
167 # note: bearer vs self._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))
172 if res.status_code != 200:
173 logger.critical("User-Login failed: %d", res.status_code)
176 self._bearer = str(res.headers["Token"])
179 self._bearer = bearer
181 logger.info("Token Bearer: %s", self._bearer)
182 self._headers.update({"Authorization": "Bearer "+self._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)
190 # also store our user_id
191 ret = json.loads(res.text)
192 self._my_user_id = ret["id"]
196 def logout(self, **kwargs):
198 This will end the session at the server and invalidate this MMApi-object.
200 return self._post("/v4/users/logout", **kwargs)
204 ################################################
208 #def create_user() #NOT_IMPLEMENTED
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):
214 Generator: iterates over all users. Results can be restricted based on other parameters.
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.
226 generates: One User at a time.
229 ApiException: Passed on from lower layers.
233 data_page = self._get("/v4/users", params={
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 {}),
250 for data in data_page:
255 def get_users_by_ids_list(self, user_ids_list, **kwargs): #UNTESTED
257 Get a list of users based on a provided list of user ids.
260 user_ids_list (list): see MM-API doc.
266 ApiException: Passed on from lower layers.
268 return self._post("/v4/users/ids", data=user_ids_list, **kwargs)
272 def get_users_by_group_channel_ids_list(self, group_channel_ids_list, **kwargs): #UNTESTED
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.
277 group_channel_ids_list (list): see MM-API doc.
280 list: of channel_ids: list of Users.
283 ApiException: Passed on from lower layers.
285 return self._post("/v4/users/group_channels", data=group_channel_ids_list, **kwargs)
289 def get_users_by_usernames_list(self, usernames_list, **kwargs):
291 Get a list of users based on a provided list of usernames.
294 usernames_list (list): see MM-API doc.
300 ApiException: Passed on from lower layers.
302 return self._post("/v4/users/usernames", data=usernames_list, **kwargs)
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
313 def get_user(self, user_id=None, **kwargs):
315 Get a list of users based on a provided list of usernames.
318 user_id (string, optional): if not given, returns Data on calling user. (/me)
324 ApiException: Passed on from lower layers.
327 return self._get("/v4/users/me", **kwargs)
329 return self._get("/v4/users/"+user_id, **kwargs)
333 #def update_user() #NOT_IMPLEMENTED: # use patch_user
334 #def deactivate_user() #NOT_IMPLEMENTED
338 def patch_user(self, user_id, props=None, **kwargs):
340 Partially update a user by providing only the fields you want to update.
343 user_id (string): User to patch.
344 props (dict, optional): fields you want to update.
350 ApiException: Passed on from lower layers.
352 return self._put("/v4/users/"+user_id+"/patch", data=props, **kwargs)
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
365 def get_user_by_username(self, username, **kwargs):
367 Get a user object by providing a username.
368 Sensitive information will be sanitized out.
371 username (string): User's username.
377 ApiException: Passed on from lower layers.
379 return self._get("/v4/users/username/"+username, **kwargs)
383 #def reset_user_password() #NOT_IMPLEMENTED
384 #def update_user_mfa() #NOT_IMPLEMENTED
385 #def generate_user_mfa_secret() #NOT_IMPLEMENTED
389 def demote_a_user(self, user_id, **kwargs):
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.
395 user_id (string): User to demote.
401 ApiException: Passed on from lower layers.
403 return self._post("/v4/users/"+user_id+"/demote", **kwargs)
407 def promote_a_guest(self, user_id, **kwargs):
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.
413 user_id (string): User to promote .
419 ApiException: Passed on from lower layers.
421 return self._post("/v4/users/"+user_id+"/promote", **kwargs)
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
432 def get_user_sessions(self, user_id=None, **kwargs):
434 Get a list of sessions by providing the user GUID. Sensitive information will be sanitized out.
437 user_id (string, optional): if not given, returns Data on logged in user. (/me)
440 list: of dicts of Sessions.
443 ApiException: Passed on from lower layers.
446 user_id = self._my_user_id
448 return self._get("/v4/users/"+user_id+"/sessions", **kwargs)
452 def revoke_user_session(self, user_id=None, session_id=None, **kwargs):
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()
459 if not user_id and not session_id:
460 warnings.warn("revoke_user_session() without arguments is deprecated; use logout().", category=DeprecationWarning)
463 user_id = self._my_user_id
465 if session_id is None:
466 session_id = self._bearer
468 return self._post("/v4/users/"+user_id+"/sessions/revoke", data={"session_id": session_id}, **kwargs)
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?!
493 ################################################
494 #+ **BOTS** #NOT_IMPLEMENTED
498 ################################################
503 #def create_team() #NOT_IMPLEMENTED
507 def get_teams(self, include_total_count=None, **kwargs):
509 Generator: Get regular users only returns open teams. Users with the "manage_system" permission will return teams regardless of type.
512 include_total_count (bool, optional): see MM-API doc.
515 generates: One Team at a time.
518 ApiException: Passed on from lower layers.
522 data_page = self._get("/v4/teams", params={
524 **({"include_total_count": include_total_count} if include_total_count else {}),
531 for data in data_page:
536 def get_team(self, team_id, **kwargs):
538 Get a team on the system.
541 team_id (string): team_id.
547 ApiException: Passed on from lower layers.
549 return self._get("/v4/teams/"+team_id, **kwargs)
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
565 def get_team_members(self, team_id, **kwargs):
567 Generator: Get a page team members list.
570 team_id (string): team_id.
573 generates: One User at a time.
576 ApiException: Passed on from lower layers.
580 data_page = self._get("/v4/teams/"+team_id+"/members", params={"page":str(page)}, **kwargs)
586 for data in data_page:
591 def add_user_to_team(self, team_id, user_id, **kwargs):
593 Add user to the team by user_id.
596 team_id (string): team_id to add the user to.
597 user_id (string): user_id to add to team.
600 dict: Teammembership.
603 ApiException: Passed on from lower layers.
605 return self._post("/v4/teams/"+team_id+"/members", data={
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?!
618 def get_team_member(self, team_id, user_id, **kwargs):
620 Add user to the team by user_id.
623 team_id (string): team_id to add the user to.
624 user_id (string): user_id to add to team.
627 dict: Teammembership.
630 ApiException: Passed on from lower layers.
632 return self._get("/v4/teams/"+team_id+"/members/"+user_id, **kwargs)
636 def remove_user_from_team(self, team_id, user_id, **kwargs):
638 Delete the team member object for a user, effectively removing them from a team.
641 team_id (string): team_id to remove the user from.
642 user_id (string): user_id to remove.
648 ApiException: Passed on from lower layers.
650 return self._delete("/v4/teams/"+team_id+"/members/"+user_id, **kwargs)
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
664 def update_team_members_scheme_roles(self, team_id, user_id, props, **kwargs):
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.
670 team_id (string): obvious
671 user_id (string): obvious
672 props (dict): see MM-API docs.
678 ApiException: Passed on from lower layers.
680 return self._put("/v4/teams/"+team_id+"/members/"+user_id+"/schemeRoles", data=props, **kwargs)
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
696 def get_team_channels(self, team_id, **kwargs): #This belongs here, not to channels!
698 Generator: Get a page of public channels on a team.
701 team_id (string): team to get channels from.
707 ApiException: Passed on from lower layers.
711 data_page = self._get("/v4/teams/"+team_id+"/channels", params={"page":str(page)}, **kwargs)
717 for data in data_page:
722 ################################################
727 #def get_all_channels() #NOT_IMPLEMENTED NOT USEFUL AT ALL!
731 def create_channel(self, team_id, name, display_name, purpose=None, header=None, chan_type="O", **kwargs):
733 Create a new channel.
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.
744 dict: created Channel.
747 ApiException: Passed on from lower layers.
749 return self._post("/v4/channels", data={
752 "display_name": display_name,
753 **({"purpose": purpose} if purpose else {}),
754 **({"header": header} if header else {}),
760 def create_dm_channel_with(self, other_user_id, **kwargs):
762 Create a new direct message channel between two users.
765 other_user_id (string): The other user_id to create the cannel with.
768 dict: created Channel.
771 ApiException: Passed on from lower layers.
773 return self._post("/v4/channels/direct", data=[self._my_user_id, other_user_id], **kwargs)
777 def create_group_channel_with(self, other_user_ids_list, **kwargs): #UNTESTED
779 Create a new direct message channel between two users.
782 other_user_ids_list (list): List of user_ids to create the cannel with.
785 dict: created Channel.
788 ApiException: Passed on from lower layers.
790 return self._post("/v4/channels/group", data=other_user_ids_list, **kwargs)
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
801 def get_channel(self, channel_id, **kwargs):
803 Get channel from the provided channel id string.
806 channel_id (string): channel_id to get.
812 ApiException: Passed on from lower layers.
814 return self._get("/v4/channels/"+channel_id, **kwargs)
818 def update_channel(self, channel_id, props, **kwargs):
820 Update a channel. The fields that can be updated are listed as parameters. Omitted fields will be treated as blanks.
823 channel_id (string): channel_id to get.
824 props (dict, optional): fields you want to update.
830 ApiException: Passed on from lower layers.
832 return self._put("/v4/channels/"+channel_id, data=props, **kwargs)
836 def patch_channel(self, channel_id, props, **kwargs):
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.
841 channel_id (string): channel_id to get.
842 props (dict, optional): fields you want to update.
848 ApiException: Passed on from lower layers.
850 return self._put("/v4/channels/"+channel_id+"/patch", data=props, **kwargs)
854 def get_channel_posts_pinned(self, channel_id, **kwargs):
856 Get a list of pinned posts for channel.
859 channel_id (string): channel_id to get pinned posts for.
865 ApiException: Passed on from lower layers.
867 return self._get("/v4/channels/"+channel_id+"/pinned", **kwargs)
871 def search_channel(self, team_id, term, **kwargs):
873 Search public channels on a team based on the search term provided in the request body.
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.
883 ApiException: Passed on from lower layers.
885 return self._post("/v4/teams/"+team_id+"/channels/search", data={"term": term}, **kwargs)
889 def get_channel_by_name(self, team_id, channel_name, include_deleted=None, **kwargs):
891 Gets channel from the provided team id and channel name strings.
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.
902 ApiException: Passed on from lower layers.
904 return self._get("/v4/teams/"+team_id+"/channels/name/"+channel_name, params={
905 **({"include_deleted": include_deleted} if include_deleted else {}),
910 def get_channel_members(self, channel_id, **kwargs):
912 Generator: Members for a channel.
915 channel_id (string): channel_id to get the members for.
918 generates: One Member at a time.
921 ApiException: Passed on from lower layers.
925 data_page = self._get("/v4/channels/"+channel_id+"/members", params={"page":str(page)}, **kwargs)
931 for data in data_page:
936 def add_user_to_channel(self, channel_id, user_id, **kwargs):
938 Add a user to a channel by creating a channel member object.
941 channel_id (string): channel_id to add the user to.
942 user_id (string): user_id to add.
948 ApiException: Passed on from lower layers.
950 return self._post("/v4/channels/"+channel_id+"/members", data={"user_id": user_id}, **kwargs)
954 def get_channel_member(self, channel_id, user_id, **kwargs):
956 Gets channel from the provided team id and channel name strings.
959 channel_id (string): channel_id to get the members for.
960 user_id (string): user_id to get the member-data for.
966 ApiException: Passed on from lower layers.
968 return self._get("/v4/channels/"+channel_id+"/members/"+user_id, **kwargs)
972 def remove_user_from_channel(self, channel_id, user_id, **kwargs):
974 Add a user to a channel by creating a channel member object.
977 channel_id (string): channel_id to remove the user from.
978 user_id (string): user_id to remove.
984 ApiException: Passed on from lower layers.
986 return self._delete("/v4/channels/"+channel_id+"/members/"+user_id, **kwargs)
990 def update_channel_members_scheme_roles(self, channel_id, user_id, props, **kwargs):
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.
995 channel_id (string): see MM-API doc.
996 user_id (string): see MM-API doc.
997 props (dict): see MM-API doc.
1003 ApiException: Passed on from lower layers.
1005 return self._put("/v4/channels/"+channel_id+"/members/"+user_id+"/schemeRoles", data=props, **kwargs)
1009 def get_channel_memberships_for_user(self, user_id, team_id, **kwargs):
1011 Get all channel memberships and associated membership roles (i.e. channel_user, channel_admin) for a user on a specific team.
1014 user_id (string): see MM-API doc.
1015 team_id (string): see MM-API doc.
1018 list: of Memberships.
1021 ApiException: Passed on from lower layers.
1023 return self._get("/v4/users/"+user_id+"/teams/"+team_id+"/channels/members", **kwargs)
1027 def get_channels_for_user(self, user_id, team_id, **kwargs):
1029 Get all the channels on a team for a user.
1032 user_id (string): see MM-API doc.
1033 team_id (string): see MM-API doc.
1039 ApiException: Passed on from lower layers.
1041 return self._get("/v4/users/"+user_id+"/teams/"+team_id+"/channels", **kwargs)
1045 ################################################
1049 def create_post(self, channel_id, message, props=None, filepaths=None, root_id=None, **kwargs):
1051 Create a new post in a channel. To create the post as a comment on another post, provide root_id.
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.
1064 ApiException: Passed on from lower layers.
1068 for filename in filepaths:
1069 file_ids.append(self.upload_file(channel_id, filename, **kwargs)["id"])
1071 return self._post("/v4/posts", data={
1072 "channel_id": channel_id,
1074 **({"props": props} if props else {"props": {"from_webhook":"true"}}),
1076 "file_ids": file_ids,
1081 def create_ephemeral_post(self, channel_id, message, user_id, **kwargs):
1083 Create a new ephemeral post in a channel.
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.
1094 ApiException: Passed on from lower layers.
1096 return self._post("/v4/posts/ephemeral", data={
1099 "channel_id": channel_id,
1106 def get_post(self, post_id, **kwargs):
1111 post_id (string): The post ID to get.
1117 ApiException: Passed on from lower layers.
1119 return self._get("/v4/posts/"+post_id, **kwargs)
1123 def delete_post(self, post_id, **kwargs):
1125 Soft deletes a post, by marking the post as deleted in the database. Soft deleted posts will not be returned in post queries.
1128 post_id (string): The post ID to delete.
1134 ApiException: Passed on from lower layers.
1136 return self._delete("/v4/posts/"+post_id, **kwargs)
1140 def patch_post(self, post_id, message=None, is_pinned=None, props=None, **kwargs):
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.
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.
1154 ApiException: Passed on from lower layers.
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 {}),
1164 def get_posts_for_channel(self, channel_id, **kwargs):
1166 Generator: Get a page of posts in a channel. Use the query parameters to modify the behaviour of this endpoint.
1169 channel_id (string): The channel ID to iterate over.
1175 ApiException: Passed on from lower layers.
1179 data_page = self._get("/v4/channels/"+channel_id+"/posts", params={"page":str(page)}, **kwargs)
1181 if data_page["order"] == []:
1185 for order in data_page["order"]:
1186 yield data_page["posts"][order]
1190 ################################################
1194 def upload_file(self, channel_id, filepath, **kwargs):
1196 Uploads a file that can later be attached to a post.
1199 channel_id (string): The channel ID to upload to.
1200 filepath (string): The local path of the source.
1203 dict: Uploaded File.
1206 ApiException: Passed on from lower layers.
1208 return self._post("/v4/files", multipart_formdata={'files':open(filepath, "rb"), "channel_id":(None, channel_id)}, **kwargs)["file_infos"][0]
1212 def get_file(self, file_id, **kwargs):
1214 Uploads a file that can later be attached to a post.
1217 file_id (string): The file ID to get.
1220 binary: file-content.
1223 ApiException: Passed on from lower layers.
1225 return self._get("/v4/files/"+file_id, raw=True, **kwargs)
1229 ################################################
1230 #+ **PREFERENCES** #NOT_IMPLEMENTED
1232 ################################################
1233 #+ **STATUS** #NOT_IMPLEMENTED
1235 ################################################
1236 #+ **EMOJI** #NOT_IMPLEMENTED
1238 ################################################
1242 def create_reaction(self, user_id, post_id, emoji_name, **kwargs):
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.
1252 dict: created Reaction.
1255 ApiException: Passed on from lower layers.
1257 return self._post("/v4/reactions", data={
1260 "emoji_name": emoji_name,
1265 ################################################
1269 def create_outgoing_hook(self, team_id, display_name, trigger_words, callback_urls, channel_id=None, description=None, trigger_when=0, **kwargs):
1271 Create an outgoing webhook for a team.
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.
1283 dict: created Webhook.
1286 ApiException: Passed on from lower layers.
1288 return self._post("/v4/hooks/outgoing", data={
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",
1301 def list_outgoing_hooks(self, team_id, channel_id=None, **kwargs):
1303 Generator: Get a page of a list of outgoing webhooks. Optionally filter for a specific channel using query parameters.
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.
1310 generates: One Webhook at a time.
1313 ApiException: Passed on from lower layers.
1315 return self._get("/v4/hooks/outgoing", params={
1317 **({"channel_id": channel_id} if channel_id else {}),
1322 def delete_outgoing_hook(self, hook_id, **kwargs):
1324 Delete an outgoing webhook given the hook id.
1327 hook_id (string): The ID of the hook to delete.
1333 ApiException: Passed on from lower layers.
1335 return self._delete("/v4/hooks/outgoing/"+hook_id, **kwargs)
1339 ################################################
1343 def create_slash_command(self, team_id, trigger, url, **kwargs):
1345 Create a command for a team.
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.
1353 dict: created Command.
1356 ApiException: Passed on from lower layers.
1358 return self._post("/v4/commands", data={
1367 def list_custom_slash_commands_for_team(self, team_id, **kwargs):
1369 List commands for a team.
1372 team_id (string): The ID of the team to get hooks for.
1378 ApiException: Passed on from lower layers.
1380 return self._get("/v4/commands", params={
1387 def update_slash_command(self, data, **kwargs):
1389 Update a single command based on command id string and Command struct.
1392 data (dict): Command to update.
1395 dict: updated Command.
1398 ApiException: Passed on from lower layers.
1400 return self._put("/v4/commands/"+data["id"], data=data, **kwargs)
1404 def delete_slash_command(self, command_id, **kwargs):
1406 Delete a command based on command id string.
1409 command_id (string): ID of the command to delete.
1415 ApiException: Passed on from lower layers.
1417 return self._delete("/v4/commands/"+command_id, **kwargs)
1421 ################################################
1422 #+ **OPENGRAPH** #NOT_IMPLEMENTED
1424 ################################################
1425 #+ **SYSTEM** #NOT_IMPLEMENTED
1427 ################################################
1428 #+ **BRAND** #NOT_IMPLEMENTED
1430 ################################################
1431 #+ **OAUTH** #NOT_IMPLEMENTED
1433 ################################################
1434 #+ **SAML** #NOT_IMPLEMENTED
1436 ################################################
1437 #+ **LDAP** #NOT_IMPLEMENTED
1439 ################################################
1440 #+ **GROUPS** #NOT_IMPLEMENTED
1442 ################################################
1443 #+ **COMPLIANCE** #NOT_IMPLEMENTED
1445 ################################################
1446 #+ **CLUSTER** #NOT_IMPLEMENTED
1448 ################################################
1449 #+ **ELASTICSEARCH** #NOT_IMPLEMENTED
1451 ################################################
1452 #+ **BLEVE** #NOT_IMPLEMENTED
1454 ################################################
1455 #+ **DATARETENTION** #NOT_IMPLEMENTED
1457 ################################################
1458 #+ **JOBS** #NOT_IMPLEMENTED
1460 ################################################
1461 #+ **PLUGINS** #NOT_IMPLEMENTED
1463 ################################################
1464 #+ **ROLES** #NOT_IMPLEMENTED
1466 ################################################
1467 #+ **SCHEMES** #NOT_IMPLEMENTED
1469 ################################################
1470 #+ **INTEGRATION_ACTIONS**
1473 def open_dialog(self, trigger_id, response_url, dialog, **kwargs):
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.
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.
1486 ApiException: Passed on from lower layers.
1488 return self._post("/v4/actions/dialogs/open", data={
1489 "trigger_id": trigger_id,
1490 "url": response_url,
1496 ################################################
1497 #+ **TERMS_OF_SERVICE** #NOT_IMPLEMENTED