-
Notifications
You must be signed in to change notification settings - Fork 0
/
space_closer.py
188 lines (158 loc) · 7.15 KB
/
space_closer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# Copyright (C) 2020 Frederick W. Nielsen
#
# This file is part of Cisco Collaboration Cloud Tools.
#
# Cisco Collaboration Cloud Tools is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Cisco Collaboration Cloud Tools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Cisco Collaboration Cloud Tools. If not, see <http://www.gnu.org/licenses/>.
"""
Prompts for a space name, searches for space, confirms match and then empties space of all
users, effectively "closing" it.
Requires an auth token from a user with admin privileges against the Webex Control Hub org.
"""
import datetime
import os
import yaml
from webexteamssdk import WebexTeamsAPI, ApiError
# specifies separate config file containing non-portable parameters
# looks for a YAML file in the user's home directory under the subfolder "Personal-Local"
# i.e. c:\users\jsmith\Personal-Local\config.yml
CONFIG_FILE = os.path.join(os.path.expanduser('~'), "Personal-Local", "config.yml")
# user agent for browser fake operations (lifted from Chrome v74)
USER_AGENT = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
' (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36')
STALE_DAYS = 60
EMPTY_THRESHOLD = 1
def bad_choice():
"""print error string and exit"""
print('### No spaces found or invalid selection made, exiting.')
exit()
def old_date(datestamp, stale_threshold):
"""Compares datestamp with stale date"""
if datestamp:
date = datetime.datetime.strptime(datestamp, "%Y-%m-%dT%H:%M:%S.%fZ")
if date.date() < stale_threshold:
return True
return False
def confirmed(question):
"""ask user to confirm"""
answer = input(question + "(y/n): ").lower().strip()
print("")
while not(answer == "y" or answer == "yes" or \
answer == "n" or answer == "no"):
print("Input yes or no")
answer = input(question + "(y/n):").lower().strip()
print("")
if answer[0] == "y":
return True
else:
return False
def timestamp():
"""return formatted date-time string"""
return datetime.datetime.now().strftime('%b %d_%H:%M:%S')
def matchlist_entry(wx_space):
"""returns dict with space matchlist entry"""
return {'title': wx_space.title,
'lastActivity': wx_space.lastActivity,
'id': wx_space.id,
'creatorId': wx_space.creatorId}
def leave_space(members, myself, api):
"""iterate space members and remove ourself"""
for member in members:
if myself in member.personId:
print('Removing ourselves...')
membership_delete(member.id, api)
def membership_delete(membership_id, api):
"""execute delete call, wrapped in error handler because errors happen on the reg here"""
try:
api.memberships.delete(membership_id)
except ApiError as error:
print(f'#### API error: {error}')
def main():
"""allows user to 'close' one or more spaces in Webex Teams"""
with open(CONFIG_FILE, 'r') as config_file:
config_params = yaml.full_load(config_file)
wxteams_config = config_params['wxteams']
wxteams_token = wxteams_config['auth_token']
wxteams_spacequery = input('Please enter name of space to close (or \'stale\'): ')
# Query Webex Teams API for its list of users, webexteamssdk abstracts most of the work
# https://github.com/CiscoDevNet/webexteamssdk/
print('Building space list, please wait...')
api = WebexTeamsAPI(access_token=wxteams_token)
# Grab our personId, we'll need it later
wxteams_me = api.people.me().id
# Populate list of query matches from full list, case insensitive
wx_space_matchlist = list()
wx_space_fulllist = list(api.rooms.list(type='group', sortBy='lastactivity'))
if wxteams_spacequery == 'stale':
print('Finding stale spaces, this may take quite some time...')
stale_threshold = datetime.date.today() - datetime.timedelta(days=STALE_DAYS)
# wx_space_fulllist = api.rooms.list(type='group', sortBy='lastactivity')
for wx_space in wx_space_fulllist:
if not wx_space.lastActivity or old_date(str(wx_space.lastActivity), stale_threshold):
wx_space_messages = list(api.messages.list(roomId=wx_space.id, max=500))
if len(wx_space_messages) <= EMPTY_THRESHOLD:
wx_space_matchlist.append(matchlist_entry(wx_space))
else:
# Grab our group spaces
# wx_space_fulllist = api.rooms.list(type='group', sortBy='lastactivity')
for wx_space in wx_space_fulllist:
if wxteams_spacequery.upper() in wx_space.title.upper():
wx_space_matchlist.append(matchlist_entry(wx_space))
# Finalize space list
if not wx_space_matchlist:
bad_choice()
elif len(wx_space_matchlist) > 1:
# multiple matches found, present user with choice
counter = 1
print()
for wx_space in wx_space_matchlist:
print(f'{counter}: {wx_space["title"]}, last activity: {wx_space["lastActivity"]}')
counter += 1
try:
space_number = int(input('\nPlease enter number of space to close (0 for all): '))
except ValueError:
bad_choice()
if space_number >= 0 and space_number < len(wx_space_matchlist)+1:
space_number -= 1
else:
bad_choice()
else:
# one match found, use it
space_number = 0
# space selected
wx_space_remove_all = False
if space_number >= 0:
wx_space_matchlist = [{'title': wx_space_matchlist[space_number]['title'],
'lastActivity': wx_space_matchlist[space_number]['lastActivity'],
'id': wx_space_matchlist[space_number]['id'],
'creatorId': wx_space_matchlist[space_number]['creatorId']}]
wx_space_matchtitle = wx_space_matchlist[0]['title']
else:
wx_space_matchtitle = 'ALL'
wx_space_remove_all = True
if confirmed(f'{wx_space_matchtitle} selected, are you sure?'):
for wx_space in wx_space_matchlist:
print(f'Working on {wx_space["title"]}')
wx_space_members = api.memberships.list(roomId=wx_space['id'])
if wx_space_remove_all and wx_space['creatorId'] == wxteams_me:
print('ALL selected and our space, closing')
api.rooms.delete(wx_space['id'])
elif wx_space_remove_all and wx_space['creatorId'] != wxteams_me:
print('ALL selected and NOT our space, leaving')
leave_space(wx_space_members, wxteams_me, api)
elif not wx_space_remove_all:
api.rooms.delete(wx_space['id'])
print('Complete.')
else:
bad_choice()
if __name__ == "__main__":
main()