-
Notifications
You must be signed in to change notification settings - Fork 1
/
leadboard_time.py
155 lines (118 loc) · 5.61 KB
/
leadboard_time.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
import datetime
import os
import sched
import time
import json
import urllib.request
from time import gmtime, strftime
from discord.ext import commands
import discord
# Load the variables
dotenv = Dotenv('.env')
TOKEN = dotenv['DISCORD_TOKEN']
URL = dotenv['AOC_URL']
COOKIE = dotenv['AOC_COOKIE']
# Advent Of Code request that you don't poll their API more often than once every 15 minutes
POLL_MINS = 15
YEAR = 2023
# Discord messages are limited to 2000 characters. This also includes space for 6 '`' characters for a code block
MAX_MESSAGE_LEN = 2000 - 6
PLAYER_STR_FORMAT = '{rank:2}) {name:{name_pad}} ({points:{points_pad}}) {stars:{stars_pad}}* ({star_time})\n'
# A cache to make sure we do not need to poll the Advent of Code API within 15 min
players_cache = ()
def get_players():
global players_cache
now = time.time()
debug_msg = 'Got Leader board From Cache'
# If the cache is more than POLL_MINS old, refresh the cache, else use the cache
if not players_cache or (now - players_cache[0]) > (60 * POLL_MINS):
debug_msg = 'Got Leader board Fresh'
req = urllib.request.Request(URL)
req.add_header('Cookie', 'session=' + COOKIE)
page = urllib.request.urlopen(req).read()
data = json.loads(page)
# Extract the data from the JSON
players = [(member['name'],
member['local_score'],
member['stars'],
int(member['last_star_ts']),
member['completion_day_level'],
member['id']) for member in data['members'].values()]
# Players that are anonymous have no name in the JSON, so give them a default name "Anon"
for i, player in enumerate(players):
if not player[0]:
anon_name = "anon #" + str(player[5])
players[i] = (anon_name, player[1], player[2], player[3], player[4], player[5])
# Sort the table primarily by score, secondly by stars and finally by timestamp
players.sort(key=lambda tup: tup[3])
players.sort(key=lambda tup: tup[2], reverse=True)
players.sort(key=lambda tup: tup[1], reverse=True)
players_cache = (now, players)
print(debug_msg)
return players_cache[1]
async def output_leader_board(context, leader_board_lst):
item_len = len(leader_board_lst[0])
block_size = MAX_MESSAGE_LEN // item_len
tmp_leader_board = leader_board_lst
while (len(tmp_leader_board) * item_len) > MAX_MESSAGE_LEN:
output_str = '```'
output_str += ''.join(tmp_leader_board[:block_size])
output_str += '```'
await context.send(output_str)
tmp_leader_board = tmp_leader_board[block_size:]
output_str = '```'
output_str += ''.join(tmp_leader_board)
output_str += '```'
await context.send(output_str)
async def leader_board(context, num_players: int = 20):
print('Leader board requested')
players = get_players()[:num_players]
# Get string lengths for the format string
max_name_len = len(max(players, key=lambda t: len(t[0]))[0])
max_points_len = len(str(max(players, key=lambda t: t[1])[1]))
max_stars_len = len(str(max(players, key=lambda t: t[2])[2]))
leader_board = []
for i, player in enumerate(players):
leader_board.append(PLAYER_STR_FORMAT.format(rank=i + 1,
name=player[0], name_pad=max_name_len,
points=player[1], points_pad=max_points_len,
stars=player[2], stars_pad=max_stars_len,
star_time=time.strftime('%H:%M %d/%m', time.localtime(player[3]))))
await output_leader_board(context, leader_board)
async def keen(context):
# Only respond if used in a channel called 'advent-of-code'
if context.channel.name != 'advent-of-code':
return
print('Keenest bean requested')
all_players = get_players()
# Calculate the highest number of stars gained by anyone in the leader board
max_stars = max(all_players, key=lambda t: t[2])[2]
# Get list of players with max stars
players = [(i, player) for i, player in enumerate(all_players) if player[2] == max_stars]
# Find the first person who got the max stars
i, player = min(players, key=lambda t: t[1][3])
result = 'Today\'s keenest bean is:\n```'
result += PLAYER_STR_FORMAT.format(rank=i + 1,
name=player[0], name_pad=len(player[0]),
points=player[1], points_pad=len(str(player[1])),
stars=player[2], stars_pad=len(str(player[2])),
star_time=time.strftime('%H:%M %d/%m', time.localtime(player[3])))
result += '```'
await context.send(result)
# Create the bot and specify to only look for messages starting with '!'
bot = commands.Bot(command_prefix='!', intents=discord.Intents.all())
@bot.event
async def on_ready():
print(f'{bot.user.name} has connected to Discord and is in the following channels:')
for guild in bot.guilds:
print(' ', guild.name)
@bot.command(name='daily_leader_board')
async def daily_leader_board(context, num_players: int = 20):
day = 2
while True:
await leader_board(context, num_players)
await keen(context)
print("I feel sleepy: ", str((datetime.datetime(YEAR,12,day,20,00,00) - datetime.datetime.now()).total_seconds()))
time.sleep((datetime.datetime(YEAR,12,day,20,00,00) - datetime.datetime.now()).total_seconds())
day += 1
bot.run(TOKEN)