-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSRC_Comments_Deleter.py
362 lines (314 loc) · 14.5 KB
/
SRC_Comments_Deleter.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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
#Change this to the Game name on SRC
#Grab this from the URL easily; such as "https://www.speedrun.com/newtone" would be "newtone"
GamePage = 'newtone'
#If username contains these strings, don't delete them
#Recommended to put Moderator username list in this. It is comma separated, quotes around names
#If a comment is made by anyone in this list, it will *not* delete the comment. This is case-insensitive.
userWhiteList = ["kupokraft", "Gordo", "Vapo", "Critch_", "ItsTicker", "Greenbulmers", "djlambton", "Derek", "Spruce37", "PotatoOnCrack", "Greeny0359"]
#How many runs from each leaderboard should we check?
#For example, if this was "25" it would only get the top 25 runs from each category/level leaderboard
#If this is set quite high for leaderboards with many categories and levels, it may take a while to refresh.
maxRunsToCheck = 50
#How many seconds in between refreshing runs + comments. Don't crank this number too low, SRC crashes enough as is
timewait = 60
#Should this run as a dryrun? Basically, if set to "True", script will only print comment information of what it would delete instead of deleting them
#This is so you can test the script before running it
dryRun = True
#NOTE THAT THIS REQUIRES A SUPPORTED BROWSER TO BE LOGGED IN TO SRC TO ACTUALLY DELETE COMMENTS
#That is because this uses cookies from your web browser in order to make the delete request
#Which web browser should the script grab cookies from?
#Supported options are: Firefox, Chrome, Chromium, Opera, Edge, Brave
#Examples:
#Browser = 'Firefox'
#Browser = 'Chrome'
#Browser = 'Chromium'
#Browser = 'Opera'
#Browser = 'Edge'
#Browser = 'Brave'
Browser = 'Chrome'
#END OF USER-CONFIGURABLE PARTS
#Check python version
import sys
if sys.version_info < (3, 0):
sys.stdout.write("Sorry, requires Python 3.x, not Python 2.x\n")
sys.exit(1)
#need json formatter
import json
#Make sure browser_cookie3 is installed
try:
import browser_cookie3
except ImportError:
print ('--Python module browser_cookie3 not installed, unable to delete comments without using a Speedrun.com cookie. Continuing in Dryrun mode--')
dryRun = True
pass
def cookieFailure(x = False):
if not x:
print('--Failed to import cookies. Is ' + Browser + ' installed and logged in to speedrun.com?--')
print('--Continuing in Dryrun mode, as I can\'t delete anything without cookies--')
global dryRun
dryRun = True
#Don't need cookies if we're just loading comments
if not dryRun:
#Import cookies from Firefox for speedrun.com
if (Browser.lower() == 'firefox'):
try:
cookiejar = browser_cookie3.firefox(domain_name='speedrun.com')
except:
cookieFailure()
pass
elif (Browser.lower() == 'chrome'):
try:
cookiejar = browser_cookie3.chrome(domain_name='speedrun.com')
except:
cookieFailure()
pass
elif (Browser.lower() == 'chromium'):
try:
cookiejar = browser_cookie3.chromium(domain_name='speedrun.com')
except:
cookieFailure()
pass
elif (Browser.lower() == 'opera'):
try:
cookiejar = browser_cookie3.opera(domain_name='speedrun.com')
except:
cookieFailure()
pass
elif (Browser.lower() == 'edge'):
try:
cookiejar = browser_cookie3.edge(domain_name='speedrun.com')
except:
cookieFailure()
pass
elif (Browser.lower() == 'brave'):
try:
cookiejar = browser_cookie3.brave(domain_name='speedrun.com')
except:
cookieFailure()
pass
else:
print('--Unsupported browser specified, check the "Browser = " section of the script above--')
cookieFailure(True)
pass
from datetime import datetime
import os
import requests
import time
import ctypes
import urllib3
#sets nice title
ctypes.windll.kernel32.SetConsoleTitleW("SRC Comments Grabber/Deleter v1.0.2")
#disable SSL warnings. SRC requires HTTPS but sometimes their certificate isn't "proper", this makes it connect
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
#Makes a universal cls function to clear screen. Thanks popcnt: https://stackoverflow.com/a/684344
def cls():
os.system('cls' if os.name=='nt' else 'clear')
#Plz no bully the SRC servers
#If you're bright enough to remove this sleep, you're bright enough to know why we shouldn't send one request after another to SRC instantly
def sleepy():
time.sleep(0.25)
#Set whitelist to lowercase, so we can do case-insensitive checking later by lowercase-ing the usernames in comments
for i in range(len(userWhiteList)):
userWhiteList[i] = userWhiteList[i].lower()
#hasLevels boolean; if a game has individual levels we need to handle getting runs a bit extra methody
hasLevels = False
#Time to get a list of every single leaderboard, category and level for the game. I only do this once instead of every time we check for comments
try:
#Get list of Categories
categories = requests.get('https://www.speedrun.com/api/v1/games/' + GamePage + '/categories', verify=False)
except Exception as e:
#If it fails, URL is invalid... or SRC is down. That's always an option
print ("Exception: " + str(e))
print ("Invalid URL connection to SRC failed. Open this python script and check that 2nd line! Exiting...")
sys.exit(1)
jsonOut = json.loads(categories.content)
categoryList = []
for i in jsonOut['data']:
#print(i['id'])
#print(jsonOut['data'][0]['id'])
if (i['type'] == 'per-game'):
categoryList.append(i['id'])
if (i['type'] == 'per-level'):
hasLevels = True
#Get List of Levels
levelList = []
if hasLevels:
try:
levels = requests.get('https://www.speedrun.com/api/v1/games/' + GamePage + '/levels', verify=False)
except Exception as e:
#If it fails, URL is invalid... or SRC is down. That's always an option
print ("Exception: " + str(e))
print ("Connection to SRC failed. SRC could be down! Exiting...")
pass
jsonOut = json.loads(levels.content)
for i in jsonOut['data']:
levelList.append(i['id'])
#They have set us up the loop
while True:
if not dryRun:
print('WARNING, SCRIPT CONFIGURED TO DELETE COMMENTS. CLOSE WINDOW OR PRESS CTRL+C NOW TO CANCEL.')
print('SRC page to check: https://speedrun.com/' + GamePage)
if not dryRun:
print('Using browser cookies from: ' + Browser)
print('User Whitelist:')
for i in range(len(userWhiteList)):
print(userWhiteList[i],end = ', ')
print('\n')
print('Number of categories: ' + str(len(categoryList)))
if hasLevels:
print('Number of levels: ' + str(len(levelList)))
if not dryRun:
#Get csrftoken from speedrun.com main page. Without beautifulsoup this is 'fun'TM
try:
mainPage = requests.get('https://www.speedrun.com/', verify=False, cookies=cookiejar)
split1 = str(mainPage.content).split('<meta name="csrftoken" content="')
split2 = split1[1].split('">')
csrftoken = split2[0]
#print('csrftoken: ' + csrftoken)
#Just in-case people don't want this showing on their screen...
print('csrftoken: ********************************')
except Exception as e:
print ("Exception: " + str(e))
print('\nCould not connect to SRC and get csrftoken. Are you sure you\'re logged in to firefox on SRC? This script uses Firefox\'s cookies')
sys.exit(0)
try:
#Get list of runs from all categories
runList = []
print('Getting list of top ' + str(maxRunsToCheck) + ' runs from all categories...')
for i in categoryList:
try:
runs = requests.get('https://www.speedrun.com/api/v1/leaderboards/' + GamePage + '/category/' + i, verify=False)
except Exception as e:
#If it fails, URL is invalid... or SRC is down. That's always an option
print ("Exception: " + str(e))
print ("Connection to SRC failed. SRC could be down! Exiting...")
pass
#Give SRC some space to breathe between requests
sleepy()
try:
jsonOut = json.loads(runs.content)
except Exception as e:
print ("Exception converting 'runs' to JSON from categories: " + str(e))
print('runs.content value: ' + runs.content)
sys.exit(1)
runsAdded = 0
for i in jsonOut['data']['runs']:
if (runsAdded < maxRunsToCheck):
runList.append(i['run']['id'])
runsAdded += 1
#Get list of runs from all Levels (yes, they're separate from Categories... thanks SRC)
if hasLevels:
print('Getting list of top ' + str(maxRunsToCheck) + ' runs from all levels...')
for i in levelList:
try:
runs = requests.get('https://www.speedrun.com/api/v1/runs?level=' + i, verify=False)
except Exception as e:
#If it fails, URL is invalid... or SRC is down. That's always an option
print ("Exception: " + str(e))
print ("Connection to SRC failed. SRC could be down!")
pass
#Give SRC some space to breathe between requests
sleepy()
try:
jsonOut = json.loads(runs.content)
except Exception as e:
print("Exception converting 'runs' to JSON from levels: " + str(e))
print('runs.content value: ' + runs.content)
sys.exit(1)
runsAdded = 0
for i in jsonOut['data']:
if (runsAdded < maxRunsToCheck):
runList.append(i['id'])
runsAdded += 1
print('Number of runs: ' + str(len(runList)))
#Finally, we get a list of comments from every run
print('Getting list of comments from all runs... this may take a while')
commentList = []
commentRunDict = {}
#Why did I name the loop counter this? Don't ask, it was hours past bedtime
donionRings = 0
percCompleteNew = 0
percComplete = 0
for x in runList:
try:
now = str(round(time.time()))
#for testing deleting comments that don't exist. SRC caches anything gotten with that specific 'now' value
#now = '0'
runPage = requests.get('https://www.speedrun.com/_fedata/comments/list?itemType=run&itemId=' + x + '&page=1&now=' + now + '&all=1', verify=False)
except Exception as e:
#If it fails, URL is invalid... or SRC is down. That's always an option
print ("Exception: " + str(e))
print ("Connection to SRC failed. SRC could be down!")
donionRings +=1
#Give SRC some space to breathe between requests
sleepy()
percCompleteNew = round(donionRings / len(runList) * 100)
if (percCompleteNew != percComplete):
print('Complete: %' + str(percComplete + 1), end='\r', flush=True)
percComplete = percCompleteNew
if (percComplete == 100):
print()
jsonOut = json.loads(runPage.content)
for i in jsonOut['comments']:
commentDict = {'id': i['commentId'],'user': (i['user']['name']),'comment': i['text'],'run':x}
commentRunDict[i['commentId']] = x
#print(commentDict)
#print('--')
#print('CommentID: \t' + i['commentId'])
#print('Username: \t' + i['user']['name'])
#print('Comment: \t' + i['text'])
commentList.append(commentDict)
print()
commentIdsToDelete = []
#Check if user is in whitelist
for i in commentList:
if i['user'].lower() in userWhiteList:
pass
#print('not deleting comment by: ' + i['user'])
else:
commentIdsToDelete.append(i['id'])
#print('deleting comment by: ' + i['user'])
#
if dryRun:
print('Dryrun Enabled, printing all comment info: ')
for commentData in commentList:
print(commentData)
print('Dryrun enabled, would delete these comments: ')
for i in commentIdsToDelete:
print('Would delete: ' + i)
if not commentIdsToDelete:
print('None')
else:
for i in commentIdsToDelete:
print('Deleting CommentID: ' + i)
#i = 'o4liy'
deletePost = {"commentId":i,"deleted":"true","block":"false","purge":"false","csrftoken":csrftoken}
deleteRequest = requests.post('https://www.speedrun.com/_fedata/comments/delete', json = deletePost, verify=False, cookies=cookiejar)
#print('\n\n\n\n\n\n-----------RESPONSE:--------------\n\n ' + deleteRequest.text + '\n\n\n\n\n\nJSONOUT\n\n\n')
jsonOut = deleteRequest.json()
try:
deletedResult = str(jsonOut['comment']['deleted'])
deletedResult = deletedResult.lower()
if (deletedResult == 'true'):
print('Successfully Deleted commentID ' + i + ' from run https://speedrun.com/run/' + commentRunDict[i])
else:
print('Failed to delete for some reason??? This message shouldn\'t even appear anywhere ever. Here\'s the JSON:')
print(str(jsonOut))
print('Here\'s the json type:')
try:
print(type(jsonOut['comment']['deleted']))
except:
pass
except:
print ('Failed to Delete commentID ' + i + ' from runID ' + commentRunDict[i] + '. Error from SRC: ' + str(jsonOut['errors'][0]))
sleepy()
if not commentIdsToDelete:
print('Found nothing to delete')
print('Refreshing in ' + str(timewait) + ' seconds.')
#Wait "timewait" amount of seconds before running again
time.sleep(timewait)
cls()
except KeyboardInterrupt:
# quit
print('\nKeyboard interrupt received, quitting...')
sys.exit()