/
run_bot.py
233 lines (200 loc) · 7.09 KB
/
run_bot.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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
import pywikibot
from pywikibot import pagegenerators, textlib
import re
site = pywikibot.Site('en', 'wikipedia')
TABLE_LOCATION = 'Wikipedia:Sockpuppet investigations/SPI/Cases' # location where this program should post the SPI case list
def get_clerk_list():
print("Getting list of clerks")
clerks = []
page = pywikibot.Page(site, 'Wikipedia:Sockpuppet investigations/SPI/Clerks')
lines = page.text.split('\n')
i = 0
while i < len(lines) and 'Active clerks' not in lines[i]:
i += 1
pattern = re.compile(r'{{(?:u|U)ser[^\|]*\|([^}]+)}}')
while i < len(lines) and 'inactive clerks' not in lines[i].lower():
m = pattern.search(lines[i])
if m:
clerks.append(m.group(1))
i += 1
print(clerks)
return clerks
def get_checkuser_list():
print("Getting list of checkusers")
checkusers = [user['name'] for user in site.allusers(group='checkuser')]
print(checkusers)
return checkusers
def get_status_from_categories(categories):
"""
For concision, cases will usually appear in the SPI case table only once, except that
the following statuses will always appear: clerk, admin, checked, close, and one of
(inprogress, endorsed, relist, CUrequest).
For example, if an SPI case has five active reports, one with 'clerk', one with 'close'
one with 'endorsed', one with 'CUrequest', and one with 'open', it will appear three times
in the table as 'clerk', 'close', and 'endorsed'.
"""
print("Getting case status")
cat2status = {
'SPI cases currently being checked': 'inprogress',
'SPI cases awaiting a CheckUser': 'endorsed',
'SPI cases relisted for a CheckUser': 'relist',
'SPI cases requesting a checkuser': 'CUrequest',
'SPI cases needing an Administrator': 'admin',
'SPI cases needing a Clerk': 'clerk',
'SPI cases CU complete': 'checked',
'SPI cases awaiting review': 'open',
'SPI cases declined for checkuser by CU': 'cudeclined',
'SPI cases declined for checkuser by clerk': 'declined',
'SPI cases requesting more information': 'moreinfo',
'SPI cases on hold by checkuser': 'cuhold',
'SPI cases on hold by clerk': 'hold',
'SPI cases awaiting archive': 'close',
}
statuses = []
for cat in categories:
title = cat.title(with_ns=False)
if title in cat2status:
statuses.append(cat2status[title])
priority = ['clerk', 'admin', 'checked', 'close']
result = []
curequest = {'inprogress': 0, 'relist': 1, 'endorsed': 2, 'CUrequest': 3}
curequest_only = []
misc = {'open': 0, 'cudeclined': 1, 'declined': 2, 'moreinfo': 3, 'cuhold': 4, 'hold': 5}
misc_only = []
for status in statuses:
if status in priority:
result.append(status)
elif status in curequest:
curequest_only.append(status)
elif status in misc:
misc_only.append(status)
if curequest_only:
result.append(min(curequest_only, key=lambda x: curequest[x]))
if misc_only and (len(result) == 0 or (len(result) == 1 and result[0] == 'close')):
result.append(min(misc_only, key=lambda x: misc[x]))
return result
def get_case_details(case_page, clerks=[]):
print("Now getting case details for {0}".format(case_page.title()))
case = {}
# get case name
case['name'] = case_page.title().split("/")[1]
# get case status
case['status'] = get_status_from_categories(case_page.categories())
# get page revisions
revisions = case_page.revisions()
# get last user and time
print("Getting last user")
last_rev = next(revisions)
case['last_user'] = last_rev.user
case['last_user_time'] = last_rev.timestamp.strftime('%Y-%m-%d %H:%M')
# get last clerk/checkuser and time
# the clerks list passed as a parameter also includes checkusers
print("Getting last clerk or checkuser")
if last_rev.user in clerks:
case['last_clerk'] = last_rev.user
case['last_clerk_time'] = last_rev.timestamp.strftime('%Y-%m-%d %H:%M')
else:
case['last_clerk'] = ''
case['last_clerk_time'] = ''
for rev in revisions:
lowercase_edit_summary = rev.comment.lower()
if 'archiv' in lowercase_edit_summary or 'moving case' in lowercase_edit_summary:
# only look for clerks/CUs up to the last archive or case moved to another case
break
if rev.user in clerks:
case['last_clerk'] = rev.user
case['last_clerk_time'] = rev.timestamp.strftime('%Y-%m-%d %H:%M')
break
# get file time
print("Getting file time")
ts = textlib.TimeStripper(site)
case_lines = case_page.text.split('\n')
for line in case_lines:
time = ts.timestripper(line)
if time:
break
if time:
case['file_time'] = time.strftime('%Y-%m-%d %H:%M')
else:
case['file_time'] = 'Unknown'
print(case)
return case
def format_table_row(case):
return "{{" + "SPIstatusentry|{0}|{1}|{2}|{3}|{4}|{5}|{6}".format(
case['name'],
case['status'],
case['file_time'],
case['last_user'],
case['last_user_time'],
case['last_clerk'],
case['last_clerk_time']
) + "}}\n"
def generate_case_table(cases):
result = "{{SPIstatusheader}}\n"
for case in cases:
result += format_table_row(case)
result += '|}'
if len(cases) == 0:
print("No SPI cases detected!")
result += '{{User:Mz7/SPI 0}}'
return result
def sort_cases(cases):
"""
Order of the SPI case table:
inprogress > endorsed > relist > CUrequest > admin > clerk > checked > open > cudeclined >
declined > moreinfo > cuhold > hold > close
"""
rank = {'inprogress': 0, 'endorsed': 1, 'relist': 2, 'QUICK': 3, 'CUrequest': 4, 'admin': 5,
'clerk': 6, 'checked': 7, 'open': 8, 'cudeclined': 9, 'declined': 10, 'moreinfo': 11, 'cuhold': 12,
'hold': 13, 'close': 14}
return sorted(cases, key=lambda case: (rank[case['status']], case['file_time']))
def get_all_cases(clerks):
cat = pywikibot.Category(site, 'Category:Open SPI cases')
gen = pagegenerators.CategorizedPageGenerator(cat)
cases = []
for page in gen:
case = get_case_details(page, clerks)
if len(case['status']) > 1:
statuses = case['status']
for status in statuses:
case_copy = case.copy()
case_copy['status'] = status
cases.append(case_copy)
else:
try:
case['status'] = case['status'][0]
cases.append(case)
except IndexError as e:
print(e)
print("The following case may have been archived while the case details were being grabbed:")
print(case)
cases += get_cu_needed_templates()
return sort_cases(cases)
def get_cu_needed_templates():
print("Getting CU needed templates")
cat = pywikibot.Category(site, 'Category:Requests for checkuser')
gen = pagegenerators.CategorizedPageGenerator(cat)
cases = []
for page in gen:
# ignoring User and Template namespaces because of the issue where some users transclude AIV etc. in those namespaces
if page.namespace() == 2 or page.namespace() == 10:
continue
cases.append({
'name': 'link={0}#checkuser_needed|CU needed: {0}'.format(page.title()),
'status': 'QUICK',
'file_time': page.editTime().strftime('%Y-%m-%d %H:%M'),
'last_user': '',
'last_user_time': '',
'last_clerk': '',
'last_clerk_time': ''
})
print(cases)
return cases
def main():
clerks = get_clerk_list()
clerks += get_checkuser_list()
cases = get_all_cases(clerks)
page = pywikibot.Page(site, TABLE_LOCATION)
page.text = generate_case_table(cases)
page.save(summary='Updating SPI case list ({0} active reports)'.format(len(cases)), minor=False, botflag=True)
main()