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
690 def invite_guests_to_team_by_email(self, team_id, guest_email, channels, message, **kwargs):
692 Invite guests to existing team channels usign the user's email.
693 The number of emails that can be sent is rate limited to 20 per hour with a burst of 20 emails. If the rate limit exceeds, the error message contains details on when to retry and when the timer will be reset.
696 team_id (string): obvious
697 guest_email (strings): whom to invite.
698 channels (list of channel_ids): into which channels.
699 message (string, optional): optional message
705 ApiException: Passed on from lower layers.
707 return self._post("/v4/teams/"+team_id+"/invite-guests/email", data={
708 "emails": [guest_email],
709 "channels": channels,
710 **({"message": message} if message else {}),
714 #def invalidate_invites_to_team_by_email() #NOT_IMPLEMENTED
715 #def import_team() #NOT_IMPLEMENTED
716 #def get_team_invite_info() #NOT_IMPLEMENTED
717 #def set_team_scheme() #NOT_IMPLEMENTED
718 #def get_team_members_minus_group_members() #NOT_IMPLEMENTED
722 def get_team_channels(self, team_id, **kwargs): #This belongs here, not to channels!
724 Generator: Get a page of public channels on a team.
727 team_id (string): team to get channels from.
733 ApiException: Passed on from lower layers.
737 data_page = self._get("/v4/teams/"+team_id+"/channels", params={"page":str(page)}, **kwargs)
743 for data in data_page:
748 ################################################
753 #def get_all_channels() #NOT_IMPLEMENTED NOT USEFUL AT ALL!
757 def create_channel(self, team_id, name, display_name, purpose=None, header=None, chan_type="O", **kwargs):
759 Create a new channel.
762 team_id (string): The team ID of the team to create the channel on.
763 name (string): The unique handle for the channel, will be present in the channel URL.
764 display_name (string): see MM-API docs.
765 purpose (string, optional): see MM-API docs.
766 header (string, optional): see MM-API docs.
767 chan_type (string, default: public): see MM-API docs.
770 dict: created Channel.
773 ApiException: Passed on from lower layers.
775 return self._post("/v4/channels", data={
778 "display_name": display_name,
779 **({"purpose": purpose} if purpose else {}),
780 **({"header": header} if header else {}),
786 def create_dm_channel_with(self, other_user_id, **kwargs):
788 Create a new direct message channel between two users.
791 other_user_id (string): The other user_id to create the cannel with.
794 dict: created Channel.
797 ApiException: Passed on from lower layers.
799 return self._post("/v4/channels/direct", data=[self._my_user_id, other_user_id], **kwargs)
803 def create_group_channel_with(self, other_user_ids_list, **kwargs): #UNTESTED
805 Create a new direct message channel between two users.
808 other_user_ids_list (list): List of user_ids to create the cannel with.
811 dict: created Channel.
814 ApiException: Passed on from lower layers.
816 return self._post("/v4/channels/group", data=other_user_ids_list, **kwargs)
820 #def search_all_private_and_public_channels() #NOT_IMPLEMENTED
821 #def search_all_users_group_channels() #NOT_IMPLEMENTED
822 #def get_team_channels_by_id() #NOT_IMPLEMENTED
823 #def get_timezones_of_users_in_channel() #NOT_IMPLEMENTED
827 def get_channel(self, channel_id, **kwargs):
829 Get channel from the provided channel id string.
832 channel_id (string): channel_id to get.
838 ApiException: Passed on from lower layers.
840 return self._get("/v4/channels/"+channel_id, **kwargs)
844 def update_channel(self, channel_id, props, **kwargs):
846 Update a channel. The fields that can be updated are listed as parameters. Omitted fields will be treated as blanks.
849 channel_id (string): channel_id to get.
850 props (dict, optional): fields you want to update.
856 ApiException: Passed on from lower layers.
858 return self._put("/v4/channels/"+channel_id, data=props, **kwargs)
862 def patch_channel(self, channel_id, props, **kwargs):
864 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.
867 channel_id (string): channel_id to get.
868 props (dict, optional): fields you want to update.
874 ApiException: Passed on from lower layers.
876 return self._put("/v4/channels/"+channel_id+"/patch", data=props, **kwargs)
880 def get_channel_posts_pinned(self, channel_id, **kwargs):
882 Get a list of pinned posts for channel.
885 channel_id (string): channel_id to get pinned posts for.
891 ApiException: Passed on from lower layers.
893 return self._get("/v4/channels/"+channel_id+"/pinned", **kwargs)
897 def search_channel(self, team_id, term, **kwargs):
899 Search public channels on a team based on the search term provided in the request body.
902 team_id (string): team_id to search in.
903 term (string): The search term to match against the name or display name of channels.
909 ApiException: Passed on from lower layers.
911 return self._post("/v4/teams/"+team_id+"/channels/search", data={"term": term}, **kwargs)
915 def get_channel_by_name(self, team_id, channel_name, include_deleted=None, **kwargs):
917 Gets channel from the provided team id and channel name strings.
920 team_id (string): team_id to search in.
921 term (string): The search term to match against the name or display name of channels.
922 include_deleted (bool, optional): see MM-API doc.
928 ApiException: Passed on from lower layers.
930 return self._get("/v4/teams/"+team_id+"/channels/name/"+channel_name, params={
931 **({"include_deleted": include_deleted} if include_deleted else {}),
936 def get_channel_members(self, channel_id, **kwargs):
938 Generator: Members for a channel.
941 channel_id (string): channel_id to get the members for.
944 generates: One Member at a time.
947 ApiException: Passed on from lower layers.
951 data_page = self._get("/v4/channels/"+channel_id+"/members", params={"page":str(page)}, **kwargs)
957 for data in data_page:
962 def add_user_to_channel(self, channel_id, user_id, **kwargs):
964 Add a user to a channel by creating a channel member object.
967 channel_id (string): channel_id to add the user to.
968 user_id (string): user_id to add.
974 ApiException: Passed on from lower layers.
976 return self._post("/v4/channels/"+channel_id+"/members", data={"user_id": user_id}, **kwargs)
980 def get_channel_member(self, channel_id, user_id, **kwargs):
982 Gets channel from the provided team id and channel name strings.
985 channel_id (string): channel_id to get the members for.
986 user_id (string): user_id to get the member-data for.
992 ApiException: Passed on from lower layers.
994 return self._get("/v4/channels/"+channel_id+"/members/"+user_id, **kwargs)
998 def remove_user_from_channel(self, channel_id, user_id, **kwargs):
1000 Add a user to a channel by creating a channel member object.
1003 channel_id (string): channel_id to remove the user from.
1004 user_id (string): user_id to remove.
1010 ApiException: Passed on from lower layers.
1012 return self._delete("/v4/channels/"+channel_id+"/members/"+user_id, **kwargs)
1016 def update_channel_members_scheme_roles(self, channel_id, user_id, props, **kwargs):
1018 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.
1021 channel_id (string): see MM-API doc.
1022 user_id (string): see MM-API doc.
1023 props (dict): see MM-API doc.
1029 ApiException: Passed on from lower layers.
1031 return self._put("/v4/channels/"+channel_id+"/members/"+user_id+"/schemeRoles", data=props, **kwargs)
1035 def get_channel_memberships_for_user(self, user_id, team_id, **kwargs):
1037 Get all channel memberships and associated membership roles (i.e. channel_user, channel_admin) for a user on a specific team.
1040 user_id (string): see MM-API doc.
1041 team_id (string): see MM-API doc.
1044 list: of Memberships.
1047 ApiException: Passed on from lower layers.
1049 return self._get("/v4/users/"+user_id+"/teams/"+team_id+"/channels/members", **kwargs)
1053 def get_channels_for_user(self, user_id, team_id, **kwargs):
1055 Get all the channels on a team for a user.
1058 user_id (string): see MM-API doc.
1059 team_id (string): see MM-API doc.
1065 ApiException: Passed on from lower layers.
1067 return self._get("/v4/users/"+user_id+"/teams/"+team_id+"/channels", **kwargs)
1071 ################################################
1075 def create_post(self, channel_id, message, props=None, filepaths=None, root_id=None, **kwargs):
1077 Create a new post in a channel. To create the post as a comment on another post, provide root_id.
1080 channel_id (string): The channel ID to create the post in.
1081 message (string): The message text.
1082 props (string, optional): see MM-API docs.
1083 filepaths (list, optional): Paths to upload files from and attach to post.
1084 root_id (string, optional): see MM-API docs.
1090 ApiException: Passed on from lower layers.
1094 for filename in filepaths:
1095 file_ids.append(self.upload_file(channel_id, filename, **kwargs)["id"])
1097 return self._post("/v4/posts", data={
1098 "channel_id": channel_id,
1100 **({"props": props} if props else {"props": {"from_webhook":"true"}}),
1102 "file_ids": file_ids,
1107 def create_ephemeral_post(self, channel_id, message, user_id, **kwargs):
1109 Create a new ephemeral post in a channel.
1112 channel_id (string): The channel ID to create the post in.
1113 message (string): The message text.
1114 user_id (string): The user ID to display the post to.
1120 ApiException: Passed on from lower layers.
1122 return self._post("/v4/posts/ephemeral", data={
1125 "channel_id": channel_id,
1132 def get_post(self, post_id, **kwargs):
1137 post_id (string): The post ID to get.
1143 ApiException: Passed on from lower layers.
1145 post = self._get("/v4/posts/"+post_id, **kwargs)
1146 if "has_reactions" not in post:
1147 post["has_reactions"] = False
1152 def delete_post(self, post_id, **kwargs):
1154 Soft deletes a post, by marking the post as deleted in the database. Soft deleted posts will not be returned in post queries.
1157 post_id (string): The post ID to delete.
1163 ApiException: Passed on from lower layers.
1165 return self._delete("/v4/posts/"+post_id, **kwargs)
1168 def update_post(self, post_id, message, is_pinned, has_reactions, props, **kwargs):
1170 Update a post. Only the fields listed below are updatable, omitted fields will be treated as blank.
1173 post_id (string): The post ID to patch.
1174 message (string): see MM-API doc.
1175 is_pinned (bool): see MM-API doc.
1176 has_reactions (list of has_reactions): see MM-API doc.
1177 props (dict): see MM-API doc.
1183 ApiException: Passed on from lower layers.
1185 return self._put("/v4/posts/"+post_id, data={
1188 "is_pinned": is_pinned,
1189 "has_reactions": has_reactions,
1194 def patch_post(self, post_id, message=None, is_pinned=None, file_ids=None, has_reactions=None, props=None, **kwargs):
1196 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.
1199 post_id (string): The post ID to patch.
1200 message (string, optional): see MM-API doc.
1201 is_pinned (bool, optional): see MM-API doc.
1202 file_ids (list of file_ids, optional): see MM-API doc.
1203 has_reactions (list of has_reactions, optional): see MM-API doc.
1204 props (dict, optional): see MM-API doc.
1210 ApiException: Passed on from lower layers.
1212 return self._put("/v4/posts/"+post_id+"/patch", data={
1213 **({"message": message} if message else {}),
1214 **({"is_pinned": is_pinned} if is_pinned else {}),
1215 **({"file_ids": file_ids} if file_ids else {}),
1216 **({"has_reactions": has_reactions} if has_reactions else {}),
1217 **({"props": props} if props else {}),
1222 def get_posts_for_channel(self, channel_id, **kwargs):
1224 Generator: Get a page of posts in a channel. Use the query parameters to modify the behaviour of this endpoint.
1227 channel_id (string): The channel ID to iterate over.
1233 ApiException: Passed on from lower layers.
1237 data_page = self._get("/v4/channels/"+channel_id+"/posts", params={"page":str(page)}, **kwargs)
1239 if data_page["order"] == []:
1243 for order in data_page["order"]:
1244 yield data_page["posts"][order]
1248 ################################################
1252 def upload_file(self, channel_id, filepath, **kwargs):
1254 Uploads a file that can later be attached to a post.
1257 channel_id (string): The channel ID to upload to.
1258 filepath (string): The local path of the source.
1261 dict: Uploaded File.
1264 ApiException: Passed on from lower layers.
1266 return self._post("/v4/files", multipart_formdata={'files':open(filepath, "rb"), "channel_id":(None, channel_id)}, **kwargs)["file_infos"][0]
1270 def get_file(self, file_id, **kwargs):
1272 Uploads a file that can later be attached to a post.
1275 file_id (string): The file ID to get.
1278 binary: file-content.
1281 ApiException: Passed on from lower layers.
1283 return self._get("/v4/files/"+file_id, raw=True, **kwargs)
1287 ################################################
1288 #+ **PREFERENCES** #NOT_IMPLEMENTED
1290 ################################################
1291 #+ **STATUS** #NOT_IMPLEMENTED
1293 ################################################
1294 #+ **EMOJI** #NOT_IMPLEMENTED
1296 ################################################
1300 def create_reaction(self, user_id, post_id, emoji_name, **kwargs):
1305 user_id (string): The ID of the user that made this reaction.
1306 post_id (string): The ID of the post to which this reaction was made.
1307 emoji_name (string): The name of the emoji that was used for this reaction.
1310 dict: created Reaction.
1313 ApiException: Passed on from lower layers.
1315 return self._post("/v4/reactions", data={
1318 "emoji_name": emoji_name,
1323 ################################################
1327 def create_outgoing_hook(self, team_id, display_name, trigger_words, callback_urls, channel_id=None, description=None, trigger_when=0, **kwargs):
1329 Create an outgoing webhook for a team.
1332 team_id (string): The ID of the team that the webhook watchs.
1333 display_name (string): The display name for this outgoing webhook.
1334 trigger_words (list): List of words for the webhook to trigger on.
1335 callback_urls (list): The URLs to POST the payloads to when the webhook is triggered.
1336 channel_id (string, optional): The ID of a public channel that the webhook watchs.
1337 description (string, optional): The description for this outgoing webhook.
1338 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.
1341 dict: created Webhook.
1344 ApiException: Passed on from lower layers.
1346 return self._post("/v4/hooks/outgoing", data={
1348 "display_name": display_name,
1349 "trigger_words": trigger_words,
1350 "callback_urls": callback_urls,
1351 **({"channel_id": channel_id} if channel_id else {}),
1352 **({"description": description} if description else {}),
1353 "trigger_when": trigger_when,
1354 "content_type": "application/json",
1359 def list_outgoing_hooks(self, team_id, channel_id=None, **kwargs):
1361 Generator: Get a page of a list of outgoing webhooks. Optionally filter for a specific channel using query parameters.
1364 team_id (string): The ID of the team to get hooks for.
1365 channel_id (string, optional): The ID of the channel to get hooks for.
1368 generates: One Webhook at a time.
1371 ApiException: Passed on from lower layers.
1373 return self._get("/v4/hooks/outgoing", params={
1375 **({"channel_id": channel_id} if channel_id else {}),
1380 def delete_outgoing_hook(self, hook_id, **kwargs):
1382 Delete an outgoing webhook given the hook id.
1385 hook_id (string): The ID of the hook to delete.
1391 ApiException: Passed on from lower layers.
1393 return self._delete("/v4/hooks/outgoing/"+hook_id, **kwargs)
1397 ################################################
1401 def create_slash_command(self, team_id, trigger, url, **kwargs):
1403 Create a command for a team.
1406 team_id (string): Team ID to where the command should be created.
1407 trigger (string): Activation word to trigger the command.
1408 url (string): The URL that the command will make the request.
1411 dict: created Command.
1414 ApiException: Passed on from lower layers.
1416 return self._post("/v4/commands", data={
1425 def list_custom_slash_commands_for_team(self, team_id, **kwargs):
1427 List commands for a team.
1430 team_id (string): The ID of the team to get hooks for.
1436 ApiException: Passed on from lower layers.
1438 return self._get("/v4/commands", params={
1445 def update_slash_command(self, data, **kwargs):
1447 Update a single command based on command id string and Command struct.
1450 data (dict): Command to update.
1453 dict: updated Command.
1456 ApiException: Passed on from lower layers.
1458 return self._put("/v4/commands/"+data["id"], data=data, **kwargs)
1462 def delete_slash_command(self, command_id, **kwargs):
1464 Delete a command based on command id string.
1467 command_id (string): ID of the command to delete.
1473 ApiException: Passed on from lower layers.
1475 return self._delete("/v4/commands/"+command_id, **kwargs)
1479 ################################################
1480 #+ **OPENGRAPH** #NOT_IMPLEMENTED
1482 ################################################
1483 #+ **SYSTEM** #NOT_IMPLEMENTED
1485 ################################################
1486 #+ **BRAND** #NOT_IMPLEMENTED
1488 ################################################
1489 #+ **OAUTH** #NOT_IMPLEMENTED
1491 ################################################
1492 #+ **SAML** #NOT_IMPLEMENTED
1494 ################################################
1495 #+ **LDAP** #NOT_IMPLEMENTED
1497 ################################################
1498 #+ **GROUPS** #NOT_IMPLEMENTED
1500 ################################################
1501 #+ **COMPLIANCE** #NOT_IMPLEMENTED
1503 ################################################
1504 #+ **CLUSTER** #NOT_IMPLEMENTED
1506 ################################################
1507 #+ **ELASTICSEARCH** #NOT_IMPLEMENTED
1509 ################################################
1510 #+ **BLEVE** #NOT_IMPLEMENTED
1512 ################################################
1513 #+ **DATARETENTION** #NOT_IMPLEMENTED
1515 ################################################
1516 #+ **JOBS** #NOT_IMPLEMENTED
1518 ################################################
1519 #+ **PLUGINS** #NOT_IMPLEMENTED
1521 ################################################
1522 #+ **ROLES** #NOT_IMPLEMENTED
1524 ################################################
1525 #+ **SCHEMES** #NOT_IMPLEMENTED
1527 ################################################
1528 #+ **INTEGRATION_ACTIONS**
1531 def open_dialog(self, trigger_id, response_url, dialog, **kwargs):
1533 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.
1536 trigger_id (string): Trigger ID provided by other action.
1537 response_url (string): The URL to send the submitted dialog payload to.
1538 dialog (dict): Dialog definition.
1544 ApiException: Passed on from lower layers.
1546 return self._post("/v4/actions/dialogs/open", data={
1547 "trigger_id": trigger_id,
1548 "url": response_url,
1554 ################################################
1555 #+ **TERMS_OF_SERVICE** #NOT_IMPLEMENTED