]> git.somenet.org - pub/jan/mattermost-bot.git/blob - modules/CommandFSInfAccess.py
new file: modules/CommandFSOpen.py
[pub/jan/mattermost-bot.git] / modules / CommandFSInfAccess.py
1 # Mattermost Bot module.
2 #  Copyright (c) 2016-2022 by Someone <someone@somenet.org> (aka. Jan Vales <jan@jvales.net>)
3 #  published under MIT-License
4
5 import json
6 import pprint
7 from inspect import cleandoc
8
9
10 # pylint: disable=wrong-import-position
11 from AbstractCommand import AbstractCommand
12 class CommandFSInfAccess(AbstractCommand):
13     TRIGGER = "fsinf-access"
14     CONFIG = {"display_name": "somebot-command", "auto_complete": True,
15               "auto_complete_hint": "'out-out'|'opt-in' [<channel_id>]|'sync'",
16              }
17     CONFIG["auto_complete_desc"] = CONFIG["description"] = AbstractCommand.ICON_PUBLIC+"Manage fsinf-channel auto-joins. [FSINF]"
18
19
20     def __init__(self, team_id, managed_teams, fsinf_intern_channelid, fsinf_teams_and_channels, datadir):
21         super().__init__(team_id)
22
23         self.managed_teams = managed_teams
24         self.fsinf_intern_channelid = fsinf_intern_channelid
25         self.fsinf_teams_and_channels = {x:{y:[] for y in fsinf_teams_and_channels[x]} for x in fsinf_teams_and_channels}
26         self.datadir = datadir
27
28
29     def on_POST(self, request, data):
30         self._require_in_channel(self.fsinf_intern_channelid, data) # will throw an exception if not. (Dont try-except: Its handled up the stack.)
31
32         # load and merge data on first use.
33         self.fsinf_teams_and_channels = self._load_and_merge_optout_data(self.datadir+"./optout.json", self.fsinf_teams_and_channels)
34
35         msg_text = data['text'].strip().split(" ", 1)
36         if msg_text[0].strip() == 'opt-out':
37             if data['team_id'] in self.fsinf_teams_and_channels and data['channel_id'] in self.fsinf_teams_and_channels[data['team_id']]:
38                 self.fsinf_teams_and_channels[data['team_id']][data['channel_id']] = sorted(list(set(self.fsinf_teams_and_channels[data['team_id']][data['channel_id']] + [data['user_id']])))
39                 self._store_optout_data(self.datadir+"./optout.json", self.fsinf_teams_and_channels)
40                 request.respond_cmd_temp("## :white_check_mark: Success! :)\n#### Now leave this channel manually.")
41                 return
42             else:
43                 request.respond_cmd_err("``/"+self.TRIGGER+"`` Trying to opt-out from an unmanaged channel.")
44                 return
45
46         elif msg_text[0].strip() == 'opt-in' and len(msg_text) == 1:
47             optout_list = []
48             for team, to_unpack in self.fsinf_teams_and_channels.items():
49                 for channel, excluded in to_unpack.items():
50                     if data['user_id'] in self.fsinf_teams_and_channels[team][channel]:
51                         optout_list += [channel]
52             if optout_list:
53                 request.respond_cmd_temp("#### You have opted-out from these channels:\n + "+("\n + ".join([cid+" ("+self.bot.api.get_channel(cid)["name"]+")" for cid in optout_list])))
54                 return
55             else:
56                 request.respond_cmd_temp("#### You have not opted-out from any channels.")
57                 return
58
59         elif msg_text[0].strip() == 'opt-in' and len(msg_text) > 1:
60             managed = False
61             chan_id = msg_text[1].strip()
62             for team, to_unpack in self.fsinf_teams_and_channels.items():
63                 if chan_id in self.fsinf_teams_and_channels[team]:
64                     managed = True
65                     if data['user_id'] in self.fsinf_teams_and_channels[team][chan_id]:
66                         newset = set(self.fsinf_teams_and_channels[team][chan_id])
67                         newset.remove(data['user_id'])
68                         self.fsinf_teams_and_channels[team][chan_id] = sorted(list(newset))
69                         self._store_optout_data(self.datadir+"./optout.json", self.fsinf_teams_and_channels)
70                     else:
71                         request.respond_cmd_err("``/"+self.TRIGGER+"`` You have not opted-out from this channel.")
72                         return
73
74             if managed:
75                 request.respond_cmd_temp("## :white_check_mark: Success! :)\n#### Run ``/"+self.TRIGGER+" sync`` to apply the changes.")
76                 return
77             else:
78                 request.respond_cmd_err("``/"+self.TRIGGER+"`` Trying to opt-in to an unmanaged or unknown channel.")
79                 return
80
81         elif msg_text[0].strip() == 'sync':
82             fsinf_intern_userids = set([u["user_id"] for u in self.bot.api.get_channel_members(self.fsinf_intern_channelid)])
83             all_users = {u["id"]:u["username"] for u in self.bot.api.get_users()}
84
85             request.respond_cmd_temp("## :white_check_mark: Success! :)\n#### Sync started. See ~debug for details.")
86
87             dbg_post = self.bot.debug_chan(cleandoc("""
88                 ## ``/fsinf-access sync``
89                 This will add people to various channels unless they are opted out already:
90                 + run **``/fsinf-access opt-out``** in the channel to opt out from
91                 + run **``/fsinf-access opt-in``** to list channels, you are opted out of.
92                 + run **``/fsinf-access opt-in <channel_id>``** to opt-in again, then run **``/fsinf-access sync``** to apply this change.
93
94                 These accounts were detected to be fsinf-intern by being in ~off-topic:
95                 ``"""+str(sorted([all_users[u] for u in fsinf_intern_userids]))+"""``
96
97                 Adding active fsinf accounts to these teams (and granting TEAM_ADMIN permissions) and channels unless opted-out (run ``/fsinf-access opt-out` in the channel to opt out from):
98                 ```
99                 """)+"\n"+pprint.pformat({self.bot.api.get_team(tid)["name"]:{self.bot.api.get_channel(cid)["name"]:uids for cid,uids in self.fsinf_teams_and_channels[tid].items()} for tid in self.fsinf_teams_and_channels})+"\n"+cleandoc("""
100                 ```
101                 Applying now ...
102                 """))
103
104             # join into teams + make team admins
105             for user_id in fsinf_intern_userids:
106                 for _, t in self.managed_teams.items():
107                     self.bot.api.add_user_to_team(t[0], user_id, exc=False)
108                     self.bot.api.update_team_members_scheme_roles(t[0], user_id, {"scheme_user": True, "scheme_admin": True}, exc=False)
109
110                 # join into special channels
111                 for team_id, chan_ids in self.fsinf_teams_and_channels.items():
112                     for chan_id, excluded_user_ids in chan_ids.items():
113                         if not user_id in excluded_user_ids:
114                             self.bot.api.add_user_to_channel(chan_id, user_id, exc=False)
115
116             self.bot.debug_chan("## :white_check_mark: Success! :)", root_id=dbg_post['id'])
117
118
119     def _load_and_merge_optout_data(self, path, to_merge_with):
120         try:
121             merged = to_merge_with.copy()
122             with open(path, "r") as f:
123                 stored_data = json.load(f)
124                 for team, to_unpack in to_merge_with.items():
125                     for channel, excluded in to_unpack.items():
126                         if team in stored_data and channel in stored_data[team]:
127                             merged[team][channel] = sorted(list(set(merged[team][channel]+stored_data[team][channel])))
128             return merged
129         except:
130             return to_merge_with
131
132
133     def _store_optout_data(self, path, data):
134         with open(path, "w") as f:
135            json.dump(data, f, sort_keys=True, indent=2)
136         self.bot.debug_chan("``/fsinf-access`` optout.json updated.")