main.py 26 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 Rules', 'label_rules')
  51. roles_rules = config.get('Reactionroles Rules', '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.Forbidden:
  243. await ctx.respond("Error: I don't have permission to ban this user.", ephemeral=True)
  244. except discord.HTTPException as e:
  245. await ctx.respond(f"Error: Could not ban User {user.mention}. Reason: {e}", ephemeral=True)
  246. except Exception as e:
  247. await ctx.respond(f"Unexpected error: {e}", ephemeral=True)
  248. #---------------------------------#
  249. #Unban
  250. @bot.slash_command(name="unban", description="Unban a user from this Server")
  251. async def unban(
  252. ctx,
  253. user: Option(discord.User, description = "Insert User ID", required=True), # type: ignore
  254. reason: Option(str, description = "Reason for the unbanning", default="No reason provided") # type: ignore
  255. ):
  256. if not ctx.author.guild_permissions.ban_members:
  257. await ctx.respond("Error: You don't have the permission to unban Members!", ephemeral=True)
  258. return
  259. if user == bot.user:
  260. await ctx.respond("Error: I can't unban myself!", ephemeral=True)
  261. return
  262. if user == ctx.author:
  263. await ctx.respond("Error: You can't unban yourself!", ephemeral=True)
  264. return
  265. if user in ctx.guild.members:
  266. await ctx.respond("Error: This user is not banned!", ephemeral=True)
  267. return
  268. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  269. embed = discord.Embed(
  270. title=f"Unban of **{user.name}**",
  271. description=f"User {user.mention} was unbanned from this server.",
  272. color=discord.Color.green()
  273. )
  274. time = discord.utils.format_dt(datetime.now(), "f")
  275. embed.add_field(name="Unban Date", value=time, inline=False)
  276. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  277. embed.add_field(name="Reason", value=reason, inline=False)
  278. embed.add_field(name="User ID", value=user.id)
  279. embed.set_thumbnail(url=user.display_avatar.url)
  280. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  281. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  282. try:
  283. await ctx.guild.unban(user, reason=reason)
  284. await ctx.respond(f"User {user.mention} is now unbanned!", ephemeral=True)
  285. await channel.send(embed=embed)
  286. cursor.execute(
  287. "INSERT INTO Unbans (userid, username, moderatorname, reason) VALUES (%s, %s, %s, %s)",
  288. (user.id, str(user), str(ctx.author), reason)
  289. )
  290. conn.commit()
  291. except discord.Forbidden:
  292. await ctx.respond("Error: I don't have permission to unban this user.", ephemeral=True)
  293. except discord.HTTPException as e:
  294. await ctx.respond(f"Error: Could not unban User {user.mention}. Reason: {e}", ephemeral=True)
  295. except Exception as e:
  296. await ctx.respond(f"Unexpected error: {e}", ephemeral=True)
  297. #---------------------------------#
  298. #_________________________________#
  299. #---------------------------------#
  300. #Kick
  301. @bot.slash_command(name="kick", description="Kick a user from this Server")
  302. async def kick(
  303. ctx,
  304. user: Option(discord.User, description = "Select User", required=True), # type: ignore
  305. reason: Option(str, description = "Reason for the ban", default="No reason provided") # type: ignore
  306. ):
  307. if not ctx.author.guild_permissions.kick_members:
  308. await ctx.respond("Error: You don't have the permission to kick Members!", ephemeral=True)
  309. return
  310. if user == bot.user:
  311. await ctx.respond("Error: I can't kick myself!", ephemeral=True)
  312. return
  313. if user == ctx.author:
  314. await ctx.respond("Error: You can't kick yourself!", ephemeral=True)
  315. return
  316. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  317. embed = discord.Embed(
  318. title=f"Kick of **{user.name}**",
  319. description=f"User {user.mention} has been kicked from the Server",
  320. color=discord.Color.red()
  321. )
  322. time = discord.utils.format_dt(datetime.now(), "f")
  323. embed.add_field(name="Kick Date", value=time, inline=False)
  324. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  325. embed.add_field(name="Reason", value=reason, inline=False)
  326. embed.add_field(name="User ID", value=user.id)
  327. embed.set_thumbnail(url=user.display_avatar.url)
  328. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  329. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  330. #DM to user
  331. embed_dm = discord.Embed(
  332. title=f"You have been kicked from {ctx.guild.name}",
  333. description=f"Reason: {reason}\n\nIf you believe this was a mistake, please contact the moderators.",
  334. color=discord.Color.red()
  335. )
  336. embed_dm.add_field(name="Kick Date", value=time, inline=False)
  337. embed_dm.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  338. embed_dm.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  339. try:
  340. await user.send(embed=embed_dm)
  341. await ctx.guild.kick(user, reason=reason)
  342. await ctx.respond(f"User {user.mention} has been kicked from this Server!", ephemeral=True)
  343. cursor.execute(
  344. "INSERT INTO Kick (userid, username, moderatorname, reason) VALUES (%s, %s, %s, %s)",
  345. (int(user.id), str(user), str(ctx.author), reason)
  346. )
  347. conn.commit()
  348. await channel.send(embed=embed)
  349. except discord.Forbidden:
  350. await ctx.respond("Error: I don't have permission to kick this user.", ephemeral=True)
  351. except discord.HTTPException as e:
  352. await ctx.respond(f"Error: Could not kick User {user.mention}. Reason: {e}", ephemeral=True)
  353. except Exception as e:
  354. await ctx.respond(f"Unexpected error: {e}", ephemeral=True)
  355. #---------------------------------#
  356. #---------------------------------#
  357. #Warn
  358. @bot.slash_command(name="warn", description="Warn a user from this Server")
  359. async def warn(
  360. ctx,
  361. user: Option(discord.User, required=True), # type: ignore
  362. reason: Option(str, default="No reason provided") # type: ignore
  363. ):
  364. await ctx.defer(ephemeral=True)
  365. if not ctx.author.guild_permissions.kick_members:
  366. await ctx.respond("No permission.", ephemeral=True)
  367. return
  368. if user in (bot.user, ctx.author):
  369. await ctx.respond("Invalid target.", ephemeral=True)
  370. return
  371. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  372. embed = discord.Embed(
  373. title=f"Warn of **{user.name}**",
  374. description=f"User {user.mention} has been warned.",
  375. color=discord.Color.red()
  376. )
  377. time = discord.utils.format_dt(datetime.now(), "f")
  378. embed.add_field(name="Warn Date", value=time, inline=False)
  379. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  380. embed.add_field(name="Reason", value=reason, inline=False)
  381. embed.add_field(name="User ID", value=user.id)
  382. embed.set_thumbnail(url=user.display_avatar.url)
  383. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  384. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  385. #DM to user
  386. embed_dm = discord.Embed(
  387. title=f"You have been warned on {ctx.guild.name}",
  388. description=f"Reason: {reason}\n\nIf you believe this was a mistake, please contact the moderators.",
  389. color=discord.Color.red()
  390. )
  391. embed_dm.add_field(name="Warn Date", value=time, inline=False)
  392. embed_dm.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  393. embed_dm.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  394. try:
  395. await user.send(embed=embed_dm)
  396. except discord.Forbidden:
  397. await ctx.respond("Error: I can't send a DM to this user. The user was warned without a information.", ephemeral=True)
  398. pass # User has DMs closed or blocked the bot
  399. cursor.execute(
  400. "INSERT INTO Warns (userid, username, moderatorname, reason) VALUES (%s, %s, %s, %s)",
  401. (user.id, str(user), str(ctx.author), reason)
  402. )
  403. conn.commit()
  404. await channel.send(embed=embed)
  405. await ctx.followup.send(
  406. f"User {user.mention} has been warned for: {reason}",
  407. ephemeral=True
  408. )
  409. #---------------------------------#
  410. #Note -> add informations to a user for team internal use
  411. @bot.slash_command(name="note", description="Add a note to a user for internal use")
  412. async def note(
  413. ctx,
  414. user: Option(discord.User, required=True), # type: ignore
  415. information: Option(str, required=True) # type: ignore
  416. ):
  417. await ctx.defer(ephemeral=True)
  418. team_role = ctx.guild.get_role(int(team_role_id))
  419. if not team_role in ctx.author.roles:
  420. await ctx.followup.send("You don't have permissions to execute this command", ephemeral=True)
  421. return
  422. if user in (bot.user, ctx.author):
  423. await ctx.followup.send("Invalid target.", ephemeral=True)
  424. return
  425. channel= discord.utils.get(ctx.guild.channels, id = int(channel_mod_log))
  426. embed = discord.Embed(
  427. title=f"Note added to **{user.name}**",
  428. color=discord.Color.blue()
  429. )
  430. time = discord.utils.format_dt(datetime.now(), "f")
  431. embed.add_field(name="Date", value=time, inline=False)
  432. embed.add_field(name="Moderator", value=f"{ctx.author}", inline=False)
  433. embed.add_field(name="Note", value=information, inline=False)
  434. embed.add_field(name="User ID", value=user.id)
  435. embed.set_thumbnail(url=user.display_avatar.url)
  436. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  437. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  438. #Database entry
  439. cursor.execute(
  440. "INSERT INTO Notes (userid, username, moderatorname, information) VALUES (%s, %s, %s, %s)",
  441. (user.id, str(user), str(ctx.author), information)
  442. )
  443. conn.commit()
  444. await channel.send(embed=embed)
  445. await ctx.followup.send(
  446. f"Note '{information}' has been added to {user.mention}",
  447. ephemeral=True
  448. )
  449. #---------------------------------#
  450. #Modinfo
  451. @bot.slash_command(name="modinfo", description="Shows the moderative history of a user from this Server")
  452. async def modinfo(
  453. ctx,
  454. user: Option(discord.User, required=True) # type: ignore
  455. ):
  456. await ctx.defer(ephemeral=False)
  457. if not ctx.author.guild_permissions.kick_members:
  458. await ctx.followup.send("No permission.", ephemeral=True)
  459. return
  460. embed = discord.Embed(
  461. title=f"__Moderation History for {user.name}__",
  462. color=discord.Color.orange()
  463. )
  464. # Collect all events with timestamps
  465. events = []
  466. cursor.execute("SELECT moderatorname, reason, date FROM Warns WHERE userid = %s", (user.id,))
  467. for moderatorname, reason, date in cursor.fetchall():
  468. events.append(("Warned", moderatorname, reason, date))
  469. cursor.execute("SELECT moderatorname, reason, date FROM Kick WHERE userid = %s", (user.id,))
  470. for moderatorname, reason, date in cursor.fetchall():
  471. events.append(("Kicked", moderatorname, reason, date))
  472. cursor.execute("SELECT moderatorname, reason, date FROM Bans WHERE userid = %s", (user.id,))
  473. for moderatorname, reason, date in cursor.fetchall():
  474. events.append(("Banned", moderatorname, reason, date))
  475. cursor.execute("SELECT moderatorname, reason, date FROM Unbans WHERE userid = %s", (user.id,))
  476. for moderatorname, reason, date in cursor.fetchall():
  477. events.append(("Unbanned", moderatorname, reason, date))
  478. if not events:
  479. await ctx.followup.send(f"User `{user.name}` has no moderation history.", ephemeral=True)
  480. return
  481. # Sort chronologically: oldest -> newest
  482. events.sort(key=lambda e: e[3])
  483. # Add fields in chronological order
  484. for action, moderatorname, reason, date in events:
  485. embed.add_field(
  486. name=f"{action} by {moderatorname} on {date.strftime('%Y-%m-%d %H:%M:%S')}",
  487. value=f"Reason: {reason}",
  488. inline=False
  489. )
  490. embed.set_thumbnail(url=user.display_avatar.url)
  491. embed.set_author(name="VicePD", icon_url="https://i.imgur.com/6QteFrg.png")
  492. embed.set_footer(text="VicePD - Bot | Made by BaumSplitter41")
  493. await ctx.followup.send(embed=embed, ephemeral=False)
  494. #_________________________________#
  495. ## Reaction role system
  496. #---------------------------------#
  497. #reaction role verfiy
  498. class PersistentRoleView(discord.ui.View):
  499. def __init__(self):
  500. super().__init__(timeout=None)
  501. @discord.ui.button(
  502. label=label_rules,
  503. style=discord.ButtonStyle.success,
  504. emoji="✅",
  505. custom_id="persistent_view:role_verify"
  506. )
  507. async def verify_callback(self, button: discord.ui.Button, interaction: discord.Interaction):
  508. role_objs = []
  509. for role_id in roles_rules:
  510. try:
  511. role_obj = interaction.guild.get_role(int(role_id))
  512. if role_obj:
  513. role_objs.append(role_obj)
  514. except Exception:
  515. continue
  516. if not role_objs:
  517. await interaction.response.send_message("Error: No valid roles found", ephemeral=True)
  518. return
  519. removed_roles = []
  520. added_roles = []
  521. for role in role_objs:
  522. if role in interaction.user.roles:
  523. removed_roles.append(role.name)
  524. else:
  525. await interaction.user.add_roles(role)
  526. added_roles.append(role.name)
  527. msg = ""
  528. if removed_roles:
  529. msg += f"Du hast die benötigten Rollen bereits erhalten."
  530. if added_roles:
  531. msg += f"Du hast folgende Rollen erhalten: {', '.join(added_roles)}."
  532. if msg == "":
  533. msg = "Keine Rollen wurden geändert."
  534. await interaction.response.send_message(msg, ephemeral=True)
  535. #Setup the reaction role message
  536. @bot.slash_command(name="verify_message", description="Send the reactionrole message | This is for setup only!")
  537. async def setup_rr(
  538. ctx: discord.ApplicationContext,
  539. channel: discord.TextChannel,
  540. ):
  541. if not ctx.author.guild_permissions.administrator:
  542. await ctx.respond("You dont have the permissions to do that..", ephemeral=True)
  543. return
  544. json_path = Path(__file__).resolve().parent.joinpath("json_files", "verify_text.json")
  545. if not json_path.exists():
  546. await ctx.respond("The .json file is missing.")
  547. return
  548. try:
  549. with json_path.open("r", encoding="utf-8") as f:
  550. json_data = json.load(f)
  551. except json.JSONDecodeError:
  552. await ctx.respond("The .json file is not valid JSON.")
  553. return
  554. if isinstance(json_data, dict):
  555. entries = [json_data]
  556. elif isinstance(json_data, list):
  557. entries = json_data
  558. else:
  559. await ctx.respond("The .json file has an unexpected structure.")
  560. return
  561. if not entries or not isinstance(entries[0], dict):
  562. await ctx.respond("The .json file has an unexpected structure.")
  563. return
  564. if not entries:
  565. await ctx.respond("The .json file is empty.")
  566. return
  567. entry = entries[0]
  568. jstitle = entry.get("title", "Verify")
  569. jsdesc = entry.get("desc", "No description provided.")
  570. embed = discord.Embed(
  571. title=jstitle,
  572. description=jsdesc,
  573. color=discord.Color.red()
  574. )
  575. embed.set_image(url="https://i.imgur.com/FoF791J.png")
  576. try:
  577. await channel.send(embed=embed, view=PersistentRoleView())
  578. await ctx.respond(f"Message was succesfully sent in {channel.mention}!", ephemeral=True)
  579. except discord.Forbidden:
  580. await ctx.respond("I dont have permissions to write in this channel", ephemeral=True)
  581. #---------------------------------#
  582. #_________________________________#
  583. #--------------------------------#
  584. #Get all Users in Database periodically
  585. async def update_users_periodically():
  586. await bot.wait_until_ready()
  587. while not bot.is_closed():
  588. try:
  589. for guild in bot.guilds:
  590. batch_count = 0
  591. async for member in guild.fetch_members(limit=None):
  592. role_ids_string = ",".join([str(role.id) for role in member.roles])
  593. cursor.execute(
  594. """INSERT INTO User (userid, discordname, rolesnumber, roles)
  595. VALUES (%s, %s, %s, %s)
  596. ON DUPLICATE KEY UPDATE discordname=%s, rolesnumber=%s, roles=%s""",
  597. (member.id, str(member), len(member.roles), role_ids_string,
  598. str(member), len(member.roles), role_ids_string)
  599. )
  600. batch_count += 1
  601. if batch_count >= 100:
  602. conn.commit()
  603. batch_count = 0
  604. if batch_count > 0:
  605. conn.commit()
  606. if team_role_id:
  607. for guild in bot.guilds:
  608. team_role = guild.get_role(int(team_role_id))
  609. if team_role is None:
  610. continue
  611. batch_count = 0
  612. async for member in guild.fetch_members(limit=None):
  613. if team_role in member.roles:
  614. role_ids_string = ",".join([str(role.id) for role in member.roles])
  615. cursor.execute(
  616. """INSERT INTO Team (userid, discordname, Roles)
  617. VALUES (%s, %s, %s)
  618. ON DUPLICATE KEY UPDATE discordname=%s, Roles=%s""",
  619. (member.id, str(member), role_ids_string,
  620. str(member), role_ids_string)
  621. )
  622. batch_count += 1
  623. if batch_count >= 100:
  624. conn.commit()
  625. batch_count = 0
  626. if batch_count > 0:
  627. conn.commit()
  628. except Exception as e:
  629. print(f"[!] Fehler beim Update der User: {e}")
  630. await asyncio.sleep(60) # Update every minute
  631. #_________________________________#
  632. #---------------------------------#
  633. #Run function
  634. load_extensions()
  635. bot.run(token)
  636. #---------------------------------#