N1KF's forum game The Longest Wait is cool, but he didn't consider the work needed to calculate the scoreboard for 100+ posts.
We should campaign automate this...
Holy mother of jank it works.
I used Python to crawl all pages of the thread and retrieve their HTML, and Regex to search the HTML for post timestamps and authors. The time difference between consecutive posts can then be calculated to generate the scoreboards for both The Longest Wait and "The Shortest Wait".
▼Example raw output
The Longest Wait (Days, Hours:Minutes:Seconds)
1. 4 days, 8:21:40 - BuzzerBee
2. 4 days, 3:28:22 - Abiqi
3. 3 days, 0:44:10 - Blackmask
4. 2 days, 10:09:33 - Zumza
5. 2 days, 9:42:52 - Snowester
6. 2 days, 6:45:11 - Core
7. 2 days, 2:34:30 - Onjit
8. 1 day, 23:44:56 - 2b55b5g
9. 1 day, 21:22:47 - theoldinese
10. 1 day, 15:03:15 - Tomahawk
11. 1 day, 9:57:10 - ExFabian
12. 1 day, 9:51:43 - GravityDonut
13. 1 day, 3:50:37 - Kentiya
14. 1 day, 2:47:50 - Edilights
15. 21:34:33 - KyYay
16. 20:43:27 - hummerz5
17. 19:53:57 - soniiiety
18. 18:05:45 - NoNK
19. 17:32:35 - N1KF
20. 15:29:34 - Sticksam
21. 7:15:48 - Firecrackericebreak
22. 5:09:47 - wjyg
23. 4:55:10 - Minimania
The Shortest Wait (Days, Hours:Minutes:Seconds)
(doesn't include doubleposting)
1. 0:01:28 - ExFabian
2. 0:02:58 - Edilights
3. 0:03:12 - N1KF
4. 0:04:44 - 2b55b5g
5. 0:06:57 - Tomahawk
6. 0:09:24 - Snowester
7. 0:28:26 - GravityDonut
8. 2:19:41 - Abiqi
9. 3:29:22 - BuzzerBee
10. 3:33:24 - Minimania
11. 5:09:47 - wjyg
12. 7:15:48 - Firecrackericebreak
13. 10:30:36 - NoNK
14. 12:28:39 - Core
15. 15:29:34 - Sticksam
16. 17:47:32 - theoldinese
17. 18:24:31 - Kentiya
18. 19:53:57 - soniiiety
19. 20:43:27 - hummerz5
20. 21:34:33 - KyYay
21. 1 day, 0:15:08 - Zumza
22. 2 days, 2:34:30 - Onjit
23. 3 days, 0:44:10 - Blackmask
▼Source code
I also put it on GitHub to make it easier to read.
▼Source code
from urllib.request import Request, urlopen
import re
from datetime import datetime, timedelta
url = "https://forums.everybodyedits.com/viewtopic.php?id=47849"
selector = "/viewtopic.php?id=47849"
html = ""
r = Request(url=url, headers={'User-Agent': 'Mozilla/5.0'})
#Match next page number in thread.
nextPageRegex = '(?<=<a rel="next" href="viewtopic.php\?id=47849&p=)\d+(?=">Next<\/a>)'
#For matching timestamps.
regexTimeLookBehind = '(?<=<a href="viewtopic.php\?pid=\d{6}#p\d{6}">' #Positive lookbehind.
regexTimeLookAhead = "(?=<\/a>)" #Positive lookahead.
regexDate = "\d{4}-\d{2}-\d{2}" #YYYY-MM-DD
regexTime = "\d{2}:\d{2}:\d{2}" #HH:MM:SS
#Match YYYY-MM-DD HH:MM:SS of older posts.
regexOlderPostTimestamp = f"{regexTimeLookBehind}){regexDate} {regexTime}{regexTimeLookAhead}"
#Match HH:MM:SS of posts from Yesterday.
regexYesterdayPostTimestamp = f"{regexTimeLookBehind}Yesterday ){regexTime}{regexTimeLookAhead}"
#Match HH:MM:SS of posts from Today.
regexTodayPostTimestamp = f"{regexTimeLookBehind}Today ){regexTime}{regexTimeLookAhead}"
#Match username - one-word or two-word string between ">" and "</span>" AFTER timestamp.
#May not work if username has more than one space.
regexUsername = "(?<=>)\S+( \S+)?(?=<\/span>)"
posts = [] #List of tuples - [(timestamp, author), ...]
while True:
if html != "": #First page already processed.
p = re.search(nextPageRegex, html) #Look for next page in thread.
if p is None: break #No "Next" page. Terminate while loop.
else: r.selector = f"{selector}&p={p.group()}" #Add next page number to HTML request.
html = urlopen(r).read().decode("utf8") #Download HTML for page.
#Get timestamps and authors of all older posts.
for match in re.finditer(regexOlderPostTimestamp, html):
#Parse timestamp and convert to unix.
timestamp = datetime.strptime(match.group(), "%Y-%m-%d %H:%M:%S").timestamp()
author = re.search(regexUsername, html[match.span()[1]:]).group() #Get post author.
posts.append((timestamp, author))
dateToday = datetime.today().date()
dateYesterday = dateToday - timedelta(days=1)
#Get times and authors of all "Yesterday" posts.
for match in re.finditer(regexYesterdayPostTimestamp, html):
time = datetime.strptime(match.group(), "%H:%M:%S").time()
timestamp = datetime.combine(dateYesterday, time).timestamp()
author = re.search(regexUsername, html[match.span()[1]:]).group()
posts.append((timestamp, author))
#Get times and authors of all "Today" posts.
for match in re.finditer(regexTodayPostTimestamp, html):
time = datetime.strptime(match.group(), "%H:%M:%S").time()
timestamp = datetime.combine(dateToday, time).timestamp()
author = re.search(regexUsername, html[match.span()[1]:]).group()
posts.append((timestamp, author))
longestDeltas = {} # { "username" = unixDelta, ... }
shortestDeltas = {}
for i in range(1, len(posts)):
name = posts[i][1]
delta = posts[i][0] - posts[i-1][0]
#Save longest delta for each poster.
if name not in longestDeltas or delta > longestDeltas[name]:
longestDeltas[name] = delta
#Save shortest delta. Ignore doubleposts.
if (name not in shortestDeltas or delta < shortestDeltas[name]) and name != posts[i-1][1]:
shortestDeltas[name] = delta
print("The Longest Wait (Days, Hours:Minutes:Seconds)\n")
#Sort deltas descending and iterate resulting list of tuples.
for i, (name, delta) in enumerate(sorted(longestDeltas.items(), key = lambda x : -x[1]), start=1):
#Print index (starting at 1), time duration and username.
print(f"{i}. {str(timedelta(seconds=delta))} - {name}")
print("\nThe Shortest Wait (Days, Hours:Minutes:Seconds)")
print("(doesn't include doubleposting)\n")
for i, (name, delta) in enumerate(sorted(shortestDeltas.items(), key = lambda x : x[1]), start=1):
#Print index (starting at 1), time duration and username.
print(f"{i}. {str(timedelta(seconds=delta))} - {name}")
@N1KF - ur welcome
One bot to rule them all, one bot to find them. One bot to bring them all... and with this cliché blind them.