-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
314 lines (285 loc) · 18.1 KB
/
main.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
import re
import json
import pytz
import sqlite3
import discord
import datetime
from discord.ext import commands
from discord.commands import Option, message_command
def read_config():
with open('config.json') as f:
return json.load(f)
intents = discord.Intents.default()
intents.message_content = True
guilds = read_config()["GUILDS"]
bot = discord.Bot(intents=intents)
database = read_config()["DATABASE"]
@bot.event
async def on_ready(): # Creating the database if it doesn't exist
print(f'Logged in as {bot.user}!')
conn = sqlite3.connect(database)
cursor = conn.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS Zitate (
ID INTEGER PRIMARY KEY,
Zitat_User_Name TEXT,
Writer_User_Name TEXT,
Zitat_User_ID INTEGER,
Writer_User_ID INTEGER,
Zitat TEXT,
Context TEXT,
Date TEXT,
Message_ID INTEGER,
Rating INTEGER,
Rating_User_IDs TEXT,
Zitat_Type INTEGER
)''')
conn.commit()
conn.close()
@bot.event
async def on_raw_reaction_add(reaction): # Handling the Rating System over the Reactions
if reaction.member == bot.user:
return
if reaction.emoji.id not in [read_config()["UPVOTE_EMOJI_ID"], read_config()["DOWNVOTE_EMOJI_ID"]]:
return
# Searching if the message is in the database
conn = sqlite3.connect(database)
cursor = conn.cursor()
cursor.execute("SELECT * FROM Zitate WHERE Message_ID = ?", (reaction.message_id,))
rows = cursor.fetchall()
if len(rows) < 1:
return
row = rows[0]
channel = bot.get_channel(read_config()["QUOTES_CHANNEL"])
message = await channel.fetch_message(reaction.message_id)
embed = message.embeds[0]
await message.remove_reaction(reaction.emoji, reaction.member)
if reaction.emoji.id == read_config()["UPVOTE_EMOJI_ID"]: # Handling if the user upvotes a quote
json_obj = json.loads(row[10])
rating = int(row[9]) + 1
if reaction.member.id not in json.loads(row[10]):
json_obj.append(reaction.member.id)
cursor.execute("UPDATE Zitate SET Rating = ?, Rating_User_IDs = ? WHERE Message_ID = ?", (rating, json.dumps(json_obj), reaction.message_id))
conn.commit()
embed.fields[0].value = str(rating)
await message.edit(embed=embed)
else:
await reaction.member.send(f"Du hast bereits für dieses Zitat (https://discord.com/channels/{reaction.guild_id}/{reaction.channel_id}/{reaction.message_id}) gevotet!")
if rating >= 10: # Pinning the quote if the rating is 5 or higher
await message.pin()
elif reaction.emoji.id == read_config()["DOWNVOTE_EMOJI_ID"]: # Handling if the user downvotes a quote
json_obj = json.loads(row[10])
rating = int(row[9]) - 1
if reaction.member.id not in json.loads(row[10]):
json_obj.append(reaction.member.id)
cursor.execute("UPDATE Zitate SET Rating = ?, Rating_User_IDs = ? WHERE Message_ID = ?", (rating, json.dumps(json_obj), reaction.message_id))
conn.commit()
embed.fields[0].value = str(rating)
await message.edit(embed=embed)
else:
await reaction.member.send(f"Du hast bereits für dieses Zitat (https://discord.com/channels/{reaction.guild_id}/{reaction.channel_id}/{reaction.message_id}) gevotet!")
if rating <= -3: # Deleting the quote if the rating is -3 or lower
await message.delete()
cursor.execute("DELETE FROM Zitate WHERE Message_ID = ?", (reaction.message_id,))
conn.commit()
@bot.event
async def on_application_command_error(ctx, error): # Catching specific errors from the slash commands
if isinstance(error, commands.errors.CommandOnCooldown):
await ctx.respond(f"Bitte warte noch `{round(error.retry_after, 1)} Sekunden` und probiere es dann erneut", ephemeral=True)
elif isinstance(error, commands.errors.MissingRole):
await ctx.respond(f"Dir fehlt dazu leider die <@&{read_config()['ZITAT_PERMISSION_ROLE_ID']}> Rolle", ephemeral=True)
else:
raise error
@bot.slash_command(guild_ids=guilds) # The normal /zitat command
@commands.cooldown(1, read_config()["ZITAT_COOLDOWN"], commands.BucketType.user)
@commands.has_role(read_config()["ZITAT_PERMISSION_ROLE_ID"])
async def zitat(ctx: discord.ApplicationContext,
discord_benutzer: Option(discord.Member, "Der Discord-Benutzer, der zitiert werden soll", required=True),
zitat: Option(str, "Der Satz, der zitiert werden soll", required=True),
kontext: Option(str, "Möglicher Kontext um das Zitat zu verstehen", required=False)=""):
"""Zitiere einen bestimmten Satz von einem Discord-Benutzer"""
# Preventing problems with the length of the zitat and kontext because discords max embed description length is 4096
if len(zitat) > 1500:
await ctx.respond("Leider ist das Zitat zu lang (maximal `1500` Zeichen), bitte kürze es ein wenig", ephemeral=True)
return
if len(kontext) > 400:
await ctx.respond("Leider ist der Kontext zu lang (maximal `400` Zeichen), bitte kürze ihn ein wenig", ephemeral=True)
return
zitat_embed = discord.Embed(
color=discord.Color.random(),
description=f"## `{zitat}`\n{kontext if kontext == '' else f'({kontext})'}\n## {discord_benutzer.mention}")
zitat_embed.set_thumbnail(url=discord_benutzer.avatar.url if discord_benutzer.avatar else discord_benutzer.default_avatar.url)
zitat_embed.add_field(name="Rating", value="0", inline=False)
zitat_embed.add_field(name="Zitiert von", value=f"{ctx.author.mention}", inline=False)
zitat_embed.set_footer(icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url, text=f"{datetime.datetime.now().strftime('%d.%m.%Y | %H:%M')}")
channel = bot.get_channel(read_config()["QUOTES_CHANNEL"])
message = await channel.send(embed=zitat_embed)
await ctx.respond(f'Der Benutzer {discord_benutzer.mention} wurde mit folgendem Zitat zitiert: `{zitat}` von {ctx.author.mention}!', ephemeral=True)
await message.add_reaction(f"<:{read_config()['UPVOTE_EMOJI_NAME']}:{read_config()['UPVOTE_EMOJI_ID']}>")
await message.add_reaction(f"<:{read_config()['DOWNVOTE_EMOJI_NAME']}:{read_config()['DOWNVOTE_EMOJI_ID']}>")
conn = sqlite3.connect(database)
cursor = conn.cursor()
# Inserting the quote into the database
cursor.execute("""INSERT INTO Zitate (
Zitat_User_Name,
Writer_User_Name,
Zitat_User_ID,
Writer_User_ID,
Zitat,
Context,
Date,
Message_ID,
Rating,
Rating_User_IDs,
Zitat_Type
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(discord_benutzer.display_name,
ctx.author.display_name,
discord_benutzer.id,
ctx.author.id,
zitat,
kontext,
datetime.datetime.now().astimezone(pytz.timezone("Europe/Berlin")).strftime("%d.%m.%Y | %H:%M"),
message.id,
0,
json.dumps([]),
0
))
conn.commit()
conn.close()
@bot.slash_command(guild_ids=guilds) # The /custom_zitat command
@commands.cooldown(1, read_config()["ZITAT_COOLDOWN"], commands.BucketType.user)
@commands.has_role(read_config()["ZITAT_PERMISSION_ROLE_ID"])
async def custom_zitat(ctx: discord.ApplicationContext,
benutzer: Option(str, "Der Name, der zitiert werden soll", required=True),
zitat: Option(str, "Der Satz, der zitiert werden soll", required=True),
kontext: Option(str, "Möglicher Kontext um das Zitat zu verstehen", required=False)=""):
"""Zitiere einen bestimmten Satz von einem NICHT-Discord-Benutzer"""
# Preventing problems with the length of the zitat and kontext because discords max embed description length is 4096
if len(zitat) > 1500:
await ctx.respond("Leider ist das Zitat zu lang (maximal `1500` Zeichen), bitte kürze es ein wenig", ephemeral=True)
return
if len(kontext) > 400:
await ctx.respond("Leider ist der Kontext zu lang (maximal `400` Zeichen), bitte kürze ihn ein wenig", ephemeral=True)
return
zitat_embed = discord.Embed(
color=discord.Color.random(),
description=f"## `{zitat}`\n{kontext if kontext == '' else f'({kontext})'}\n## @{benutzer.capitalize()}")
zitat_embed.add_field(name="Rating", value="0", inline=False)
zitat_embed.add_field(name="Zitiert von", value=f"{ctx.author.mention}", inline=False)
zitat_embed.set_footer(icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url, text=f"{datetime.datetime.now().strftime('%d.%m.%Y | %H:%M')}")
channel = bot.get_channel(read_config()["QUOTES_CHANNEL"])
message = await channel.send(embed=zitat_embed)
await ctx.respond(f'**@{benutzer.capitalize()}** wurde mit folgendem Zitat zitiert: `{zitat}` von {ctx.author.mention}!', ephemeral=True)
await message.add_reaction(f"<:{read_config()['UPVOTE_EMOJI_NAME']}:{read_config()['UPVOTE_EMOJI_ID']}>")
await message.add_reaction(f"<:{read_config()['DOWNVOTE_EMOJI_NAME']}:{read_config()['DOWNVOTE_EMOJI_ID']}>")
conn = sqlite3.connect(database)
cursor = conn.cursor()
# Inserting the quote into the database
cursor.execute("""INSERT INTO Zitate (
Zitat_User_Name,
Writer_User_Name,
Zitat_User_ID,
Writer_User_ID,
Zitat,
Context,
Date,
Message_ID,
Rating,
Rating_User_IDs,
Zitat_Type
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(benutzer.capitalize(),
ctx.author.display_name,
0,
ctx.author.id,
zitat,
kontext,
datetime.datetime.now().astimezone(pytz.timezone("Europe/Berlin")).strftime("%d.%m.%Y | %H:%M"),
message.id,
0,
json.dumps([]),
1
))
conn.commit()
conn.close()
@bot.message_command(guild_ids=guilds, name="Zitieren") # The App Integration for quoting discord messages
@commands.cooldown(1, read_config()["ZITAT_COOLDOWN"], commands.BucketType.user)
@commands.has_role(read_config()["ZITAT_PERMISSION_ROLE_ID"])
async def app_zitat(ctx: discord.ApplicationContext, message):
if len(message.content) > 1500:
await ctx.respond("Leider ist die Nachricht zu lang (maximal `1500` Zeichen)", ephemeral=True)
return
zitat_embed = discord.Embed(
color=discord.Color.random(),
description=f"## `{message.content}`\n({message.jump_url})\n## {message.author.mention}")
zitat_embed.set_thumbnail(url=message.author.avatar.url if message.author.avatar else message.author.default_avatar.url)
zitat_embed.add_field(name="Rating", value="0", inline=False)
zitat_embed.add_field(name="Zitiert von", value=f"{ctx.author.mention}", inline=False)
zitat_embed.set_footer(icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url, text=f"{datetime.datetime.now().strftime('%d.%m.%Y | %H:%M')}")
channel = bot.get_channel(read_config()["QUOTES_CHANNEL"])
new_message = await channel.send(embed=zitat_embed)
await ctx.respond(f'Der Benutzer {message.author.mention} wurde mit folgendem Zitat zitiert: `{message.content}` von {ctx.author.mention}!', ephemeral=True)
await new_message.add_reaction(f"<:{read_config()['UPVOTE_EMOJI_NAME']}:{read_config()['UPVOTE_EMOJI_ID']}>")
await new_message.add_reaction(f"<:{read_config()['DOWNVOTE_EMOJI_NAME']}:{read_config()['DOWNVOTE_EMOJI_ID']}>")
conn = sqlite3.connect(database)
cursor = conn.cursor()
# Inserting the quote into the database
cursor.execute("""INSERT INTO Zitate (
Zitat_User_Name,
Writer_User_Name,
Zitat_User_ID,
Writer_User_ID,
Zitat,
Context,
Date,
Message_ID,
Rating,
Rating_User_IDs,
Zitat_Type
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(message.author.display_name,
ctx.author.display_name,
message.author.id,
ctx.author.id,
message.content,
'',
datetime.datetime.now().astimezone(pytz.timezone("Europe/Berlin")).strftime("%d.%m.%Y | %H:%M"),
new_message.id,
0,
json.dumps([]),
2
))
conn.commit()
conn.close()
@bot.slash_command(guild_ids=guilds) # The Command for the fancy information text
@discord.default_permissions(administrator=True)
async def send_info_text(ctx):
"""Sendet den Info-Text"""
allowed_mentions = discord.AllowedMentions(everyone=True, users=True, replied_user=True)
info_embed = discord.Embed(
color=discord.Color.from_rgb(209, 170, 212),
description=f"""
# Überarbeitung des Zitat-Systems
\n### Das Zitatsystem wurde überarbeitet. Ab sofort können Nachrichten nur noch per Bot in den Zitatchannel gesendet werden.
\n## Warum?
\nUm Zitate übersichtlicher zu gestalten und das gesamte System zu vereinheitlichen. Außerdem hatten einige von euch Schwierigkeiten, die richtigen Zeichen zu finden oder das richtige Format zu benutzen.
\n## Wie erstelle ich jetzt ein Zitat?
\nZitate kannst du jetzt per Slash-Command erstellen. Nutze einfach `/zitat`, wähle den Nutzer aus, den du zitieren möchtest, und gib dann das Zitat ein. Anschließend kannst du entscheiden, ob du auch noch einen Kontext hinzufügen möchtest, der möglicherweise hilft, das Zitat besser zu verstehen. Zitate werden ab jetzt in einer Datenbank gespeichert und können somit auch schnell in einen anderen Kanal oder auf andere Plattformen übertragen werden. Zusätzlich gibt es den `/custom_zitat` Befehl, der genutzt wird, wenn die Person, die du zitieren möchtest, kein Discord-Benutzer ist oder sich nicht auf dem Server befindet. Dort trägst du dann den Namen in das Namensfeld ein, und die Namen werden automatisch groß- oder kleingeschrieben. Zum Beispiel wird aus "beRnd" "Bernd". Wenn du Discord-Nachrichten direkt in den Zitate-Channel stellen möchtest, kannst du dies einfach über einen `Rechtsklick -> Apps -> Zitieren` tun. Für das Zitieren gibt es einen Cooldown von `60 Sekunden`, aber der Bot informiert dich auch, wenn du noch warten musst.
\n## Was ist das Rating?
\nDas Rating ist ein neues System zur Bewertung von Zitaten. Dazu kannst du einfach die Pfeile unter einem Zitat verwenden, um das Zitat zu bewerten. Steigt der Score auf `10` oder höher, wird das Zitat angepinnt und ist leichter zu finden. Dadurch können die besten Zitate immer schnell und direkt gefunden werden. Fällt der Score eines Zitats auf oder unter `-3`, wird es sofort gelöscht und aus der Datenbank entfernt. Du kannst ein Zitat nur **einmal** positiv oder negativ bewerten, und die Bewertung kann **nicht** rückgängig gemacht werden. Die Bewertung ist anonym, es sei denn, jemand sieht genau in diesem Moment die Nachricht; deine Reaktion wird dann anschließend gelöscht.
\n## Warum kann ich keine Unterhaltungen zitieren?
\nDas Zitieren von Unterhaltungen ist aufgrund von Discord-spezifischen Einschränkungen mit dem neuen Format nicht mehr möglich. Daran kann ich leider auch nichts ändern. :c
\n## Was passiert mit den bestehenden Zitaten?
\nAlle bereits vorhandenen Zitate bleiben weiterhin im Channel erhalten; es handelt sich also nur um einen Formatwechsel. Allerdings werden alle bereits vorhandenen Zitate **nicht** in die Datenbank übernommen, d.h., sie gehen verloren, wenn sie auf eine andere Plattform übertragen werden. Dies liegt hauptsächlich an allen, die das Zitatformat nicht richtig eingehalten haben.
\n## Ich kann die Commands nicht benutzen?
\nStelle sicher das du die <@&{read_config()["ZITAT_PERMISSION_ROLE_ID"]}> Rolle hast. Falls du sie nicht haben solltest, frag bitte einen der Admins ob er sie dir wieder gibt.
\n## Mir gefällt Feature XY an dem Bot nicht?
\nDu kannst gerne <@539142329546571806> kontaktieren und deine Idee einbringen. Alternativ kannst du auch gerne einen Pull-Request auf GitHub erstellen. \n[Link zum Repository](https://github.com/forest-cat/zitate).
\n_ _
""")
info_embed.set_footer(icon_url=bot.user.avatar.url, text=f"{bot.user.display_name} am {datetime.datetime.now().strftime('%d.%m.%Y um %H:%M')}")
info_msg = await ctx.send(embed=info_embed, allowed_mentions=allowed_mentions)
await ctx.respond(f"Der Text wurde in {ctx.channel.mention} gesendet!", ephemeral=True)
await info_msg.pin()
# running the actual bot
bot.run(read_config()["TOKEN"])