]> git.somenet.org - pub/jan/mattermost-bot.git/blob - core/AbstractCommand.py
core/AbstractCommand.py
[pub/jan/mattermost-bot.git] / core / AbstractCommand.py
1 # Mattermost Bot.
2 #  Copyright (c) 2016-2022 by Someone <someone@somenet.org> (aka. Jan Vales <jan@jvales.net>)
3 #  published under MIT-License
4
5
6 import mattermost
7
8 class RequireException(Exception):
9     """Required precondition not met-exception. Mostly used for permission checks. Message will be forwarded to command-caller/user"""
10
11
12 class AbstractCommand():
13     ICON_PUBLIC = "📢 "
14     ICON_DM = "👥 "
15     ICON_PRIVATE = "🥷 "
16
17     URL = None
18     TEAM_ID = None
19     TRIGGER = None
20     CONFIG = {"display_name": "somebot-command", "auto_complete": True}
21     USEINFO = None
22
23     bot = None
24     mm_secret_token = None
25
26
27     def __init__(self, team_id):
28         self.TEAM_ID = team_id
29
30     def __str__(self):
31         return str(self.__class__)+" for team_id: "+str(self.TEAM_ID)
32
33     def __repr__(self):
34         return self.__str__()
35
36
37     # can/should be overridden by the user
38     def on_register(self):
39         """Consider to override. Handles the post-command-registration logic at bot startup."""
40         self._create_slash_command()
41
42     # can/should be overridden by the user
43     def on_shutdown(self):
44         """Consider to override. Handles the shutdown-procedure."""
45         return
46
47     # can/should be overridden by the user
48     def on_SIGUSR1(self, sigusr1_cnt):
49         """Consider to override. Handles the SIGUSR1-signal."""
50         return
51
52     # should be overridden by the user
53     def on_POST(self, request, data):
54         """Override. Handles the post-command logic."""
55         return
56
57     # should be overridden by the user
58     # manual command authentication needed!
59     def on_POST_interactive(self, request, data):
60         """Consider to override. Handles the interactive-message logic."""
61         return
62
63     # should be overridden by the user
64     # manual command authentication needed!
65     def on_POST_dialog(self, request, data):
66         """Consider to override. Handles the dialog logic."""
67         return
68
69
70     def _on_register(self, bot):
71         self.bot = bot
72         self.URL = ("http://"+self.bot.local_websrv_hostname+":"+str(self.bot.local_websrv_port)+"/").strip("/")+"/"+self.TEAM_ID+"/"+self.TRIGGER
73         if self.USEINFO:
74             self.bot.USETOPICS.setdefault("/"+self.TRIGGER, self.USEINFO)
75         self.on_register()
76
77
78     def _on_shutdown(self):
79         self.on_shutdown()
80
81
82     def _on_SIGUSR1(self, sigusr1_cnt):
83         self.on_SIGUSR1(sigusr1_cnt)
84
85
86     def _on_POST(self, request, data):
87         try:
88             self._require_not_guest(data)
89             self.on_POST(request, data)
90         except RequireException as ex:
91             request.respond_cmd_err(str(ex))
92
93
94     def _on_POST_interactive(self, request, data):
95         try:
96             self._require_not_guest(data)
97             self.on_POST_interactive(request, data)
98         except RequireException as ex:
99             request.respond_cmd_err(str(ex))
100
101
102     def _on_POST_dialog(self, request, data):
103         try:
104             self._require_not_guest(data)
105             self.on_POST_dialog(request, data)
106         except RequireException as ex:
107             request.respond_cmd_err(str(ex))
108
109
110     def _create_slash_command(self):
111         # (possibly) delete old version of command
112         for command in self.bot.api.list_custom_slash_commands_for_team(self.TEAM_ID):
113             if command["url"] == self.URL or command["trigger"].lower() == self.TRIGGER.lower():
114                 self.bot.api.delete_slash_command(command["id"])
115
116         # create slash command
117         res = self.bot.api.create_slash_command(self.TEAM_ID, self.TRIGGER.lower(), self.URL+"/command")
118         res.update(self.CONFIG)
119         self.bot.api.update_slash_command(res)
120         self.mm_secret_token = res["token"]
121
122
123     def _require_bot_admin(self, data):
124         """
125         Require exactly bot admin priviledges.
126         Throws RequireException if not priviledged.
127         """
128         if not data["user_id"] in self.bot.admin_ids:
129             raise RequireException("### Leave me alone. You are not by real dad.")
130
131
132     def _require_system_admin(self, data, exc=True):
133         """
134         Require at least team admin priviledges.
135         Throws RequireException if not priviledged.
136         """
137         user = self.bot.api.get_user(data["user_id"])
138         if "system_admin" not in user["roles"]:
139             if exc:
140                 raise RequireException("### You are not SYSTEM_ADMIN. :(")
141             return False
142         return True
143
144
145     def _require_team_admin(self, data, exc=True):
146         """
147         Require at least team admin priviledges.
148         Throws RequireException if not priviledged.
149         """
150         team_member = self.bot.api.get_team_member(data["team_id"], data["user_id"])
151         if "team_admin" not in team_member["roles"] and not self._require_system_admin(data, exc=False):
152             if exc:
153                 raise RequireException("### You are not TEAM_ADMIN. :(")
154             return False
155         return True
156
157
158     def _require_channel_admin(self, data, exc=True):
159         """
160         Require at least channel admin priviledges.
161         Throws RequireException if not priviledged.
162         """
163         channel_member = self.bot.api.get_channel_member(data["channel_id"], data["user_id"])
164         if "channel_admin" not in channel_member["roles"] and not self._require_team_admin(data, exc=False):
165             if exc:
166                 raise RequireException("### You are not CHANNEL_ADMIN. :(")
167             return False
168         return True
169
170
171     def _require_not_guest(self, data, exc=True):
172         """
173         Require to not be a guest.
174         Throws RequireException if guest.
175         """
176         channel_member = self.bot.api.get_channel_member(data["channel_id"], data["user_id"])
177         if "channel_guest" in channel_member["roles"]:
178             if exc:
179                 raise RequireException("### The bot cannot be used by guests. :(")
180             return False
181         return True
182
183
184     def _require_in_channel(self, in_channel, data, exc=True):
185         """
186         Require to be in a channel.
187         Throws RequireException if not in the given channel.
188         """
189
190         try:
191             channel_member = self.bot.api.get_channel_member(in_channel, data["user_id"], exc=True)
192         except mattermost.ApiException as ex:
193             if exc:
194                 raise RequireException("### You are not in the required channel :(")
195             return False
196         return True