main.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. import os
  2. from dotenv import load_dotenv
  3. import discord
  4. from discord.ext import commands
  5. from discord.commands import Option
  6. from discord.commands import slash_command
  7. from datetime import datetime, time
  8. import configparser
  9. import mysql.connector
  10. import asyncio
  11. import json
  12. from flask import Flask, request, jsonify
  13. from pathlib import Path
  14. intents = discord.Intents.default()
  15. intents.message_content = True
  16. intents.members = True
  17. intents.guilds = True
  18. intents.reactions = True
  19. client = discord.Client(intents=intents)
  20. #---------------------------------#
  21. #Load .env file
  22. load_dotenv()
  23. token = os.getenv("TOKEN")
  24. if token is None:
  25. raise ValueError("TOKEN not found in .env file")
  26. debug_guilds_up = []
  27. server_token = os.getenv("SERVER").split(",")
  28. for i in range(len(server_token)):
  29. debug_guilds_up.append(int(server_token[i]))
  30. dbhost = os.getenv("HOST")
  31. if dbhost is None:
  32. raise ValueError("HOST not found in .env file")
  33. dbname = os.getenv("NAME")
  34. if dbname is None:
  35. raise ValueError("NAME not found in .env file")
  36. dbpsswd = os.getenv("PASSWORD")
  37. if dbpsswd is None:
  38. raise ValueError("PASSWORD not found in .env file")
  39. dbdb = os.getenv("DATABASE")
  40. if dbdb is None:
  41. raise ValueError("DATABASE not found in .env file")
  42. API_KEY = os.getenv("API_KEY")
  43. if API_KEY is None:
  44. raise ValueError("API_KEY not found in .env file")
  45. #---------------------------------#
  46. #ConfigParser
  47. config = configparser.RawConfigParser()
  48. configFilePath = r'config.cfg'
  49. config.read_file(open(configFilePath))
  50. label_rules = config.get('Reactionroles', 'label_rules')
  51. roles_rules = config.get('Reactionroles', 'rules_roles').split(",")
  52. roles_rules = [role.strip() for role in roles_rules if role.strip()]
  53. channel_status_log = config.get('Logs', 'status_log')
  54. channel_mod_log = config.get('Logs', 'mod_log')
  55. team_role_id = config.get('Team Roles', 'team_role_id')
  56. #---------------------------------#
  57. #Database initialization
  58. conn = mysql.connector.connect(
  59. host=dbhost,
  60. user=dbname,
  61. password=dbpsswd,
  62. charset='utf8mb4',
  63. collation='utf8mb4_unicode_ci'
  64. )
  65. cursor = conn.cursor()
  66. conn.database = dbdb
  67. cursor.execute("""
  68. CREATE TABLE IF NOT EXISTS User (
  69. userid BIGINT UNIQUE PRIMARY KEY,
  70. discordname VARCHAR(100),
  71. rolesnumber INT,
  72. Roles TEXT
  73. )
  74. """)
  75. cursor.execute("""
  76. CREATE TABLE IF NOT EXISTS Team (
  77. userid BIGINT UNIQUE PRIMARY KEY,
  78. discordname VARCHAR(100),
  79. Roles TEXT,
  80. Permissions TEXT
  81. )
  82. """)
  83. cursor.execute("""
  84. CREATE TABLE IF NOT EXISTS Warns (
  85. id INT AUTO_INCREMENT PRIMARY KEY,
  86. userid BIGINT,
  87. username VARCHAR(100),
  88. moderatorname VARCHAR(100),
  89. reason VARCHAR(250),
  90. date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  91. )
  92. """)
  93. cursor.execute("""
  94. CREATE TABLE IF NOT EXISTS Bans (
  95. id INT AUTO_INCREMENT PRIMARY KEY,
  96. userid BIGINT,
  97. username VARCHAR(100),
  98. moderatorname VARCHAR(100),
  99. reason VARCHAR(250),
  100. date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  101. )
  102. """)
  103. cursor.execute("""
  104. CREATE TABLE IF NOT EXISTS Unbans (
  105. id INT AUTO_INCREMENT PRIMARY KEY,
  106. userid BIGINT,
  107. username VARCHAR(100),
  108. moderatorname VARCHAR(100),
  109. reason VARCHAR(250),
  110. date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  111. )
  112. """
  113. )
  114. cursor.execute("""
  115. CREATE TABLE IF NOT EXISTS Kick (
  116. id INT AUTO_INCREMENT PRIMARY KEY,
  117. userid BIGINT,
  118. username VARCHAR(100),
  119. moderatorname VARCHAR(100),
  120. reason VARCHAR(250),
  121. date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  122. )
  123. """)
  124. cursor.execute("""
  125. CREATE TABLE IF NOT EXISTS Notes (
  126. id INT AUTO_INCREMENT PRIMARY KEY,
  127. userid BIGINT,
  128. username VARCHAR(100),
  129. moderatorname VARCHAR(100),
  130. information VARCHAR(250),
  131. date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  132. )
  133. """)
  134. #---------------------------------#
  135. #Initialize Bot
  136. bot = commands.Bot(
  137. command_prefix=commands.when_mentioned_or("!"),
  138. description="VicePD Bot",
  139. intents=intents,
  140. debug_guilds=debug_guilds_up if debug_guilds_up else None
  141. )
  142. #Loading Cogs
  143. def load_extensions():
  144. cogs_dir = "./cogs"
  145. if not os.path.exists(cogs_dir):
  146. print(f"Cogs directory '{cogs_dir}' not found!")
  147. return
  148. for filename in os.listdir(cogs_dir):
  149. if filename.endswith(".py"):
  150. cog_list = os.path.splitext(filename)[0]
  151. try:
  152. bot.load_extension(f"cogs.{cog_list}")
  153. print(f"Loaded cog: {cog_list}")
  154. except Exception as e:
  155. print(f"Failed to load cog {cog_list}: {e}")
  156. class Admin(commands.Cog):
  157. def __init__(self, bot):
  158. self.bot = bot
  159. #---------------------------------#
  160. #Print in Log if error occurs
  161. @bot.event
  162. async def on_application_command_error(ctx, error):
  163. print(f"[!] Error in command {ctx.command}: {error}")
  164. if ctx.guild is None:
  165. return
  166. channel = discord.utils.get(ctx.guild.channels, id=int(channel_status_log))
  167. if channel:
  168. await channel.send(f"Error occurred: {str(error)}")
  169. #---------------------------------#
  170. #Bot Online Console
  171. @bot.event
  172. async def on_ready():
  173. print("------------------------")
  174. print(f"{bot.user} is online")
  175. print("------------------------")
  176. if bot.guilds:
  177. channel = discord.utils.get(bot.guilds[0].channels, id=int(channel_status_log))
  178. if channel:
  179. await channel.send(f"{bot.user} is online")
  180. bot.add_view(PersistentRoleView()) #loading reactionrole memory
  181. print("Registrierte Slash-Commands:")
  182. command_list = "\n".join([f"- /{command.name}" for command in bot.pending_application_commands])
  183. for command in bot.pending_application_commands:
  184. print(f" - {command.name}")
  185. if channel and command_list:
  186. await channel.send(f"Registered Slash-Commands:\n{command_list}")
  187. bot.loop.create_task(update_users_periodically())
  188. #---------------------------------------------------------------------------------------#
  189. #DONT Touch anything above this line, unless you know what you are doing!#
  190. #---------------------------------------------------------------------------------------#
  191. #_________________________________#
  192. #BAN SYSTEM
  193. #---------------------------------#
  194. ##Ban
  195. @bot.slash_command(name="ban", description="Ban a user from this Server")
  196. async def ban(
  197. ctx,
  198. user: Option(discord.User, description = "Select User", required=True), # type: ignore
  199. reason: Option(str, description = "Reason for the ban", default="No reason provided") # type: ignore
  200. ):
  201. if not ctx.author.guild_permissions.ban_members:
  202. await ctx.respond("Error: You don't have the permission to ban Members!", ephemeral=True)
  203. return
  204. if user == bot.user:
  205. await ctx.respond("Error: I can't ban myself!", ephemeral=True)
  206. return
  207. if user == ctx.author:
  208. await ctx.respond("Error: You can't ban yourself!", ephemeral=True)
  209. return
  210. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  211. embed = discord.Embed(
  212. title=f"Ban of **{user.name}**",
  213. description=f"User {user.mention} has been banned from the Server",
  214. color=discord.Color.red()
  215. )
  216. time = discord.utils.format_dt(datetime.now(), "f")
  217. embed.add_field(name="Ban Date", value=time, inline=False)
  218. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  219. embed.add_field(name="Reason", value=reason, inline=False)
  220. embed.add_field(name="User ID", value=user.id)
  221. embed.set_thumbnail(url=user.display_avatar.url)
  222. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  223. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  224. embed_dm = discord.Embed(
  225. title=f"You have been banned from {ctx.guild.name}",
  226. description=f"Reason: {reason}\n\nIf you believe this was a mistake, please contact the moderators.",
  227. color=discord.Color.red()
  228. )
  229. embed_dm.add_field(name="Ban Date", value=time, inline=False)
  230. embed_dm.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  231. embed_dm.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  232. try:
  233. await user.send(embed=embed_dm)
  234. await ctx.guild.ban(user, reason=reason)
  235. await ctx.respond(f"User {user.mention} has been banned from this Server!", ephemeral=True)
  236. await channel.send(embed=embed)
  237. cursor.execute(
  238. "INSERT INTO Bans (userid, username, moderatorname, reason) VALUES (%s, %s, %s, %s)",
  239. (user.id, str(user), str(ctx.author), reason)
  240. )
  241. conn.commit()
  242. except discord.NotFound:
  243. await ctx.respond("Error: User not found.", ephemeral=True)
  244. except discord.Forbidden:
  245. await ctx.respond("Error: I don't have permission to ban this user.", ephemeral=True)
  246. except discord.HTTPException as e:
  247. await ctx.respond(f"Error: Could not ban User {user.mention}. Reason: {e}", ephemeral=True)
  248. except Exception as e:
  249. await ctx.respond(f"Unexpected error: {e}", ephemeral=True)
  250. #---------------------------------#
  251. #Unban
  252. @bot.slash_command(name="unban", description="Unban a user from this Server")
  253. async def unban(
  254. ctx,
  255. user: Option(discord.User, description = "Insert User ID", required=True), # type: ignore
  256. reason: Option(str, description = "Reason for the unbanning", default="No reason provided") # type: ignore
  257. ):
  258. if not ctx.author.guild_permissions.ban_members:
  259. await ctx.respond("Error: You don't have the permission to unban Members!", ephemeral=True)
  260. return
  261. if user == bot.user:
  262. await ctx.respond("Error: I can't unban myself!", ephemeral=True)
  263. return
  264. if user == ctx.author:
  265. await ctx.respond("Error: You can't unban yourself!", ephemeral=True)
  266. return
  267. if user in ctx.guild.members:
  268. await ctx.respond("Error: This user is not banned!", ephemeral=True)
  269. return
  270. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  271. embed = discord.Embed(
  272. title=f"Unban of **{user.name}**",
  273. description=f"User {user.mention} was unbanned from this server.",
  274. color=discord.Color.green()
  275. )
  276. time = discord.utils.format_dt(datetime.now(), "f")
  277. embed.add_field(name="Unban Date", value=time, inline=False)
  278. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  279. embed.add_field(name="Reason", value=reason, inline=False)
  280. embed.add_field(name="User ID", value=user.id)
  281. embed.set_thumbnail(url=user.display_avatar.url)
  282. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  283. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  284. try:
  285. await ctx.guild.unban(user, reason=reason)
  286. await ctx.respond(f"User {user.mention} is now unbanned!", ephemeral=True)
  287. await channel.send(embed=embed)
  288. cursor.execute(
  289. "INSERT INTO Unbans (userid, username, moderatorname, reason) VALUES (%s, %s, %s, %s)",
  290. (user.id, str(user), str(ctx.author), reason)
  291. )
  292. conn.commit()
  293. except discord.Forbidden:
  294. await ctx.respond("Error: I don't have permission to unban this user.", ephemeral=True)
  295. except discord.HTTPException as e:
  296. await ctx.respond(f"Error: Could not unban User {user.mention}. Reason: {e}", ephemeral=True)
  297. except Exception as e:
  298. await ctx.respond(f"Unexpected error: {e}", ephemeral=True)
  299. #---------------------------------#
  300. #_________________________________#
  301. #---------------------------------#
  302. #Kick
  303. @bot.slash_command(name="kick", description="Kick a user from this Server")
  304. async def kick(
  305. ctx,
  306. user: Option(discord.User, description = "Select User", required=True), # type: ignore
  307. reason: Option(str, description = "Reason for the ban", default="No reason provided") # type: ignore
  308. ):
  309. if not ctx.author.guild_permissions.kick_members:
  310. await ctx.respond("Error: You don't have the permission to kick Members!", ephemeral=True)
  311. return
  312. if user == bot.user:
  313. await ctx.respond("Error: I can't kick myself!", ephemeral=True)
  314. return
  315. if user == ctx.author:
  316. await ctx.respond("Error: You can't kick yourself!", ephemeral=True)
  317. return
  318. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  319. embed = discord.Embed(
  320. title=f"Kick of **{user.name}**",
  321. description=f"User {user.mention} has been kicked from the Server",
  322. color=discord.Color.red()
  323. )
  324. time = discord.utils.format_dt(datetime.now(), "f")
  325. embed.add_field(name="Kick Date", value=time, inline=False)
  326. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  327. embed.add_field(name="Reason", value=reason, inline=False)
  328. embed.add_field(name="User ID", value=user.id)
  329. embed.set_thumbnail(url=user.display_avatar.url)
  330. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  331. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  332. #DM to user
  333. embed_dm = discord.Embed(
  334. title=f"You have been kicked from {ctx.guild.name}",
  335. description=f"Reason: {reason}\n\nIf you believe this was a mistake, please contact the moderators.",
  336. color=discord.Color.red()
  337. )
  338. embed_dm.add_field(name="Kick Date", value=time, inline=False)
  339. embed_dm.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  340. embed_dm.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  341. try:
  342. await user.send(embed=embed_dm)
  343. await ctx.guild.kick(user, reason=reason)
  344. await ctx.respond(f"User {user.mention} has been kicked from this Server!", ephemeral=True)
  345. cursor.execute(
  346. "INSERT INTO Kick (userid, username, moderatorname, reason) VALUES (%s, %s, %s, %s)",
  347. (int(user.id), str(user), str(ctx.author), reason)
  348. )
  349. conn.commit()
  350. await channel.send(embed=embed)
  351. except discord.Forbidden:
  352. await ctx.respond("Error: I don't have permission to kick this user.", ephemeral=True)
  353. except discord.HTTPException as e:
  354. await ctx.respond(f"Error: Could not kick User {user.mention}. Reason: {e}", ephemeral=True)
  355. except Exception as e:
  356. await ctx.respond(f"Unexpected error: {e}", ephemeral=True)
  357. #---------------------------------#
  358. #---------------------------------#
  359. #Warn
  360. @bot.slash_command(name="warn", description="Warn a user from this Server")
  361. async def warn(
  362. ctx,
  363. user: Option(discord.User, required=True), # type: ignore
  364. reason: Option(str, default="No reason provided") # type: ignore
  365. ):
  366. await ctx.defer(ephemeral=True)
  367. if not ctx.author.guild_permissions.kick_members:
  368. await ctx.respond("No permission.", ephemeral=True)
  369. return
  370. if user in (bot.user, ctx.author):
  371. await ctx.respond("Invalid target.", ephemeral=True)
  372. return
  373. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  374. embed = discord.Embed(
  375. title=f"Warn of **{user.name}**",
  376. description=f"User {user.mention} has been warned.",
  377. color=discord.Color.red()
  378. )
  379. time = discord.utils.format_dt(datetime.now(), "f")
  380. embed.add_field(name="Warn Date", value=time, inline=False)
  381. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  382. embed.add_field(name="Reason", value=reason, inline=False)
  383. embed.add_field(name="User ID", value=user.id)
  384. embed.set_thumbnail(url=user.display_avatar.url)
  385. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  386. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  387. #DM to user
  388. embed_dm = discord.Embed(
  389. title=f"You have been warned on {ctx.guild.name}",
  390. description=f"Reason: {reason}\n\nIf you believe this was a mistake, please contact the moderators.",
  391. color=discord.Color.red()
  392. )
  393. embed_dm.add_field(name="Warn Date", value=time, inline=False)
  394. embed_dm.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  395. embed_dm.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  396. try:
  397. await user.send(embed=embed_dm)
  398. except discord.Forbidden:
  399. await ctx.respond("Error: I can't send a DM to this user. The user was warned without a information.", ephemeral=True)
  400. pass # User has DMs closed or blocked the bot
  401. cursor.execute(
  402. "INSERT INTO Warns (userid, username, moderatorname, reason) VALUES (%s, %s, %s, %s)",
  403. (user.id, str(user), str(ctx.author), reason)
  404. )
  405. conn.commit()
  406. await channel.send(embed=embed)
  407. await ctx.followup.send(
  408. f"User {user.mention} has been warned for: {reason}",
  409. ephemeral=True
  410. )
  411. #---------------------------------#
  412. #Note -> add informations to a user for team internal use
  413. @bot.slash_command(name="note", description="Add a note to a user for internal use")
  414. async def note(
  415. ctx,
  416. user: Option(discord.User, required=True), # type: ignore
  417. information: Option(str, required=True) # type: ignore
  418. ):
  419. await ctx.defer(ephemeral=True)
  420. team_role = ctx.guild.get_role(int(team_role_id))
  421. if not team_role in ctx.author.roles:
  422. await ctx.followup.send("You don't have permissions to execute this command", ephemeral=True)
  423. return
  424. if user in (bot.user, ctx.author):
  425. await ctx.followup.send("Invalid target.", ephemeral=True)
  426. return
  427. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  428. embed = discord.Embed(
  429. title=f"Note added to **{user.name}**",
  430. color=discord.Color.blue()
  431. )
  432. time = discord.utils.format_dt(datetime.now(), "f")
  433. embed.add_field(name="Date", value=time, inline=False)
  434. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  435. embed.add_field(name="Note", value=information, inline=False)
  436. embed.add_field(name="User ID", value=user.id)
  437. embed.set_thumbnail(url=user.display_avatar.url)
  438. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  439. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  440. #Database entry
  441. cursor.execute(
  442. "INSERT INTO Notes (userid, username, moderatorname, information) VALUES (%s, %s, %s, %s)",
  443. (user.id, str(user), str(ctx.author), information)
  444. )
  445. conn.commit()
  446. await channel.send(embed=embed)
  447. await ctx.followup.send(
  448. f"Note '{information}' has been added to {user.mention}",
  449. ephemeral=True
  450. )
  451. #---------------------------------#
  452. #Modinfo
  453. @bot.slash_command(name="modinfo", description="Shows the moderative history of a user from this Server")
  454. async def modinfo(
  455. ctx,
  456. user: Option(discord.User, required=True) # type: ignore
  457. ):
  458. await ctx.defer(ephemeral=False)
  459. if not ctx.author.guild_permissions.kick_members:
  460. await ctx.followup.send("No permission.", ephemeral=True)
  461. return
  462. embed = discord.Embed(
  463. title=f"__Moderation History for {user.name}__",
  464. color=discord.Color.orange()
  465. )
  466. # Collect all events with timestamps
  467. events = []
  468. cursor.execute("SELECT moderatorname, reason, date FROM Warns WHERE userid = %s", (user.id,))
  469. for moderatorname, reason, date in cursor.fetchall():
  470. events.append(("Warned", moderatorname, reason, date))
  471. cursor.execute("SELECT moderatorname, reason, date FROM Kick WHERE userid = %s", (user.id,))
  472. for moderatorname, reason, date in cursor.fetchall():
  473. events.append(("Kicked", moderatorname, reason, date))
  474. cursor.execute("SELECT moderatorname, reason, date FROM Bans WHERE userid = %s", (user.id,))
  475. for moderatorname, reason, date in cursor.fetchall():
  476. events.append(("Banned", moderatorname, reason, date))
  477. cursor.execute("SELECT moderatorname, reason, date FROM Unbans WHERE userid = %s", (user.id,))
  478. for moderatorname, reason, date in cursor.fetchall():
  479. events.append(("Unbanned", moderatorname, reason, date))
  480. if not events:
  481. await ctx.followup.send(f"User `{user.name}` has no moderation history.", ephemeral=True)
  482. return
  483. # Sort chronologically: oldest -> newest
  484. events.sort(key=lambda e: e[3])
  485. # Add fields in chronological order
  486. for action, moderatorname, reason, date in events:
  487. embed.add_field(
  488. name=f"{action} by {moderatorname} on {date.strftime('%Y-%m-%d %H:%M:%S')}",
  489. value=f"Reason: {reason}",
  490. inline=False
  491. )
  492. embed.set_thumbnail(url=user.display_avatar.url)
  493. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  494. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  495. await ctx.followup.send(embed=embed, ephemeral=False)
  496. #_________________________________#
  497. ## Reaction role system
  498. #---------------------------------#
  499. #reaction role verfiy
  500. class PersistentRoleView(discord.ui.View):
  501. def __init__(self):
  502. super().__init__(timeout=None)
  503. @discord.ui.button(
  504. label=label_rules,
  505. style=discord.ButtonStyle.success,
  506. emoji="✅",
  507. custom_id="persistent_view:role_verify"
  508. )
  509. async def verify_callback(self, button: discord.ui.Button, interaction: discord.Interaction):
  510. role_objs = []
  511. for role_id in roles_rules:
  512. try:
  513. role_obj = interaction.guild.get_role(int(role_id))
  514. if role_obj:
  515. role_objs.append(role_obj)
  516. except Exception:
  517. continue
  518. if not role_objs:
  519. await interaction.response.send_message("Error: No valid roles found", ephemeral=True)
  520. return
  521. removed_roles = []
  522. added_roles = []
  523. for role in role_objs:
  524. if role in interaction.user.roles:
  525. removed_roles.append(role.name)
  526. else:
  527. await interaction.user.add_roles(role)
  528. added_roles.append(role.name)
  529. msg = ""
  530. if removed_roles:
  531. msg += f"Du hast die benötigten Rollen bereits erhalten."
  532. if added_roles:
  533. msg += f"Du hast folgende Rollen erhalten: {', '.join(added_roles)}."
  534. if msg == "":
  535. msg = "Keine Rollen wurden geändert."
  536. await interaction.response.send_message(msg, ephemeral=True)
  537. #Setup the reaction role message
  538. @bot.slash_command(name="verify_message", description="Send the reactionrole message | This is for setup only!")
  539. async def setup_rr(
  540. ctx: discord.ApplicationContext,
  541. channel: discord.TextChannel,
  542. ):
  543. if not ctx.author.guild_permissions.administrator:
  544. await ctx.respond("You dont have the permissions to do that..", ephemeral=True)
  545. return
  546. json_path = Path(__file__).resolve().parent.joinpath("json_files", "verify_text.json")
  547. if not json_path.exists():
  548. await ctx.respond("The .json file is missing.")
  549. return
  550. try:
  551. with json_path.open("r", encoding="utf-8") as f:
  552. json_data = json.load(f)
  553. except json.JSONDecodeError:
  554. await ctx.respond("The .json file is not valid JSON.")
  555. return
  556. if isinstance(json_data, dict):
  557. entries = [json_data]
  558. elif isinstance(json_data, list):
  559. entries = json_data
  560. else:
  561. await ctx.respond("The .json file has an unexpected structure.")
  562. return
  563. if not entries or not isinstance(entries[0], dict):
  564. await ctx.respond("The .json file has an unexpected structure.")
  565. return
  566. if not entries:
  567. await ctx.respond("The .json file is empty.")
  568. return
  569. entry = entries[0]
  570. jstitle = entry.get("title", "Verify")
  571. jsdesc = entry.get("desc", "No description provided.")
  572. embed = discord.Embed(
  573. title=jstitle,
  574. description=jsdesc,
  575. color=discord.Color.red()
  576. )
  577. embed.set_image(url="https://i.imgur.com/FoF791J.png")
  578. try:
  579. await channel.send(embed=embed, view=PersistentRoleView())
  580. await ctx.respond(f"Message was succesfully sent in {channel.mention}!", ephemeral=True)
  581. except discord.Forbidden:
  582. await ctx.respond("I dont have permissions to write in this channel", ephemeral=True)
  583. #---------------------------------#
  584. #_________________________________#
  585. #--------------------------------#
  586. #Get all Users in Database periodically
  587. async def update_users_periodically():
  588. await bot.wait_until_ready()
  589. while not bot.is_closed():
  590. try:
  591. for guild in bot.guilds:
  592. batch_count = 0
  593. async for member in guild.fetch_members(limit=None):
  594. role_ids_string = ",".join([str(role.id) for role in member.roles])
  595. cursor.execute(
  596. """INSERT INTO User (userid, discordname, rolesnumber, roles)
  597. VALUES (%s, %s, %s, %s)
  598. ON DUPLICATE KEY UPDATE discordname=%s, rolesnumber=%s, roles=%s""",
  599. (member.id, str(member), len(member.roles), role_ids_string,
  600. str(member), len(member.roles), role_ids_string)
  601. )
  602. batch_count += 1
  603. if batch_count >= 100:
  604. conn.commit()
  605. batch_count = 0
  606. if batch_count > 0:
  607. conn.commit()
  608. if team_role_id:
  609. for guild in bot.guilds:
  610. team_role = guild.get_role(int(team_role_id))
  611. if team_role is None:
  612. continue
  613. batch_count = 0
  614. async for member in guild.fetch_members(limit=None):
  615. if team_role in member.roles:
  616. role_ids_string = ",".join([str(role.id) for role in member.roles])
  617. cursor.execute(
  618. """INSERT INTO Team (userid, discordname, Roles)
  619. VALUES (%s, %s, %s)
  620. ON DUPLICATE KEY UPDATE discordname=%s, Roles=%s""",
  621. (member.id, str(member), role_ids_string,
  622. str(member), role_ids_string)
  623. )
  624. batch_count += 1
  625. if batch_count >= 100:
  626. conn.commit()
  627. batch_count = 0
  628. if batch_count > 0:
  629. conn.commit()
  630. except Exception as e:
  631. print(f"[!] Fehler beim Update der User: {e}")
  632. await asyncio.sleep(60) # Update every minute
  633. #_________________________________#
  634. #---------------------------------#
  635. #Run function
  636. load_extensions()
  637. bot.run(token)
  638. #---------------------------------#