-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbot.py
309 lines (243 loc) · 8.69 KB
/
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
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
# Speedrunning-leaderboard-Discord-Bot-Python © 2022 by Iwakura Megumi is
# licensed under CC BY-NC 4.0. To view a copy of this license, visit
# http://creativecommons.org/licenses/by-nc/4.0/
import json
import os
import sys
from datetime import datetime, timedelta
import aiofiles
import nextcord
import sqlite3
import srcomapi, srcomapi.datatypes as dt
from dotenv import load_dotenv
from nextcord.ext import commands
from rich import print, console
intents = nextcord.Intents.default()
intents.message_content = True
intents.members = True
bot = commands.Bot(
command_prefix="!",
status=nextcord.Status.do_not_disturb,
activity=nextcord.Game(name="https://github.com/gvmii"),
intents=intents,
)
console = console.Console()
load_dotenv()
# Inititialize connection to the SQLITE3 database
con = sqlite3.connect("data/database.db")
cur = con.cursor()
async def read_config():
# tries to open config.json, if it fails it will return false and prompt
# the user to run setup.py
try:
async with aiofiles.open(
"data/config.json", mode="r", encoding="utf8"
) as jsonfile:
contents = await jsonfile.read()
except FileNotFoundError:
print(f'[red]config file not found. try running setup.py[/red]')
return False
config = json.loads(contents)
console.log("Config loaded [green]successfully[/green].")
print(config)
return config
# On ready, do this.
@bot.event
async def on_ready():
config = await read_config()
# if config is not False do stuff, otherwise quit & tell user to run
# setup.py
if config:
if not config["channel_id"]:
config = await read_config()
try:
channel_id = int(config["channel_id"])
console.log(f"Channel ID Set to: {channel_id}.")
except Exception as e:
console.log(f"[red]Error[/red]: Couldn't read channel id: {e}")
sys.exit(1)
print(f"Logged in as {bot.user}.")
channel = bot.get_channel(channel_id)
console.log(
f"Channel #{channel.name} ({channel.id}) [green]found[/green]."
)
else:
exit()
# @bot.event
# async def on_command_error(ctx, error):
# console.log(f"[red]Error[/red]: {str(error)}")
@bot.slash_command()
async def ping(ctx):
await ctx.send("pong")
async def write_to_file(data, file):
async with aiofiles.open(file, "w") as f:
await f.write(json.dumps(data))
print("Done writing JSON data into .json file")
async def read_dict(file):
async with aiofiles.open(file, "r") as f:
loaded_dict = json.loads(await f.read())
with_int_keys = {int(key): value for key, value in loaded_dict.items()}
return with_int_keys
async def read_list(file):
async with aiofiles.open(file, "r") as f:
loaded_list = json.loads(await f.read())
return loaded_list
async def deltify_time(time):
# This might be slow because try excepts are slow in Python, but whatever.
# TODO: Make this a bit more efficient
try:
t = datetime.strptime(time, "%H:%M:%S.%f").time()
delta = timedelta(
hours=t.hour,
minutes=t.minute,
seconds=t.second,
microseconds=t.microsecond,
)
except ValueError:
return ValueError
return delta
VALID_CATEGORIES = ["ANY%", "ARB", "TRUEENDING"]
async def validate_category(category):
global VALID_CATEGORIES
c = category.upper()
if c in VALID_CATEGORIES:
return True
else:
return False
# command to change the channel id. not too neccessary and will probably
# remove later
@bot.slash_command()
@commands.has_permissions(administrator=True)
async def setchannelid(ctx, channel_id):
async with aiofiles.open("config.json", mode="r", encoding="utf8") as f:
contents = await f.read()
thing = json.loads(contents)
thing["channel_id"] = channel_id
await write_to_file(thing, "config.json")
embed = nextcord.Embed(title="Success", description=f"Successfully changed the Channel ID to '{channel_id}'",
color=nextcord.Color.from_rgb(255, 38, 96))
await ctx.send(embed=embed)
@bot.slash_command()
async def register(ctx):
user_id = ctx.user.id
loaded_file = await read_dict("times.json")
writing_template = {
user_id: {
"any%": {"time": "", "state": ""},
"arb": {"time": "", "state": ""},
}
}
loaded_file.update(writing_template)
await write_to_file(loaded_file, "times.json")
@bot.slash_command()
async def submit_time(ctx, category: str, time: str):
user_id = ctx.user.id
if await validate_category(category) is False:
global VALID_CATEGORIES
await ctx.send(
"Please write a valid category. Valid categories are "
+ str(VALID_CATEGORIES)
)
return
if category == 'any%':
category = 'anypercent'
# TODO: Fix this jank thing
delta = await deltify_time(time)
try:
formatted_time = str(delta.total_seconds())
except AttributeError:
await ctx.send(
"Please write your time in the following format: H:M:S.ms"
)
return
cur.execute(f"""
INSERT OR REPLACE INTO {category} VALUES
({user_id}, {formatted_time})
""")
con.commit()
await ctx.send(
f"Successfully submitted time of **{str(delta)}** for the "
f"category **{category}** For the user {ctx.user} with ID "
f"{user_id} at {datetime.now()}"
)
@bot.slash_command()
async def personal_best(
ctx, category: str = "Any%", user: nextcord.Member = None
):
user = user or ctx.user
cur.execute(f"""
SELECT MIN(TIME) FROM {category} WHERE USER_ID = {user.id}
""")
time = cur.fetchall()[0][0]
await ctx.send(f"{user.mention}'s {category} time is {str(timedelta(seconds=time)).replace('0000', '') }")
@bot.slash_command()
async def leaderboard(ctx, category):
## SAVING THIS ABSOLUTE FUCKING MONSTROSITY FOR PROSPERITY ##
# loaded_file = await read_dict("times.json")
# dict_to_sort = {}
# for user_id, time in loaded_file.items():
# print(time[category])
# dict_to_sort.update({user_id: float(time[category]["time"])})
# sorted_obj = dict(sorted(dict_to_sort.items(), key=lambda i: i[1]))
# embed = nextcord.Embed(title="Leaderboard")
# print(sorted_obj.items())
# number = 1
# for userid, time in sorted_obj.items():
# user = await bot.fetch_user(int(userid))
# embed.add_field(
# name=f"{number} - {user.name}",
# value=str(timedelta(seconds=time)).replace("0000", ""),
# inline=False,
# )
# number += 1
if(category == 'any%'):
category = 'anypercent'
cur.execute(f"""
SELECT user_id, time FROM {category} ORDER BY time ASC
""")
leaderboard = cur.fetchall()
embed = nextcord.Embed(title=f"Leaderboard for **{category}**", color=nextcord.Color.from_rgb(255, 38, 96))
number = 0
for user_id, time in leaderboard:
number += 1
user = await bot.fetch_user(int(user_id))
embed.add_field(
name = f'{number} - {user}',
value = str(timedelta(seconds=time)).replace("0000", ""),
inline = False)
await ctx.send(embed=embed)
async def get_game():
api = srcomapi.SpeedrunCom()
game = api.search(srcomapi.datatypes.Game, {"name": "Celeste"})[0]
return game
@bot.slash_command()
async def categories(ctx):
game = await get_game()
embed = nextcord.Embed(title="Categories", color=nextcord.Color.from_rgb(255, 38, 96))
for category in game.categories:
embed.add_field(name=category.name, value=category.weblink)
await ctx.send(embed=embed)
@bot.slash_command()
async def best_times(ctx, *, category):
game = await get_game()
print(game.categories)
count = -1
cat_array_num = False
for i in game.categories:
print(i)
count += 1
if i.name.lower() == category.lower():
cat_array_num = count
print(cat_array_num)
break
if not cat_array_num:
await ctx.send("Invalid Category. You can check the categories with /categories")
return
record = game.categories[cat_array_num].records[0].runs[0]["run"]
time = (record.times["primary_t"])
delta = str(timedelta(seconds=time))
print(delta)
embed = nextcord.Embed(title="Best Times", color=nextcord.Color.from_rgb(255, 38, 96))
embed.add_field(name=game.categories[cat_array_num].name, value=f'Any%: {delta.replace("0000", "")}')
await ctx.send(embed=embed)
bot.run(os.getenv("TOKEN"))