-
Notifications
You must be signed in to change notification settings - Fork 0
/
discord-bot.py
308 lines (257 loc) · 10.1 KB
/
discord-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
# -*- coding: utf-8 -*-
import os
from unicodedata import combining
import psycopg2
import datetime
import calendar
import discord
from discord.ext import commands
import asyncio
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
DATABASE_URL = os.getenv('DATABASE_URL')
intents = discord.Intents.default()
intents.members = True
bot = commands.Bot(command_prefix=('SxK ', 'Sxk ', 'sxk '), intents=intents)
async def update_birthdays(cur):
current_date = datetime.datetime.utcnow()
cur.execute("(SELECT * FROM members "
"WHERE "
" birthday > '2036-{0.month}-{0.day}' "
"ORDER BY "
" birthday ASC) "
"UNION ALL "
"(SELECT * FROM members "
"WHERE "
" birthday IS NOT NULL "
"ORDER BY "
" birthday ASC) "
"LIMIT 5;".format(current_date))
channel = bot.get_channel(604388374324838532)
birthday_list = await channel.fetch_message(657232655196094464)
months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
]
ordinal = lambda n: "%d%s" % (n, {1:"st", 2:"nd", 3:"rd"}
.get(n if n<20 else n%10, "th"))
birthday_result = cur.fetchall()
await birthday_list.edit(content="**BIRTHDAYS**\n"
"These knights will have a birthday soon!\n```"
+ "\n".join("{0}{1}{2:>4} {3}".format(
row[1],
# Regular Python formatting works
# incorrectly with Unicode characters,
# so a manual method was implemented
' ' * (25-len(''.join(c for c in row[1]
if combining(c)==0))),
ordinal(int(row[2].day)),
months[int(row[2].month)-1])
for row in birthday_result)
+ "\n```")
# Command for administrators that works with a pinned message, blacklist.
# `SxK blacklist`: passing no arguments will briefly show the list in chat.
# `SxK blacklist name`: passing a name will add that name to the blacklist
# and update the pinned message.
# `SxK blacklist ~~name~~`: passing a name with strikethrough formatting
# will instead remove that name from the blacklist.
@bot.command()
@commands.has_role('Staff')
async def blacklist(ctx, arg=''):
def generate_blacklist(cur):
cur.execute("SELECT * FROM blacklist "
"ORDER BY "
" name ASC;")
return "**BLACKLIST**\n" + "\n".join(i[0] for i in cur.fetchall())
async def update_blacklist(cur):
officers = bot.get_channel(614159495085686794)
blacklist = await officers.fetch_message(657883991692541972)
await blacklist.edit(content=generate_blacklist(cur))
conn = psycopg2.connect(DATABASE_URL, sslmode='require')
cur = conn.cursor()
# The command will show the blacklist for 15 secs if no arguments are passed.
if not arg:
message = await ctx.send(generate_blacklist(cur)
+ '\nReact to this message to prevent timed deletion.\n'
'Doing so will result in outdated lists staying in history.')
cur.close()
conn.close()
await asyncio.sleep(15)
updated_message = await ctx.fetch_message(message.id)
if not updated_message.reactions:
await message.edit(content='This message was edited to hide outdated lists '
'and reduce littering in chat.')
return
# And will insert or delete the name from the database otherwise.
name = arg.split(" ")[0].title()
# Delete command
# `blacklist ~~name~~`
if name[:2] == name[-2:] == "~~":
name = name.strip("~*")
cur.execute("DELETE FROM blacklist "
"WHERE name=%s", [name])
conn.commit()
if cur.rowcount > 0:
await update_blacklist(cur)
await ctx.send("Removed {0} from the blacklist!".format(name))
else:
await ctx.send("Couldn't find {0} in the blacklist.".format(name))
# Insert command
# `blacklist name`
else:
try:
name = name.strip("~*")
cur.execute("INSERT INTO blacklist (name) "
"VALUES (%s);", [name])
conn.commit()
await update_blacklist(cur)
await ctx.send("Added {0} to the blacklist!".format(name))
except psycopg2.errors.UniqueViolation:
await ctx.send("Seems like this player is already "
"in the blacklist.\n"
"Check for errors and try again.")
cur.close()
conn.close()
# Command for setting a new birthday for the caller.
# The pinned message with birthday list will also be updated.
# Example: `SxK birthday 01.01`
@bot.command()
async def birthday(ctx, arg=''):
ordinal = lambda n: "%d%s" % (n, {1:"st", 2:"nd", 3:"rd"}
.get(n if n<20 else n%10, "th"))
months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
]
arg = arg.replace('!', '')
# Checking birthday by given ping
if arg[:2] == "<@" and arg[-1]==">":
conn = psycopg2.connect(DATABASE_URL, sslmode='require')
cur = conn.cursor()
cur.execute("SELECT birthday FROM members "
"WHERE id=%s", [arg[2:-1]])
birth_date = cur.fetchone()[0]
if birth_date is not None:
await ctx.send(f"{arg}'s birthday is on {ordinal(birth_date.day)} "
f"of {months[birth_date.month-1]}.")
else:
await ctx.send("I don't know their birthday yet.")
cur.close()
conn.close()
return
# Finding birthdays by given month
if arg.title() in months:
conn = psycopg2.connect(DATABASE_URL, sslmode='require')
cur = conn.cursor()
cur.execute("SELECT id, birthday FROM members "
"WHERE EXTRACT(MONTH FROM birthday)=%s "
"ORDER BY "
" birthday ASC",
[months.index(arg.title())+1])
guild = bot.get_guild(508545461939077142)
message = ''
for member in cur.fetchall():
user = guild.get_member(int(member[0]))
if user is None: continue
message += (f"{user.display_name} — {ordinal(member[1].day)} "
f"of {months[member[1].month-1]}\n")
await ctx.send(message)
cur.close()
conn.close()
return
for i in ".-/":
char_index = arg.find(i)
if char_index >= 0: break
try:
if char_index >= 0 \
and int(arg[:char_index]) in range(1, 32) \
and int(arg[char_index+1:char_index+3]) in range(1, 13):
author_id = str(ctx.author.id)
birth_date = f"2036-{arg[char_index+1:char_index+3]:0>2}-{arg[:char_index]:0>2}"
conn = psycopg2.connect(DATABASE_URL, sslmode='require')
cur = conn.cursor()
cur.execute("INSERT INTO members (id, name, birthday) "
"VALUES (%(id)s, %(name)s, %(birthday)s) "
"ON CONFLICT (id) DO UPDATE "
"SET birthday=%(birthday)s, "
"name=%(name)s;", {'id': str(author_id),
'name': ctx.author.name,
'birthday': birth_date})
conn.commit()
await update_birthdays(cur)
cur.close()
conn.close()
await ctx.send("Success! Saved {0} of {1} birthday "
"for <@{2}>.".format(ordinal(int(birth_date[8:])),
months[int(birth_date[5:7])-1],
author_id))
else:
await ctx.send("That doesn't look right.\n"
"Make sure you send your birthday in `dd.mm` format. "
"Like this, for example: `24.05`.")
except ValueError:
await ctx.send("Uh oh. Did you try to write your birthday with words?\n"
"I'm not smart enough to understand that yet, try using "
"`dd.mm` format, like this: `24.05`.")
async def check_for_birthday():
processed = list()
while True:
current_date = datetime.datetime.utcnow()
channel = bot.get_channel(604388374324838532) # '#birthdays' ID
if current_date.hour == 12 and channel:
conn = psycopg2.connect(DATABASE_URL, sslmode='require')
cur = conn.cursor()
if (not calendar.isleap(current_date.year)
and current_date.month == 2
and current_date.day == 28):
cur.execute("SELECT id FROM members "
"WHERE birthday=%s "
"OR birthday='2036-02-29'", [current_date.strftime('2036-%m-%d')])
else:
cur.execute("SELECT id FROM members "
"WHERE birthday=%s", [current_date.strftime('2036-%m-%d')])
guild = bot.get_guild(508545461939077142)
members = [m[0] for m in cur.fetchall() if guild.get_member(int(m[0])) is not None]
members = list(set(members) - set(processed))
if len(members) == 1:
await channel.send("It's <@{0}>'s <@&745891123842515024> today! 🥳\n"
"🎉🎉 Woo! 🎉🎉".format(members[0]))
elif len(members) > 1:
await channel.send("What a coincidence!\n"
"It's <@"
+ ">, <@".join(member for member in members[:-1])
+ "> and <@{0}>'s <@&745891123842515024> today!".format(members[-1])
+ " 🥳\n🎉🎉 Woo! 🎉🎉")
processed += members
await update_birthdays(cur)
cur.close()
conn.close()
else:
processed = list()
# The coroutine works once a minute
await asyncio.sleep(60)
if __name__ == "__main__":
bot.loop.create_task(check_for_birthday())
bot.run(TOKEN)