first commit
This commit is contained in:
178
bot/cogs/commands/Birthday.py
Normal file
178
bot/cogs/commands/Birthday.py
Normal file
@@ -0,0 +1,178 @@
|
||||
import discord
|
||||
from discord.ext import commands, tasks
|
||||
import json
|
||||
import datetime
|
||||
import asyncio
|
||||
import os
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
class CV2(LayoutView):
|
||||
def __init__(self, title, *sections):
|
||||
super().__init__(timeout=None)
|
||||
items = [TextDisplay(f"**{title}**")]
|
||||
for s in sections:
|
||||
if s:
|
||||
items.append(Separator(visible=True))
|
||||
items.append(TextDisplay(str(s)))
|
||||
self.add_item(build_container(*items))
|
||||
|
||||
def read_db(filename):
|
||||
"""Read the JSON database file."""
|
||||
if os.path.exists(filename):
|
||||
with open(filename, 'r') as f:
|
||||
return json.load(f)
|
||||
return {}
|
||||
|
||||
def write_db(filename, data):
|
||||
"""Write data to the JSON database file."""
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
class Birthdays(commands.Cog):
|
||||
"""Handle birthday notifications and setup."""
|
||||
|
||||
def __init__(self, client: commands.Bot):
|
||||
self.client = client
|
||||
self.check_birthdays.start()
|
||||
|
||||
@commands.command(
|
||||
name="birthdaysetup",
|
||||
help="Set up the birthday log channel and role.")
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.guild_only()
|
||||
async def birthday_setup(self, ctx: commands.Context, channel: discord.TextChannel, role: discord.Role):
|
||||
db = read_db('jsondb/birthday_logs.json')
|
||||
guild_id = str(ctx.guild.id)
|
||||
|
||||
if guild_id not in db:
|
||||
db[guild_id] = {"birthday_channel_id": channel.id, "birthday_role_id": role.id}
|
||||
else:
|
||||
db[guild_id]["birthday_channel_id"] = channel.id
|
||||
db[guild_id]["birthday_role_id"] = role.id
|
||||
|
||||
write_db('jsondb/birthday_logs.json', db)
|
||||
|
||||
await ctx.send(view=CV2("Birthday Setup", f"Birthday log channel set to {channel.mention} and birthday role set to {role.mention}."))
|
||||
|
||||
@commands.command(
|
||||
name="setbirthday",
|
||||
help="Set your birthday.")
|
||||
@commands.guild_only()
|
||||
async def set_birthday(self, ctx: commands.Context):
|
||||
def check(msg):
|
||||
return msg.author.id == ctx.author.id and msg.channel.id == ctx.channel.id
|
||||
|
||||
await ctx.send(view=CV2("Birthday Setup", "Please enter your birth day (DD):"))
|
||||
|
||||
try:
|
||||
msg = await self.client.wait_for('message', timeout=60.0, check=check)
|
||||
day = msg.content.strip().zfill(2)
|
||||
|
||||
if not day.isdigit() or int(day) not in range(1, 32):
|
||||
await ctx.send(view=CV2("Error", "Invalid day entered. Please enter a number between 01 and 31."))
|
||||
return
|
||||
|
||||
await ctx.send(view=CV2("Birthday Setup", "Please enter your birth month (MM):"))
|
||||
msg = await self.client.wait_for('message', timeout=60.0, check=check)
|
||||
month = msg.content.strip().zfill(2)
|
||||
|
||||
if not month.isdigit() or int(month) not in range(1, 13):
|
||||
await ctx.send(view=CV2("Error", "Invalid month entered. Please enter a number between 01 and 12."))
|
||||
return
|
||||
|
||||
await ctx.send(view=CV2("Birthday Setup", "Please enter your birth year (YYYY):"))
|
||||
msg = await self.client.wait_for('message', timeout=60.0, check=check)
|
||||
year = msg.content.strip()
|
||||
|
||||
if not year.isdigit() or len(year) != 4:
|
||||
await ctx.send(view=CV2("Error", "Invalid year entered. Please enter a valid year in YYYY format."))
|
||||
return
|
||||
|
||||
date = f"{month}-{day}-{year}"
|
||||
db = read_db('jsondb/birthdays.json')
|
||||
db[str(ctx.author.id)] = date
|
||||
write_db('jsondb/birthdays.json', db)
|
||||
|
||||
await ctx.send(view=CV2("Success", f"Your birthday has been set to {date}."))
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.send(view=CV2("Error", "You took too long to respond. Please try again."))
|
||||
|
||||
@commands.command(
|
||||
name="removebirthday",
|
||||
help="Remove your birthday.")
|
||||
@commands.guild_only()
|
||||
async def remove_birthday(self, ctx: commands.Context):
|
||||
db = read_db('jsondb/birthdays.json')
|
||||
|
||||
if str(ctx.author.id) in db:
|
||||
del db[str(ctx.author.id)]
|
||||
write_db('jsondb/birthdays.json', db)
|
||||
await ctx.send(view=CV2("Success", "Your birthday has been removed."))
|
||||
else:
|
||||
await ctx.send(view=CV2("Error", "You have no birthday set."))
|
||||
|
||||
@commands.command(
|
||||
name="listbirthdays",
|
||||
help="List all members who have their birthday today.")
|
||||
@commands.guild_only()
|
||||
async def list_birthdays(self, ctx: commands.Context):
|
||||
now = datetime.datetime.now()
|
||||
today_date = now.strftime("%m-%d")
|
||||
db = read_db('jsondb/birthdays.json')
|
||||
|
||||
members_with_birthday = [ctx.guild.get_member(int(user_id)) for user_id, date in db.items() if date.startswith(today_date)]
|
||||
|
||||
if members_with_birthday:
|
||||
mentions = ', '.join(member.mention for member in members_with_birthday if member)
|
||||
await ctx.send(view=CV2("Birthdays Today", f"Members with birthdays today: {mentions}"))
|
||||
else:
|
||||
await ctx.send(view=CV2("Birthdays", "No birthdays today."))
|
||||
|
||||
@commands.command(
|
||||
name="birthday",
|
||||
help="Check your birthday.")
|
||||
@commands.guild_only()
|
||||
async def check_birthday(self, ctx: commands.Context):
|
||||
db = read_db('jsondb/birthdays.json')
|
||||
|
||||
if str(ctx.author.id) in db:
|
||||
date = db[str(ctx.author.id)]
|
||||
await ctx.send(view=CV2("Your Birthday", f"Your birthday is set to {date}."))
|
||||
else:
|
||||
await ctx.send(view=CV2("Your Birthday", "You haven't set your birthday."))
|
||||
|
||||
@tasks.loop(hours=24)
|
||||
async def check_birthdays(self):
|
||||
now = datetime.datetime.now()
|
||||
today_date = now.strftime("%m-%d")
|
||||
db = read_db('jsondb/birthdays.json')
|
||||
guild_settings = read_db('jsondb/birthday_logs.json')
|
||||
|
||||
for user_id, birthday in db.items():
|
||||
if birthday.startswith(today_date):
|
||||
user = self.client.get_user(int(user_id))
|
||||
if user:
|
||||
for guild_id, settings in guild_settings.items():
|
||||
channel_id = settings.get("birthday_channel_id")
|
||||
role_id = settings.get("birthday_role_id")
|
||||
if channel_id:
|
||||
channel = self.client.get_channel(channel_id)
|
||||
if channel:
|
||||
await channel.send(view=CV2("Happy Birthday! 🎉", f"Wishing {user.mention} a fantastic birthday!"))
|
||||
role = discord.utils.get(channel.guild.roles, id=role_id)
|
||||
if role:
|
||||
await user.add_roles(role)
|
||||
break
|
||||
|
||||
@check_birthdays.before_loop
|
||||
async def before_check_birthdays(self):
|
||||
await self.client.wait_until_ready()
|
||||
now = datetime.datetime.now()
|
||||
first_run = datetime.datetime.combine(now.date(), datetime.time(hour=0, minute=0))
|
||||
if now > first_run:
|
||||
first_run += datetime.timedelta(days=1)
|
||||
await asyncio.sleep((first_run - now).total_seconds())
|
||||
|
||||
async def setup(client: commands.Bot):
|
||||
await client.add_cog(Birthdays(client))
|
||||
252
bot/cogs/commands/Embed.py
Normal file
252
bot/cogs/commands/Embed.py
Normal file
@@ -0,0 +1,252 @@
|
||||
import os
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK
|
||||
from discord.ext import commands
|
||||
from discord import ui
|
||||
import asyncio
|
||||
from utils.Tools import *
|
||||
import re
|
||||
|
||||
|
||||
class EmbedBuilder(ui.LayoutView):
|
||||
def __init__(self, ctx):
|
||||
super().__init__(timeout=180)
|
||||
self.ctx = ctx
|
||||
self.message = None
|
||||
self.embed_data = {
|
||||
"title": "Edit your Embed!",
|
||||
"description": "Select Options from the menu below to customize.",
|
||||
"color": 0xFF0000,
|
||||
"thumbnail": None,
|
||||
"image": None,
|
||||
"footer_text": None,
|
||||
"footer_icon": None,
|
||||
"author_text": None,
|
||||
"author_icon": None,
|
||||
"fields": []
|
||||
}
|
||||
self.container = ui.Container(accent_color=None)
|
||||
self._build_view()
|
||||
self.add_item(self.container)
|
||||
|
||||
def _get_preview(self):
|
||||
d = self.embed_data
|
||||
lines = []
|
||||
if d["title"]:
|
||||
lines.append(f"**Title:** {d['title']}")
|
||||
if d["description"]:
|
||||
lines.append(f"**Description:** {d['description']}")
|
||||
if d["color"]:
|
||||
lines.append(f"**Color:** `#{d['color']:06X}`")
|
||||
if d["thumbnail"]:
|
||||
lines.append(f"**Thumbnail:** [Set]({d['thumbnail']})")
|
||||
if d["image"]:
|
||||
lines.append(f"**Image:** [Set]({d['image']})")
|
||||
if d["footer_text"]:
|
||||
lines.append(f"**Footer:** {d['footer_text']}")
|
||||
if d["footer_icon"]:
|
||||
lines.append(f"**Footer Icon:** [Set]({d['footer_icon']})")
|
||||
if d["author_text"]:
|
||||
lines.append(f"**Author:** {d['author_text']}")
|
||||
if d["author_icon"]:
|
||||
lines.append(f"**Author Icon:** [Set]({d['author_icon']})")
|
||||
if d["fields"]:
|
||||
for i, f in enumerate(d["fields"]):
|
||||
lines.append(f"**Field {i+1}:** {f['name']} — {f['value']}")
|
||||
return "\n".join(lines) if lines else "No properties set yet."
|
||||
|
||||
def _build_view(self):
|
||||
self.container.clear_items()
|
||||
|
||||
self.container.add_item(ui.TextDisplay("# Embed Builder"))
|
||||
self.container.add_item(ui.Separator())
|
||||
self.container.add_item(ui.TextDisplay(self._get_preview()))
|
||||
self.container.add_item(ui.Separator())
|
||||
self.container.add_item(ui.TextDisplay("*Select an option to edit. Respond within 30 seconds.*"))
|
||||
|
||||
# Select menu
|
||||
select = ui.Select(
|
||||
placeholder="Choose an option to edit the Embed",
|
||||
min_values=1, max_values=1,
|
||||
options=[
|
||||
discord.SelectOption(label="Title", description="Edit the title"),
|
||||
discord.SelectOption(label="Description", description="Edit the description"),
|
||||
discord.SelectOption(label="Add Field", description="Add a field"),
|
||||
discord.SelectOption(label="Color", description="Edit the color (hex)"),
|
||||
discord.SelectOption(label="Thumbnail", description="Set thumbnail URL"),
|
||||
discord.SelectOption(label="Image", description="Set image URL"),
|
||||
discord.SelectOption(label="Footer Text", description="Edit footer text"),
|
||||
discord.SelectOption(label="Footer Icon", description="Set footer icon URL"),
|
||||
discord.SelectOption(label="Author Text", description="Edit author text"),
|
||||
discord.SelectOption(label="Author Icon", description="Set author icon URL"),
|
||||
]
|
||||
)
|
||||
select.callback = self._select_callback
|
||||
self.container.add_item(ui.ActionRow(select))
|
||||
|
||||
# Buttons
|
||||
send_btn = ui.Button(label="Send Embed", emoji=TICK, style=discord.ButtonStyle.success)
|
||||
send_btn.callback = self._send_callback
|
||||
cancel_btn = ui.Button(label="Cancel Setup", emoji=CROSS, style=discord.ButtonStyle.danger)
|
||||
cancel_btn.callback = self._cancel_callback
|
||||
self.container.add_item(ui.ActionRow(send_btn, cancel_btn))
|
||||
|
||||
def _build_embed(self):
|
||||
"""Build a real discord.Embed from stored data"""
|
||||
d = self.embed_data
|
||||
embed = discord.Embed(
|
||||
title=d["title"],
|
||||
description=d["description"],
|
||||
color=d["color"]
|
||||
)
|
||||
if d["thumbnail"]:
|
||||
embed.set_thumbnail(url=d["thumbnail"])
|
||||
if d["image"]:
|
||||
embed.set_image(url=d["image"])
|
||||
if d["footer_text"] or d["footer_icon"]:
|
||||
embed.set_footer(text=d["footer_text"] or "", icon_url=d["footer_icon"] or discord.Embed.Empty)
|
||||
if d["author_text"] or d["author_icon"]:
|
||||
embed.set_author(name=d["author_text"] or "", icon_url=d["author_icon"] or discord.Embed.Empty)
|
||||
for field in d["fields"]:
|
||||
embed.add_field(name=field["name"], value=field["value"], inline=False)
|
||||
return embed
|
||||
|
||||
async def _select_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
await interaction.response.send_message("This builder doesn't belong to you.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
|
||||
value = interaction.data["values"][0]
|
||||
|
||||
def chk(m):
|
||||
return m.channel.id == self.ctx.channel.id and m.author.id == self.ctx.author.id
|
||||
|
||||
prompts = {
|
||||
"Title": "Enter the **Title** of the embed:",
|
||||
"Description": "Enter the **Description** of the embed:",
|
||||
"Color": "Enter the color as a hex value (e.g., `#FF0000`):",
|
||||
"Thumbnail": "Enter the **Thumbnail URL**:",
|
||||
"Image": "Enter the **Image URL**:",
|
||||
"Footer Text": "Enter the **Footer text**:",
|
||||
"Footer Icon": "Enter the **Footer icon URL**:",
|
||||
"Author Text": "Enter the **Author text**:",
|
||||
"Author Icon": "Enter the **Author icon URL**:",
|
||||
"Add Field": "Enter the **Field title**:",
|
||||
}
|
||||
|
||||
await self.ctx.send(prompts.get(value, "Enter a value:"))
|
||||
|
||||
try:
|
||||
msg = await self.ctx.bot.wait_for("message", timeout=30, check=chk)
|
||||
|
||||
if value == "Title":
|
||||
self.embed_data["title"] = msg.content
|
||||
elif value == "Description":
|
||||
self.embed_data["description"] = msg.content
|
||||
elif value == "Color":
|
||||
try:
|
||||
self.embed_data["color"] = int(msg.content.strip("#"), 16)
|
||||
except ValueError:
|
||||
await self.ctx.send("Invalid hex color. Please try again.")
|
||||
return
|
||||
elif value == "Thumbnail":
|
||||
if not msg.content.startswith("http"):
|
||||
await self.ctx.send("Invalid URL format.")
|
||||
return
|
||||
self.embed_data["thumbnail"] = msg.content
|
||||
elif value == "Image":
|
||||
if not msg.content.startswith("http"):
|
||||
await self.ctx.send("Invalid URL format.")
|
||||
return
|
||||
self.embed_data["image"] = msg.content
|
||||
elif value == "Footer Text":
|
||||
self.embed_data["footer_text"] = msg.content
|
||||
elif value == "Footer Icon":
|
||||
if not msg.content.startswith("http"):
|
||||
await self.ctx.send("Invalid URL format.")
|
||||
return
|
||||
self.embed_data["footer_icon"] = msg.content
|
||||
elif value == "Author Text":
|
||||
self.embed_data["author_text"] = msg.content
|
||||
elif value == "Author Icon":
|
||||
if not msg.content.startswith("http"):
|
||||
await self.ctx.send("Invalid URL format.")
|
||||
return
|
||||
self.embed_data["author_icon"] = msg.content
|
||||
elif value == "Add Field":
|
||||
field_name = msg.content
|
||||
await self.ctx.send("Enter the **Field value**:")
|
||||
val_msg = await self.ctx.bot.wait_for("message", timeout=30, check=chk)
|
||||
self.embed_data["fields"].append({"name": field_name, "value": val_msg.content})
|
||||
|
||||
# Rebuild and update
|
||||
self._build_view()
|
||||
await self.message.edit(view=self)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
await self.ctx.send("Timed Out.")
|
||||
|
||||
async def _send_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
await interaction.response.send_message("This builder doesn't belong to you.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
|
||||
await self.ctx.send("Mention the **channel** where you want to send this embed:")
|
||||
|
||||
def chk(m):
|
||||
return m.channel.id == self.ctx.channel.id and m.author.id == self.ctx.author.id
|
||||
|
||||
try:
|
||||
msg = await self.ctx.bot.wait_for("message", timeout=30, check=chk)
|
||||
chnl = msg.channel_mentions[0]
|
||||
embed = self._build_embed()
|
||||
await chnl.send(embed=embed)
|
||||
|
||||
# Show success
|
||||
self.container.clear_items()
|
||||
self.container.add_item(ui.TextDisplay(f"# {TICK} Embed Sent"))
|
||||
self.container.add_item(ui.Separator())
|
||||
self.container.add_item(ui.TextDisplay(f"Successfully sent the embed to {chnl.mention}"))
|
||||
await self.message.edit(view=self)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
await self.ctx.send("Timed Out.")
|
||||
except (IndexError, AttributeError):
|
||||
await self.ctx.send("Please mention a valid channel.")
|
||||
|
||||
async def _cancel_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
await interaction.response.send_message("This builder doesn't belong to you.", ephemeral=True)
|
||||
return
|
||||
self.container.clear_items()
|
||||
self.container.add_item(ui.TextDisplay("# Embed Builder"))
|
||||
self.container.add_item(ui.Separator())
|
||||
self.container.add_item(ui.TextDisplay(f"{CROSS} Embed setup cancelled."))
|
||||
await interaction.response.edit_message(view=self)
|
||||
self.stop()
|
||||
|
||||
async def on_timeout(self):
|
||||
try:
|
||||
self.container.clear_items()
|
||||
self.container.add_item(ui.TextDisplay("# Embed Builder"))
|
||||
self.container.add_item(ui.Separator())
|
||||
self.container.add_item(ui.TextDisplay("⏰ Builder timed out. Use the command again."))
|
||||
await self.message.edit(view=self)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class Embed(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.hybrid_command(name="embed")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 7, commands.BucketType.user)
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def _embed(self, ctx):
|
||||
view = EmbedBuilder(ctx)
|
||||
view.message = await ctx.send(view=view)
|
||||
185
bot/cogs/commands/Games.py
Normal file
185
bot/cogs/commands/Games.py
Normal file
@@ -0,0 +1,185 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import os
|
||||
from core import Cog, zyrox, Context
|
||||
import games as games
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2
|
||||
from games import button_games as btn
|
||||
import random
|
||||
import asyncio
|
||||
|
||||
|
||||
|
||||
class Games(Cog):
|
||||
"""Zyrox Games"""
|
||||
|
||||
def __init__(self, client: zyrox):
|
||||
self.client = client
|
||||
|
||||
|
||||
@commands.hybrid_command(name="chess",
|
||||
help="Play Chess with a user.",
|
||||
usage="Chess <user>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(5, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _chess(self, ctx: Context, player: discord.Member):
|
||||
if player == ctx.author:
|
||||
await ctx.send(view=CV2("❌ Error", "You Cannot play game with yourself!"))
|
||||
elif player.bot:
|
||||
await ctx.send(view=CV2("❌ Error", "You cannot play with bots!"))
|
||||
else:
|
||||
game = btn.BetaChess(white=ctx.author, black=player)
|
||||
await game.start(ctx)
|
||||
|
||||
|
||||
@commands.hybrid_command(name="rps",
|
||||
help="Play Rock Paper Scissor with bot/user.",
|
||||
aliases=["rockpaperscissors"],
|
||||
usage="Rockpaperscissors")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(5, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _rps(self, ctx: Context, player: discord.Member = None):
|
||||
game = btn.BetaRockPaperScissors(player)
|
||||
await game.start(ctx, timeout=120)
|
||||
|
||||
@commands.hybrid_command(name="tic-tac-toe",
|
||||
help="play tic-tac-toe game with a user.",
|
||||
aliases=["ttt", "tictactoe"],
|
||||
usage="Ticktactoe <member>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(5, per=commands.BucketType.user, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _ttt(self, ctx: Context, player: discord.Member):
|
||||
if player == ctx.author:
|
||||
await ctx.send(view=CV2("❌ Error", "You cannot play game with yourself!"))
|
||||
elif player.bot:
|
||||
await ctx.send(view=CV2("❌ Error", "You cannot play with bots!"))
|
||||
else:
|
||||
game = btn.BetaTictactoe(cross=ctx.author, circle=player)
|
||||
await game.start(ctx, timeout=30)
|
||||
|
||||
@commands.hybrid_command(name="wordle",
|
||||
help="Wordle Game | Play with bot.",
|
||||
usage="Wordle")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(3, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _wordle(self, ctx: Context):
|
||||
game = games.Wordle()
|
||||
await game.start(ctx, timeout=120)
|
||||
|
||||
@commands.hybrid_command(name="2048",
|
||||
help="Play 2048 game with bot.",
|
||||
aliases=["twenty48"],
|
||||
usage="2048")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(3, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _2048(self, ctx: Context):
|
||||
game = btn.BetaTwenty48()
|
||||
await game.start(ctx, win_at=2048)
|
||||
|
||||
@commands.hybrid_command(name="memory-game",
|
||||
help="How strong is your memory?",
|
||||
aliases=["memory"],
|
||||
usage="memory-game")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(3, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _memory(self, ctx: Context):
|
||||
game = btn.MemoryGame()
|
||||
await game.start(ctx)
|
||||
|
||||
@commands.hybrid_command(name="number-slider",
|
||||
help="slide numbers with bot",
|
||||
aliases=["slider"],
|
||||
usage="slider")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(3, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _number_slider(self, ctx: Context):
|
||||
game = btn.NumberSlider()
|
||||
await game.start(ctx)
|
||||
|
||||
@commands.hybrid_command(name="battleship",
|
||||
help="Play battleship game with your friend.",
|
||||
aliases=["battle-ship"],
|
||||
usage="battleship <user>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(3, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _battle(self, ctx: Context, player: discord.Member):
|
||||
game = btn.BetaBattleShip(player1=ctx.author, player2=player)
|
||||
await game.start(ctx)
|
||||
|
||||
@commands.group(name="country-guesser",
|
||||
help="Guess name of the country by flag.",
|
||||
aliases=["guess", "guesser", "countryguesser"],
|
||||
usage="country-guesser")
|
||||
@commands.guild_only()
|
||||
async def _country_guesser(self, ctx: Context):
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help("country-guesser")
|
||||
|
||||
@_country_guesser.command(name="start",
|
||||
help="Starts the country guesser game. It's a 100 Seconds Game so suggested to play in a SPECIFIC CHANNEL.")
|
||||
async def _start_country_guesser(self, ctx: Context):
|
||||
game = games.CountryGuesser(is_flags=True, hints=2)
|
||||
await game.start(ctx)
|
||||
|
||||
"""@_country_guesser.command(name="end",
|
||||
help="Ends the country guesser game.")
|
||||
async def _end_country_guesser(self, ctx: Context):
|
||||
await self.country_guesser_game.end_game_manually(ctx)"""
|
||||
|
||||
@commands.hybrid_command(name="connectfour",
|
||||
help="Play Connect Four game with user.",
|
||||
aliases=["c4", "connect-four", "connect4"],
|
||||
usage="connectfour <user>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.user, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _connectfour(self, ctx: Context, player: discord.Member):
|
||||
if player == ctx.author:
|
||||
await ctx.send(view=CV2("❌ Error", "You cannot play against yourself!"))
|
||||
elif player.bot:
|
||||
await ctx.send(view=CV2("❌ Error", "You cannot play with bots!"))
|
||||
else:
|
||||
game = games.ConnectFour(red=ctx.author, blue=player)
|
||||
await game.start(ctx, timeout=300)
|
||||
|
||||
|
||||
|
||||
@commands.hybrid_command(name="lights-out",
|
||||
help="Play Lights Show game with bot.",
|
||||
aliases=["lightsout"],
|
||||
usage="Lights-out")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(3, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _lights_show(self, ctx: Context):
|
||||
game = btn.LightsOut()
|
||||
await game.start(ctx)
|
||||
126
bot/cogs/commands/Invc.py
Normal file
126
bot/cogs/commands/Invc.py
Normal file
@@ -0,0 +1,126 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import aiosqlite
|
||||
import asyncio
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2
|
||||
from utils.config import *
|
||||
|
||||
class Invcrole(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.db_path = 'db/invc.db'
|
||||
self.bot.loop.create_task(self.create_table())
|
||||
|
||||
async def create_table(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS vcroles (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
role_id INTEGER NOT NULL
|
||||
)
|
||||
''')
|
||||
await db.commit()
|
||||
|
||||
@commands.group(name='vcrole', help="Vcrole Setup commands", invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def vcrole(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@vcrole.command(name='add', help="Adds a role to the vcrole list")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def add(self, ctx, role: discord.Role):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute('SELECT role_id FROM vcroles WHERE guild_id = ?', (ctx.guild.id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row:
|
||||
await ctx.reply(view=CV2("⚠️ Access Denied", f"VC role is already set in this guild with the role {ctx.guild.get_role(row[0]).mention}.\nPlease **remove** it to add another one."))
|
||||
return
|
||||
await db.execute('INSERT INTO vcroles (guild_id, role_id) VALUES (?, ?)', (ctx.guild.id, role.id))
|
||||
await db.commit()
|
||||
await ctx.reply(view=CV2("✅ Success", f"VC role {role.mention} added for this guild."))
|
||||
|
||||
@vcrole.command(name='remove', aliases=["reset"], help="Removes the role from vcrole list")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def remove(self, ctx, role: discord.Role):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute('SELECT role_id FROM vcroles WHERE guild_id = ? AND role_id = ?', (ctx.guild.id, role.id)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if not row:
|
||||
await ctx.send(view=CV2("❌ Error", "Given role is not set in VC role."))
|
||||
return
|
||||
await db.execute('DELETE FROM vcroles WHERE guild_id = ? AND role_id = ?', (ctx.guild.id, role.id))
|
||||
await db.commit()
|
||||
await ctx.send(view=CV2("✅ Success", f"VC role {role.mention} removed for this guild."))
|
||||
|
||||
@vcrole.command(name='config', aliases=['view', 'show'], help="Shows the Current vcrole in this Guild")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def config(self, ctx):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute('SELECT role_id FROM vcroles WHERE guild_id = ?', (ctx.guild.id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if not row:
|
||||
await ctx.send(view=CV2("❌ Error", "VC role is not set in this guild."))
|
||||
return
|
||||
role = ctx.guild.get_role(row[0])
|
||||
await ctx.send(view=CV2("VC Role Configuration", f"Current VC role in this guild is {role.mention}.\n\n*Make sure to place My role above Vc role*"))
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_voice_state_update(self, member, before, after):
|
||||
try:
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute('SELECT role_id FROM vcroles WHERE guild_id = ?', (member.guild.id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if not row:
|
||||
return
|
||||
role = member.guild.get_role(row[0])
|
||||
|
||||
if after.channel and role not in member.roles:
|
||||
await self.add_role_with_retry(member, role, reason=f"Member Joined VC | {BRAND_NAME} Invcrole")
|
||||
elif not after.channel and role in member.roles:
|
||||
await self.remove_role_with_retry(member, role, reason=f"Member Left VC | {BRAND_NAME} Invcrole")
|
||||
except discord.Forbidden:
|
||||
print(f"Bot lacks permissions to maange role in a guild during Invc Event .")
|
||||
except Exception as e:
|
||||
print(f"Error in on_voice_state_update: {e}")
|
||||
|
||||
async def add_role_with_retry(self, member, role, reason, retries=5):
|
||||
attempt = 0
|
||||
while attempt < retries:
|
||||
try:
|
||||
await member.add_roles(role, reason=reason)
|
||||
break
|
||||
except discord.errors.RateLimited as e:
|
||||
retry_after = e.retry_after if hasattr(e, 'retry_after') else 1
|
||||
await asyncio.sleep(retry_after)
|
||||
except discord.HTTPException as e:
|
||||
print(f"Error adding role: {e}")
|
||||
break
|
||||
attempt += 1
|
||||
|
||||
async def remove_role_with_retry(self, member, role, reason, retries=5):
|
||||
attempt = 0
|
||||
while attempt < retries:
|
||||
try:
|
||||
await member.remove_roles(role, reason=reason)
|
||||
break
|
||||
except discord.errors.RateLimited as e:
|
||||
retry_after = e.retry_after if hasattr(e, 'retry_after') else 1
|
||||
await asyncio.sleep(retry_after)
|
||||
except discord.HTTPException as e:
|
||||
print(f"Error removing role: {e}")
|
||||
break
|
||||
attempt += 1
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Invcrole(bot))
|
||||
223
bot/cogs/commands/Media.py
Normal file
223
bot/cogs/commands/Media.py
Normal file
@@ -0,0 +1,223 @@
|
||||
import discord
|
||||
import aiosqlite
|
||||
from discord.ext import commands
|
||||
from utils.Tools import blacklist_check, ignore_check
|
||||
from collections import defaultdict
|
||||
import time
|
||||
from utils.cv2 import CV2
|
||||
|
||||
class Media(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.infractions = defaultdict(list)
|
||||
|
||||
|
||||
async def set_db(self):
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS media_channels (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
channel_id INTEGER NOT NULL
|
||||
)
|
||||
''')
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS media_bypass (
|
||||
guild_id INTEGER,
|
||||
user_id INTEGER,
|
||||
PRIMARY KEY (guild_id, user_id)
|
||||
)
|
||||
''')
|
||||
await db.commit()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
await self.set_db()
|
||||
|
||||
@commands.hybrid_group(name="media", help="Setup Media channel, Media channel will not allow users to send messages other than media files.", invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def media(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@media.command(name="setup", aliases=["set", "add"], help="Sets up a media-only channel for the server")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def setup(self, ctx, *, channel: discord.TextChannel):
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
async with db.execute('SELECT channel_id FROM media_channels WHERE guild_id = ?', (ctx.guild.id,)) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
if result:
|
||||
await ctx.reply(view=CV2("❌ Error", "A media channel is already set. Please remove it before setting a new one."))
|
||||
return
|
||||
|
||||
await db.execute('INSERT INTO media_channels (guild_id, channel_id) VALUES (?, ?)', (ctx.guild.id, channel.id))
|
||||
await db.commit()
|
||||
|
||||
await ctx.reply(view=CV2("✅ Success", f"Successfully set {channel.mention} as the media-only channel.\n\n*Make sure to grant me \"Manage Messages\" permission for functioning of media channel.*"))
|
||||
|
||||
@media.command(name="remove", aliases=["reset", "delete"], help="Removes the current media-only channel")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def remove(self, ctx):
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
async with db.execute('SELECT channel_id FROM media_channels WHERE guild_id = ?', (ctx.guild.id,)) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
if not result:
|
||||
await ctx.reply(view=CV2("❌ Error", "There is no media-only channel set for this server."))
|
||||
return
|
||||
|
||||
await db.execute('DELETE FROM media_channels WHERE guild_id = ?', (ctx.guild.id,))
|
||||
await db.commit()
|
||||
|
||||
await ctx.reply(view=CV2("✅ Success", "Successfully removed the media-only channel."))
|
||||
|
||||
@media.command(name="config", aliases=["settings", "show"], help="Shows the configured media-only channel")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def config(self, ctx):
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
async with db.execute('SELECT channel_id FROM media_channels WHERE guild_id = ?', (ctx.guild.id,)) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
if not result:
|
||||
await ctx.reply(view=CV2("❌ Error", "There is no media-only channel set for this server."))
|
||||
return
|
||||
|
||||
channel = self.client.get_channel(result[0])
|
||||
await ctx.reply(view=CV2("Media Only Channel", f"The configured media-only channel is {channel.mention}."))
|
||||
|
||||
@media.group(name="bypass", help="Add/Remove user to bypass in Media only channel, Bypassed users can send messages in Media channel.", invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@bypass.command(name="add", help="Adds a user to the bypass list")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_add(self, ctx, user: discord.Member):
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
async with db.execute('SELECT COUNT(*) FROM media_bypass WHERE guild_id = ?', (ctx.guild.id,)) as cursor:
|
||||
count = await cursor.fetchone()
|
||||
if count[0] >= 25:
|
||||
await ctx.reply(view=CV2("❌ Error", "The bypass list can only hold up to 25 users."))
|
||||
return
|
||||
|
||||
async with db.execute('SELECT 1 FROM media_bypass WHERE guild_id = ? AND user_id = ?', (ctx.guild.id, user.id)) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
if result:
|
||||
await ctx.reply(view=CV2("❌ Error", f"{user.mention} is already in the bypass list."))
|
||||
return
|
||||
|
||||
await db.execute('INSERT INTO media_bypass (guild_id, user_id) VALUES (?, ?)', (ctx.guild.id, user.id))
|
||||
await db.commit()
|
||||
|
||||
await ctx.reply(view=CV2("✅ Success", f"{user.mention} has been added to the bypass list."))
|
||||
|
||||
@bypass.command(name="remove", help="Removes a user from the bypass list")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_remove(self, ctx, user: discord.Member):
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
async with db.execute('SELECT 1 FROM media_bypass WHERE guild_id = ? AND user_id = ?', (ctx.guild.id, user.id)) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
if not result:
|
||||
await ctx.reply(view=CV2("❌ Error", f"{user.mention} is not in the bypass list."))
|
||||
return
|
||||
|
||||
await db.execute('DELETE FROM media_bypass WHERE guild_id = ? AND user_id = ?', (ctx.guild.id, user.id))
|
||||
await db.commit()
|
||||
|
||||
await ctx.reply(view=CV2("✅ Success", f"{user.mention} has been removed from the bypass list."))
|
||||
|
||||
@bypass.command(name="show", aliases=["list", "view"], help="Shows the bypass list")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_show(self, ctx):
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
async with db.execute('SELECT user_id FROM media_bypass WHERE guild_id = ?', (ctx.guild.id,)) as cursor:
|
||||
result = await cursor.fetchall()
|
||||
if not result:
|
||||
await ctx.reply(view=CV2("Bypass List", "There are no users in the bypass list."))
|
||||
return
|
||||
|
||||
users = [self.client.get_user(user_id).mention for user_id, in result]
|
||||
user_mentions = "\n".join(users)
|
||||
|
||||
await ctx.reply(view=CV2("Bypass List", user_mentions))
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.bot:
|
||||
return
|
||||
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
async with db.execute('SELECT channel_id FROM media_channels WHERE guild_id = ?', (message.guild.id,)) as cursor:
|
||||
media_channel = await cursor.fetchone()
|
||||
|
||||
if media_channel and message.channel.id == media_channel[0]:
|
||||
async with aiosqlite.connect('db/block.db') as block_db:
|
||||
async with block_db.execute('SELECT 1 FROM user_blacklist WHERE user_id = ?', (message.author.id,)) as cursor:
|
||||
blacklisted = await cursor.fetchone()
|
||||
|
||||
async with aiosqlite.connect('db/media.db') as db:
|
||||
async with db.execute('SELECT 1 FROM media_bypass WHERE guild_id = ? AND user_id = ?', (message.guild.id, message.author.id)) as cursor:
|
||||
bypassed = await cursor.fetchone()
|
||||
|
||||
if blacklisted or bypassed:
|
||||
return
|
||||
|
||||
if not message.attachments:
|
||||
try:
|
||||
await message.delete()
|
||||
msg = await message.channel.send(view=CV2("⚠️ Warning", f"{message.author.mention} This channel is configured for Media only. Please send only media files."))
|
||||
await msg.delete(delay=5)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
current_time = time.time()
|
||||
self.infractions[message.author.id].append(current_time)
|
||||
|
||||
|
||||
self.infractions[message.author.id] = [
|
||||
infraction for infraction in self.infractions[message.author.id]
|
||||
if current_time - infraction <= 5
|
||||
]
|
||||
|
||||
if len(self.infractions[message.author.id]) >= 5:
|
||||
async with aiosqlite.connect('db/block.db') as block_db:
|
||||
await block_db.execute('INSERT OR IGNORE INTO user_blacklist (user_id) VALUES (?)', (message.author.id,))
|
||||
|
||||
await block_db.commit()
|
||||
|
||||
desc = (
|
||||
"⚠️ You are blacklisted from using my commands due to spamming in the media channel. "
|
||||
"If you believe this is a mistake, please reach out to the support server with proof."
|
||||
)
|
||||
await message.channel.send(f"{message.author.mention}", view=CV2("You Have Been Blacklisted", desc))
|
||||
del self.infractions[message.author.id]
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Media(bot))
|
||||
236
bot/cogs/commands/afk.py
Normal file
236
bot/cogs/commands/afk.py
Normal file
@@ -0,0 +1,236 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container, Button, ActionRow
|
||||
import aiosqlite
|
||||
import os
|
||||
import time
|
||||
|
||||
from utils.Tools import blacklist_check, ignore_check
|
||||
from utils.cv2 import CV2, build_container
|
||||
from utils.emoji import TICK, MENTION, SEED, TIME
|
||||
from utils.config import *
|
||||
|
||||
DB_PATH = "db/afk.db"
|
||||
THEME_COLOR = 0xFF0000
|
||||
FOOTER_TEXT = f"Developed by {BRAND_NAME}"
|
||||
|
||||
class AfkTypeView(LayoutView):
|
||||
def __init__(self, author, reason, timeout=60):
|
||||
super().__init__(timeout=timeout)
|
||||
self.author = author
|
||||
self.reason = reason
|
||||
self.value = None
|
||||
|
||||
self.global_btn = Button(label="Global AFK", style=discord.ButtonStyle.primary)
|
||||
self.local_btn = Button(label="Local AFK", style=discord.ButtonStyle.success)
|
||||
|
||||
self.global_btn.callback = self.global_afk
|
||||
self.local_btn.callback = self.local_afk
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"You are going AFK for reason: **{reason}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("Select your preferred AFK type from the buttons below."),
|
||||
ActionRow(self.global_btn, self.local_btn)
|
||||
)
|
||||
)
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.author.id:
|
||||
await interaction.response.send_message(
|
||||
f"Only **{self.author.display_name}** can use this button.", ephemeral=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
async def global_afk(self, interaction: discord.Interaction):
|
||||
self.value = "global"
|
||||
await interaction.response.defer()
|
||||
self.stop()
|
||||
|
||||
async def local_afk(self, interaction: discord.Interaction):
|
||||
self.value = "local"
|
||||
await interaction.response.defer()
|
||||
self.stop()
|
||||
|
||||
class AfkSuccess(LayoutView):
|
||||
def __init__(self, type_val, reason):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"{TICK} **AFK Activated**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"**{MENTION} You are now marked as {type_val.capitalize()} AFK.**\n{SEED} **Reason:** {reason}")
|
||||
)
|
||||
)
|
||||
|
||||
class AfkMention(LayoutView):
|
||||
def __init__(self, user_mention, afk_time, reason):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{user_mention.display_name}** is AFK right now!"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"Went AFK <t:{int(afk_time)}:R> for the following reason:\n**{reason}**")
|
||||
)
|
||||
)
|
||||
|
||||
class AfkWelcomeBack(LayoutView):
|
||||
def __init__(self, author, mentions, elapsed_time):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{author.display_name}** Is Back!"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"{TICK} **AFK Removed**\n{MENTION} **Mentions:** {mentions}\n{TIME} **AFK Time:** {elapsed_time}")
|
||||
)
|
||||
)
|
||||
|
||||
class afk(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.theme_color = THEME_COLOR
|
||||
self.bot.loop.create_task(self.initialize_db())
|
||||
|
||||
async def initialize_db(self):
|
||||
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS afk (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
type TEXT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
time INTEGER NOT NULL,
|
||||
mentions INTEGER NOT NULL DEFAULT 0
|
||||
)
|
||||
""")
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS afk_guild (
|
||||
user_id INTEGER NOT NULL,
|
||||
guild_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (user_id, guild_id)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
async def time_formatter(self, seconds: float):
|
||||
m, s = divmod(int(seconds), 60)
|
||||
h, m = divmod(m, 60)
|
||||
d, h = divmod(h, 24)
|
||||
parts = []
|
||||
if d > 0: parts.append(f"{d}d")
|
||||
if h > 0: parts.append(f"{h}h")
|
||||
if m > 0: parts.append(f"{m}m")
|
||||
if s > 0: parts.append(f"{s}s")
|
||||
return " ".join(parts) or "0s"
|
||||
|
||||
async def set_afk(self, user, afk_type, reason, current_guild=None):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("DELETE FROM afk_guild WHERE user_id = ?", (user.id,))
|
||||
|
||||
await db.execute(
|
||||
"INSERT OR REPLACE INTO afk (user_id, type, reason, time, mentions) VALUES (?, ?, ?, ?, 0)",
|
||||
(user.id, afk_type, reason, int(time.time()))
|
||||
)
|
||||
if afk_type == "global":
|
||||
for g in self.bot.guilds:
|
||||
if g.get_member(user.id):
|
||||
await db.execute("INSERT OR IGNORE INTO afk_guild (user_id, guild_id) VALUES (?, ?)", (user.id, g.id))
|
||||
elif current_guild:
|
||||
await db.execute("INSERT OR IGNORE INTO afk_guild (user_id, guild_id) VALUES (?, ?)", (user.id, current_guild.id))
|
||||
|
||||
await db.commit()
|
||||
|
||||
async def clear_afk(self, message):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
cursor = await db.execute("SELECT 1 FROM afk_guild WHERE user_id = ? AND guild_id = ?", (message.author.id, message.guild.id))
|
||||
if not await cursor.fetchone():
|
||||
return
|
||||
|
||||
cursor = await db.execute("SELECT type, time, mentions FROM afk WHERE user_id = ?", (message.author.id,))
|
||||
afk_data = await cursor.fetchone()
|
||||
if not afk_data: return
|
||||
|
||||
afk_type, afk_time, mentions = afk_data
|
||||
elapsed_time = await self.time_formatter(time.time() - afk_time)
|
||||
|
||||
if afk_type == 'global':
|
||||
await db.execute("DELETE FROM afk WHERE user_id = ?", (message.author.id,))
|
||||
await db.execute("DELETE FROM afk_guild WHERE user_id = ?", (message.author.id,))
|
||||
else:
|
||||
await db.execute("DELETE FROM afk_guild WHERE user_id = ? AND guild_id = ?", (message.author.id, message.guild.id))
|
||||
cursor_check = await db.execute("SELECT 1 FROM afk_guild WHERE user_id = ?", (message.author.id,))
|
||||
if not await cursor_check.fetchone():
|
||||
await db.execute("DELETE FROM afk WHERE user_id = ?", (message.author.id,))
|
||||
|
||||
await db.commit()
|
||||
|
||||
view = AfkWelcomeBack(message.author, mentions, elapsed_time)
|
||||
try:
|
||||
await message.reply(view=view, delete_after=10, mention_author=False)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.bot or not message.guild:
|
||||
return
|
||||
|
||||
await self.clear_afk(message)
|
||||
|
||||
if not message.mentions:
|
||||
return
|
||||
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
for mentioned in message.mentions:
|
||||
if mentioned.bot or mentioned.id == message.author.id:
|
||||
continue
|
||||
|
||||
cursor = await db.execute("SELECT 1 FROM afk_guild WHERE user_id = ? AND guild_id = ?", (mentioned.id, message.guild.id))
|
||||
if await cursor.fetchone():
|
||||
cursor_main = await db.execute("SELECT reason, mentions, time FROM afk WHERE user_id = ?", (mentioned.id,))
|
||||
afk_data = await cursor_main.fetchone()
|
||||
if not afk_data: continue
|
||||
|
||||
reason, mentions, afk_time = afk_data
|
||||
|
||||
view = AfkMention(mentioned, afk_time, reason)
|
||||
await message.reply(view=view, delete_after=10, mention_author=False)
|
||||
|
||||
new_mentions = mentions + 1
|
||||
await db.execute("UPDATE afk SET mentions = ? WHERE user_id = ?", (new_mentions, mentioned.id))
|
||||
await db.commit()
|
||||
|
||||
dm_embed = discord.Embed(description=f"You were mentioned in **{message.guild.name}** by **{message.author}**", color=self.theme_color)
|
||||
dm_embed.add_field(name="Total Mentions", value=str(new_mentions))
|
||||
dm_embed.add_field(name="Jump to Message", value=f"[Click Here]({message.jump_url})")
|
||||
dm_embed.set_footer(text=FOOTER_TEXT, icon_url=self.bot.user.avatar.url)
|
||||
try:
|
||||
await mentioned.send(embed=dm_embed)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
|
||||
@commands.hybrid_command(name="afk", description="Set your AFK status with a reason (Global or Local).")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
async def afk(self, ctx: commands.Context, *, reason: str = "I am AFK"):
|
||||
if any(w in reason.lower() for w in ("discord.gg", "gg/")):
|
||||
return await ctx.send(embed=discord.Embed(description="⚠️ Advertising is not allowed in AFK reasons.", color=self.theme_color), ephemeral=True)
|
||||
|
||||
type_view = AfkTypeView(ctx.author, reason)
|
||||
msg = await ctx.reply(view=type_view, mention_author=False)
|
||||
await type_view.wait()
|
||||
|
||||
if not type_view.value:
|
||||
await msg.edit(content="Timed out.", view=None)
|
||||
return
|
||||
|
||||
await self.set_afk(ctx.author, type_view.value, reason, ctx.guild)
|
||||
|
||||
success_view = AfkSuccess(type_view.value, reason)
|
||||
await msg.edit(view=success_view)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(afk(bot))
|
||||
1617
bot/cogs/commands/ai.py
Normal file
1617
bot/cogs/commands/ai.py
Normal file
File diff suppressed because it is too large
Load Diff
86
bot/cogs/commands/anti_unwl.py
Normal file
86
bot/cogs/commands/anti_unwl.py
Normal file
@@ -0,0 +1,86 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, MANAGER, REDDOT, TICK
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
import aiosqlite
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Unwhitelist(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.initialize_db())
|
||||
|
||||
#@commands.Cog.listener()
|
||||
async def initialize_db(self):
|
||||
self.db = await aiosqlite.connect('db/anti.db')
|
||||
|
||||
@commands.hybrid_command(name='unwhitelist', aliases=['unwl'], help="Unwhitelist a user from antinuke")
|
||||
@commands.has_permissions(administrator=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def unwhitelist(self, ctx, member: discord.Member = None):
|
||||
if ctx.guild.member_count < 2:
|
||||
view = CV2(f"{CROSS} Error", "Your Server Doesn't Meet My 30 Member Criteria")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT owner_id FROM extraowners WHERE guild_id = ? AND owner_id = ?",
|
||||
(ctx.guild.id, ctx.author.id)
|
||||
) as cursor:
|
||||
check = await cursor.fetchone()
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT status FROM antinuke WHERE guild_id = ?",
|
||||
(ctx.guild.id,)
|
||||
) as cursor:
|
||||
antinuke = await cursor.fetchone()
|
||||
|
||||
is_owner = ctx.author.id == ctx.guild.owner_id
|
||||
if not is_owner and not check:
|
||||
view = CV2(f"{CROSS} Access Denied", "Only Server Owner or Extra Owner can Run this Command!")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
if not antinuke or not antinuke[0]:
|
||||
view = CV2(
|
||||
f"{ctx.guild.name} Security Settings {MANAGER}",
|
||||
f"Ohh NO! looks like your server doesn't enabled security\n\nCurrent Status : {CROSS}\n\nTo enable use `antinuke enable`"
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
if not member:
|
||||
view = CV2(
|
||||
"__Unwhitelist Commands__",
|
||||
"**Removes user from whitelisted users which means that the antinuke module will now take actions on them if they trigger it.**",
|
||||
f"**Usage**\n{REDDOT} `unwhitelist @user/id`\n{REDDOT} `unwl @user`"
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT * FROM whitelisted_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, member.id)
|
||||
) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if not data:
|
||||
view = CV2(f"{CROSS} Error", f"<@{member.id}> is not a whitelisted member.")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
await self.db.execute(
|
||||
"DELETE FROM whitelisted_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, member.id)
|
||||
)
|
||||
await self.db.commit()
|
||||
|
||||
view = CV2(f"{TICK} Success", f"User <@!{member.id}> has been removed from the whitelist.")
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
|
||||
351
bot/cogs/commands/anti_wl.py
Normal file
351
bot/cogs/commands/anti_wl.py
Normal file
@@ -0,0 +1,351 @@
|
||||
import discord
|
||||
from utils.emoji import ARROWRED, CROSS, DISABLE, ENABLE, MANAGER, TICK
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
import aiosqlite
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Whitelist(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.initialize_db())
|
||||
|
||||
|
||||
#@commands.Cog.listener()
|
||||
async def initialize_db(self):
|
||||
self.db = await aiosqlite.connect('db/anti.db')
|
||||
await self.db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS whitelisted_users (
|
||||
guild_id INTEGER,
|
||||
user_id INTEGER,
|
||||
ban BOOLEAN DEFAULT FALSE,
|
||||
kick BOOLEAN DEFAULT FALSE,
|
||||
prune BOOLEAN DEFAULT FALSE,
|
||||
botadd BOOLEAN DEFAULT FALSE,
|
||||
serverup BOOLEAN DEFAULT FALSE,
|
||||
memup BOOLEAN DEFAULT FALSE,
|
||||
chcr BOOLEAN DEFAULT FALSE,
|
||||
chdl BOOLEAN DEFAULT FALSE,
|
||||
chup BOOLEAN DEFAULT FALSE,
|
||||
rlcr BOOLEAN DEFAULT FALSE,
|
||||
rlup BOOLEAN DEFAULT FALSE,
|
||||
rldl BOOLEAN DEFAULT FALSE,
|
||||
meneve BOOLEAN DEFAULT FALSE,
|
||||
mngweb BOOLEAN DEFAULT FALSE,
|
||||
mngstemo BOOLEAN DEFAULT FALSE,
|
||||
PRIMARY KEY (guild_id, user_id)
|
||||
)
|
||||
''')
|
||||
await self.db.commit()
|
||||
|
||||
@commands.hybrid_command(name='whitelist', aliases=['wl'], help="Whitelists a user from antinuke for a specific action.")
|
||||
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
|
||||
async def whitelist(self, ctx, member: discord.Member = None):
|
||||
if ctx.guild.member_count < 2:
|
||||
view = CV2(f"{CROSS} Error", "Your Server Doesn't Meet My 30 Member Criteria")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
prefix=ctx.prefix
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT owner_id FROM extraowners WHERE guild_id = ? AND owner_id = ?",
|
||||
(ctx.guild.id, ctx.author.id)
|
||||
) as cursor:
|
||||
check = await cursor.fetchone()
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT status FROM antinuke WHERE guild_id = ?",
|
||||
(ctx.guild.id,)
|
||||
) as cursor:
|
||||
antinuke = await cursor.fetchone()
|
||||
|
||||
is_owner = ctx.author.id == ctx.guild.owner_id
|
||||
if not is_owner and not check:
|
||||
view = CV2(f"{CROSS} Access Denied", "Only Server Owner or Extra Owner can Run this Command!")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
if not antinuke or not antinuke[0]:
|
||||
view = CV2(
|
||||
f"{ctx.guild.name} Security Settings {MANAGER}",
|
||||
f"Ohh No! looks like your server doesn't enabled Antinuke\n\nCurrent Status : {CROSS}\n\nTo enable use `{prefix}antinuke enable`"
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
if not member:
|
||||
view = CV2(
|
||||
"__Whitelist Commands__",
|
||||
"**Adding a user to the whitelist means that no actions will be taken against them if they trigger the Anti-Nuke Module.**",
|
||||
f"**Usage**\n{ARROWRED} `{prefix}whitelist @user/id`\n{ARROWRED} `{prefix}wl @user`"
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT * FROM whitelisted_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, member.id)
|
||||
) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if data:
|
||||
view = CV2(f"{CROSS} Error", f"<@{member.id}> is already a whitelisted member, **Unwhitelist** the user and try again.")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
await self.db.execute(
|
||||
"INSERT INTO whitelisted_users (guild_id, user_id) VALUES (?, ?)",
|
||||
(ctx.guild.id, member.id)
|
||||
)
|
||||
await self.db.commit()
|
||||
|
||||
options = [
|
||||
discord.SelectOption(label="Ban", description="Whitelist a member with ban permission", value="ban"),
|
||||
discord.SelectOption(label="Kick", description="Whitelist a member with kick permission", value="kick"),
|
||||
discord.SelectOption(label="Prune", description="Whitelist a member with prune permission", value="prune"),
|
||||
discord.SelectOption(label="Bot Add", description="Whitelist a member with bot add permission", value="botadd"),
|
||||
discord.SelectOption(label="Server Update", description="Whitelist a member with server update permission", value="serverup"),
|
||||
discord.SelectOption(label="Member Update", description="Whitelist a member with member update permission", value="memup"),
|
||||
discord.SelectOption(label="Channel Create", description="Whitelist a member with channel create permission", value="chcr"),
|
||||
discord.SelectOption(label="Channel Delete", description="Whitelist a member with channel delete permission", value="chdl"),
|
||||
discord.SelectOption(label="Channel Update", description="Whitelist a member with channel update permission", value="chup"),
|
||||
discord.SelectOption(label="Role Create", description="Whitelist a member with role create permission", value="rlcr"),
|
||||
discord.SelectOption(label="Role Update", description="Whitelist a member with role update permission", value="rlup"),
|
||||
discord.SelectOption(label="Role Delete", description="Whitelist a member with role delete permission", value="rldl"),
|
||||
discord.SelectOption(label="Mention Everyone", description="Whitelist a member with mention everyone permission", value="meneve"),
|
||||
discord.SelectOption(label="Manage Webhook", description="Whitelist a member with manage webhook permission", value="mngweb")
|
||||
]
|
||||
|
||||
select = discord.ui.Select(placeholder="Choose Your Options", min_values=1, max_values=len(options), options=options, custom_id="wl")
|
||||
button = discord.ui.Button(label="Add This User To All Categories", style=discord.ButtonStyle.primary, custom_id="catWl")
|
||||
|
||||
action_view = discord.ui.View()
|
||||
action_view.add_item(select)
|
||||
action_view.add_item(button)
|
||||
|
||||
disabled_list = (
|
||||
f"{DISABLE} : **Ban**\n"
|
||||
f"{DISABLE} : **Kick**\n"
|
||||
f"{DISABLE} : **Prune**\n"
|
||||
f"{DISABLE} : **Bot Add**\n"
|
||||
f"{DISABLE} : **Server Update**\n"
|
||||
f"{DISABLE} : **Member Update**\n"
|
||||
f"{DISABLE} : **Channel Create**\n"
|
||||
f"{DISABLE} : **Channel Delete**\n"
|
||||
f"{DISABLE} : **Channel Update**\n"
|
||||
f"{DISABLE} : **Role Create**\n"
|
||||
f"{DISABLE} : **Role Delete**\n"
|
||||
f"{DISABLE} : **Role Update**\n"
|
||||
f"{DISABLE} : **Mention** @everyone\n"
|
||||
f"{DISABLE} : **Webhook Management**"
|
||||
)
|
||||
|
||||
wl_view = CV2(
|
||||
ctx.guild.name,
|
||||
disabled_list,
|
||||
f"**Executor:** <@!{ctx.author.id}> │ **Target:** <@!{member.id}>"
|
||||
)
|
||||
|
||||
msg = await ctx.send(view=action_view)
|
||||
|
||||
def check(interaction):
|
||||
return interaction.user.id == ctx.author.id and interaction.message.id == msg.id
|
||||
|
||||
try:
|
||||
interaction = await self.bot.wait_for("interaction", check=check, timeout=60.0)
|
||||
if interaction.data["custom_id"] == "catWl":
|
||||
|
||||
await self.db.execute(
|
||||
"UPDATE whitelisted_users SET ban = ?, kick = ?, prune = ?, botadd = ?, serverup = ?, memup = ?, chcr = ?, chdl = ?, chup = ?, rlcr = ?, rldl = ?, rlup = ?, meneve = ?, mngweb = ?, mngstemo = ? WHERE guild_id = ? AND user_id = ?",
|
||||
(True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, ctx.guild.id, member.id)
|
||||
)
|
||||
await self.db.commit()
|
||||
|
||||
enabled_list = (
|
||||
f"{ENABLE} : **Ban**\n"
|
||||
f"{ENABLE} : **Kick**\n"
|
||||
f"{ENABLE} : **Prune**\n"
|
||||
f"{ENABLE} : **Bot Add**\n"
|
||||
f"{ENABLE} : **Server Update**\n"
|
||||
f"{ENABLE} : **Member Update**\n"
|
||||
f"{ENABLE} : **Channel Create**\n"
|
||||
f"{ENABLE} : **Channel Delete**\n"
|
||||
f"{ENABLE} : **Channel Update**\n"
|
||||
f"{ENABLE} : **Role Create**\n"
|
||||
f"{ENABLE} : **Role Delete**\n"
|
||||
f"{ENABLE} : **Role Update**\n"
|
||||
f"{ENABLE} : **Mention** @everyone\n"
|
||||
f"{ENABLE} : **Webhook Management**"
|
||||
)
|
||||
|
||||
result = CV2(
|
||||
ctx.guild.name,
|
||||
enabled_list,
|
||||
f"**Executor:** <@!{ctx.author.id}> │ **Target:** <@!{member.id}>"
|
||||
)
|
||||
await interaction.response.edit_message(view=result)
|
||||
else:
|
||||
|
||||
fields = {
|
||||
'ban': 'Ban',
|
||||
'kick': 'Kick',
|
||||
'prune': 'Prune',
|
||||
'botadd': 'Bot Add',
|
||||
'serverup': 'Server Update',
|
||||
'memup': 'Member Update',
|
||||
'chcr': 'Channel Create',
|
||||
'chdl': 'Channel Delete',
|
||||
'chup': 'Channel Update',
|
||||
'rlcr': 'Role Create',
|
||||
'rldl': 'Role Delete',
|
||||
'rlup': 'Role Update',
|
||||
'meneve': 'Mention Everyone',
|
||||
'mngweb': 'Manage Webhooks'
|
||||
}
|
||||
|
||||
|
||||
status_lines = []
|
||||
selected_values = interaction.data["values"]
|
||||
for key, name in fields.items():
|
||||
if key in selected_values:
|
||||
status_lines.append(f"{ENABLE} : **{name}**")
|
||||
else:
|
||||
status_lines.append(f"{DISABLE} : **{name}**")
|
||||
|
||||
for value in selected_values:
|
||||
await self.db.execute(
|
||||
f"UPDATE whitelisted_users SET {value} = ? WHERE guild_id = ? AND user_id = ?",
|
||||
(True, ctx.guild.id, member.id)
|
||||
)
|
||||
|
||||
await self.db.commit()
|
||||
|
||||
result = CV2(
|
||||
ctx.guild.name,
|
||||
"\n".join(status_lines),
|
||||
f"**Executor:** <@!{ctx.author.id}> │ **Target:** <@!{member.id}>"
|
||||
)
|
||||
await interaction.response.edit_message(view=result)
|
||||
except TimeoutError:
|
||||
await msg.edit(view=None)
|
||||
|
||||
|
||||
@commands.hybrid_command(name='whitelisted', aliases=['wlist'], help="Shows the list of whitelisted users.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def whitelisted(self, ctx):
|
||||
if ctx.guild.member_count < 2:
|
||||
view = CV2(f"{CROSS} Error", "Your Server Doesn't Meet My 30 Member Criteria")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
pre=ctx.prefix
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT owner_id FROM extraowners WHERE guild_id = ? AND owner_id = ?",
|
||||
(ctx.guild.id, ctx.author.id)
|
||||
) as cursor:
|
||||
check = await cursor.fetchone()
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT status FROM antinuke WHERE guild_id = ?",
|
||||
(ctx.guild.id,)
|
||||
) as cursor:
|
||||
antinuke = await cursor.fetchone()
|
||||
|
||||
is_owner = ctx.author.id == ctx.guild.owner_id
|
||||
if not is_owner and not check:
|
||||
view = CV2(f"{CROSS} Access Denied", "Only Server Owner or Extra Owner can Run this Command!")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
if not antinuke or not antinuke[0]:
|
||||
view = CV2(
|
||||
f"{ctx.guild.name} Security Settings {MANAGER}",
|
||||
f"Ohh NO! looks like your server doesn't enabled security\n\nCurrent Status : {CROSS}\n\nTo enable use `{pre}antinuke enable`"
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT user_id FROM whitelisted_users WHERE guild_id = ?",
|
||||
(ctx.guild.id,)
|
||||
) as cursor:
|
||||
data = await cursor.fetchall()
|
||||
|
||||
if not data:
|
||||
view = CV2(f"{CROSS} Error", "No whitelisted users found.")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
whitelisted_users = [self.bot.get_user(user_id[0]) for user_id in data]
|
||||
whitelisted_users_str = ", ".join(f"<@!{user.id}>" for user in whitelisted_users if user)
|
||||
|
||||
view = CV2(f"__Whitelisted Users for {ctx.guild.name}__", whitelisted_users_str)
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
@commands.hybrid_command(name="whitelistreset", aliases=['wlreset'], help="Resets the whitelisted users.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def whitelistreset(self, ctx):
|
||||
if ctx.guild.member_count < 2:
|
||||
view = CV2(f"{CROSS} Error", "Your Server Doesn't Meet My 30 Member Criteria")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
pre=ctx.prefix
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT owner_id FROM extraowners WHERE guild_id = ? AND owner_id = ?",
|
||||
(ctx.guild.id, ctx.author.id)
|
||||
) as cursor:
|
||||
check = await cursor.fetchone()
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT status FROM antinuke WHERE guild_id = ?",
|
||||
(ctx.guild.id,)
|
||||
) as cursor:
|
||||
antinuke = await cursor.fetchone()
|
||||
|
||||
is_owner = ctx.author.id == ctx.guild.owner_id
|
||||
if not is_owner and not check:
|
||||
view = CV2(f"{CROSS} Access Denied", "Only Server Owner or Extra Owner can Run this Command!")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
if not antinuke or not antinuke[0]:
|
||||
view = CV2(
|
||||
f"{ctx.guild.name} Security Settings {MANAGER}",
|
||||
f"Ohh NO! looks like your server doesn't enabled security\n\nCurrent Status : {CROSS}\n\nTo enable use `{pre}antinuke enable`"
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT user_id FROM whitelisted_users WHERE guild_id = ?",
|
||||
(ctx.guild.id,)
|
||||
) as cursor:
|
||||
data = await cursor.fetchall()
|
||||
|
||||
|
||||
if not data:
|
||||
view = CV2(f"{CROSS} Error", "No whitelisted users found.")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
await self.db.execute("DELETE FROM whitelisted_users WHERE guild_id = ?", (ctx.guild.id,))
|
||||
await self.db.commit()
|
||||
view = CV2(f"{TICK} Success", f"Removed all whitelisted members from {ctx.guild.name}")
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
239
bot/cogs/commands/antinuke.py
Normal file
239
bot/cogs/commands/antinuke.py
Normal file
@@ -0,0 +1,239 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, EMOTE, TICK, ZSAFE, ZSETTINGS
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container, Button, ActionRow
|
||||
import aiosqlite
|
||||
import asyncio
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
from utils.config import *
|
||||
|
||||
|
||||
|
||||
|
||||
class Antinuke(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.initialize_db())
|
||||
|
||||
async def initialize_db(self):
|
||||
self.db = await aiosqlite.connect('db/anti.db')
|
||||
await self.db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS antinuke (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
status BOOLEAN
|
||||
)
|
||||
''')
|
||||
await self.db.commit()
|
||||
|
||||
|
||||
async def enable_limit_settings(self, guild_id):
|
||||
default_limits = DEFAULT_LIMITS
|
||||
for action, limit in default_limits.items():
|
||||
await self.db.execute('INSERT OR REPLACE INTO limit_settings (guild_id, action_type, action_limit, time_window) VALUES (?, ?, ?, ?)', (guild_id, action, limit, TIME_WINDOW))
|
||||
await self.db.commit()
|
||||
|
||||
async def disable_limit_settings(self, guild_id):
|
||||
await self.db.execute('DELETE FROM limit_settings WHERE guild_id = ?', (guild_id,))
|
||||
await self.db.commit()
|
||||
|
||||
|
||||
@commands.hybrid_command(name='antinuke', aliases=['antiwizz', 'anti'], help="Enables/Disables Anti-Nuke Module in the server")
|
||||
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def antinuke(self, ctx, option: str = None):
|
||||
guild_id = ctx.guild.id
|
||||
pre=ctx.prefix
|
||||
|
||||
async with self.db.execute('SELECT status FROM antinuke WHERE guild_id = ?', (guild_id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT owner_id FROM extraowners WHERE guild_id = ? AND owner_id = ?",
|
||||
(ctx.guild.id, ctx.author.id)
|
||||
) as cursor:
|
||||
check = await cursor.fetchone()
|
||||
|
||||
is_owner = ctx.author.id == ctx.guild.owner_id
|
||||
if not is_owner and not check:
|
||||
view = CV2(f"{CROSS} Access Denied", "Only Server Owner or Extra Owner can Run this Command!")
|
||||
return await ctx.send(view=view)
|
||||
|
||||
is_activated = row[0] if row else False
|
||||
|
||||
if option is None:
|
||||
view = CV2(
|
||||
f"{ZSAFE} {BRAND_NAME} Security",
|
||||
"**Antinuke Defense Mode** — Protect your server from harmful admin actions with smart automated security protocols.",
|
||||
"**Core Functionalities**\n"
|
||||
"• Auto-ban malicious admin activities instantly.\n"
|
||||
"• Whitelist protection for trusted users.\n"
|
||||
"• Live monitoring of admin actions.\n"
|
||||
"• Rapid threat detection & neutralization.",
|
||||
"**Configuration Panel**\n"
|
||||
f"{TICK} Enable Protection: `antinuke enable`\n"
|
||||
f"{CROSS} Disable Protection: `antinuke disable`"
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
|
||||
elif option.lower() == 'enable':
|
||||
if is_activated:
|
||||
view = CV2(
|
||||
f"Security Settings For {ctx.guild.name}",
|
||||
f"Your server __**already has Antinuke enabled.**__\n\nCurrent Status: {TICK} Enabled\nTo Disable use `antinuke disable`"
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
else:
|
||||
|
||||
setup_view = CV2(f"Antinuke Setup {EMOTE}", f"{TICK} | Initializing Quick Setup!")
|
||||
setup_message = await ctx.send(view=setup_view)
|
||||
|
||||
|
||||
if not ctx.guild.me.guild_permissions.administrator:
|
||||
view = CV2(f"Antinuke Setup {EMOTE}",
|
||||
f"{TICK} | Initializing Quick Setup!\n"
|
||||
f"{CROSS} | **Ops! It seems I Don't Have Administrator Perm To enable antinuke**.")
|
||||
await setup_message.edit(view=view)
|
||||
return
|
||||
|
||||
await asyncio.sleep(1)
|
||||
view = CV2(f"Antinuke Setup {EMOTE}",
|
||||
f"{TICK} | Initializing Quick Setup!\n"
|
||||
f"{TICK} Checking {BRAND_NAME}'s role position for optimal configuration...")
|
||||
await setup_message.edit(view=view)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
view = CV2(f"Antinuke Setup {EMOTE}",
|
||||
f"{TICK} | Initializing Quick Setup!\n"
|
||||
f"{TICK} Checking {BRAND_NAME}'s role position for optimal configuration...\n"
|
||||
f"{TICK} | Crafting and configuring the {BRAND_NAME} Supreme role...")
|
||||
await setup_message.edit(view=view)
|
||||
|
||||
try:
|
||||
role = await ctx.guild.create_role(
|
||||
name=f"{BRAND_NAME} Supreme™",
|
||||
color=0xFF0000,
|
||||
permissions=discord.Permissions(administrator=True),
|
||||
hoist=False,
|
||||
mentionable=False,
|
||||
reason="Antinuke setup Role Creation"
|
||||
)
|
||||
await ctx.guild.me.add_roles(role)
|
||||
except discord.Forbidden:
|
||||
view = CV2("Antinuke Setup", f"{CROSS} | **Uh oh! I don't Have perms to enable antinuke**.")
|
||||
await setup_message.edit(view=view)
|
||||
return
|
||||
except discord.HTTPException as e:
|
||||
view = CV2("Antinuke Setup", f"{CROSS} | **Uh: HTTPException: {e}\nCheck Guild Audit Logs**.")
|
||||
await setup_message.edit(view=view)
|
||||
return
|
||||
|
||||
await asyncio.sleep(1)
|
||||
view = CV2(f"Antinuke Setup {EMOTE}",
|
||||
f"{TICK} | Initializing Quick Setup!\n"
|
||||
f"{TICK} Checking {BRAND_NAME}'s role position...\n"
|
||||
f"{TICK} | Crafting the {BRAND_NAME} Supreme role...\n"
|
||||
f"{TICK} | Ensuring precise placement of the {BRAND_NAME} Supreme™ role...")
|
||||
await setup_message.edit(view=view)
|
||||
|
||||
try:
|
||||
await ctx.guild.edit_role_positions(positions={role: 1})
|
||||
except discord.Forbidden:
|
||||
view = CV2("Antinuke Setup", f"{CROSS} | Ops! I don't have sufficient perms to move role.")
|
||||
await setup_message.edit(view=view)
|
||||
return
|
||||
except discord.HTTPException as e:
|
||||
view = CV2("Antinuke Setup", f"{CROSS} | Setup failed: HTTPException: {e}.")
|
||||
await setup_message.edit(view=view)
|
||||
return
|
||||
|
||||
await asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await self.db.execute('INSERT OR REPLACE INTO antinuke (guild_id, status) VALUES (?, ?)', (guild_id, True))
|
||||
await self.db.commit()
|
||||
|
||||
await asyncio.sleep(1)
|
||||
await setup_message.delete()
|
||||
|
||||
modules = (
|
||||
f"{TICK} **Anti Ban**\n"
|
||||
f"{TICK} **Anti Kick**\n"
|
||||
f"{TICK} **Anti Bot**\n"
|
||||
f"{TICK} **Anti Channel Create**\n"
|
||||
f"{TICK} **Anti Channel Delete**\n"
|
||||
f"{TICK} **Anti Channel Update**\n"
|
||||
f"{TICK} **Anti Everyone/Here**\n"
|
||||
f"{TICK} **Anti Role Create**\n"
|
||||
f"{TICK} **Anti Role Delete**\n"
|
||||
f"{TICK} **Anti Role Update**\n"
|
||||
f"{TICK} **Anti Member Update**\n"
|
||||
f"{TICK} **Anti Guild Update**\n"
|
||||
f"{TICK} **Anti Integration**\n"
|
||||
f"{TICK} **Anti Webhook Create**\n"
|
||||
f"{TICK} **Anti Webhook Delete**\n"
|
||||
f"{TICK} **Anti Webhook Update**\n"
|
||||
f"{TICK} **Anti Prune**\n"
|
||||
f"{TICK} **Auto Recovery**"
|
||||
)
|
||||
|
||||
punishment_btn = Button(label="Show Punishment Type", style=discord.ButtonStyle.secondary)
|
||||
punishment_btn.callback = self._show_punishment
|
||||
|
||||
result_view = LayoutView(timeout=None)
|
||||
result_view.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{ZSETTINGS} Security Settings For {ctx.guild.name}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("Tip: For optimal functionality, please ensure that my role has **Administration** permissions and is positioned at the **Top** of the roles list."),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"**Modules Enabled**\n{modules}"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"Successfully Enabled Antinuke | Powered by {BRAND_NAME} Development™"),
|
||||
ActionRow(punishment_btn)
|
||||
)
|
||||
)
|
||||
|
||||
await ctx.send(view=result_view)
|
||||
|
||||
elif option.lower() == 'disable':
|
||||
if not is_activated:
|
||||
view = CV2(
|
||||
f"Security Settings For {ctx.guild.name}",
|
||||
f"Uhh, looks like your server hasn't enabled Antinuke.\n\nCurrent Status: {CROSS} Disabled\n\nTo Enable use `antinuke enable`"
|
||||
)
|
||||
else:
|
||||
await self.db.execute('DELETE FROM antinuke WHERE guild_id = ?', (guild_id,))
|
||||
await self.db.commit()
|
||||
view = CV2(
|
||||
f"Security Settings For {ctx.guild.name}",
|
||||
f"Successfully disabled Antinuke for this server.\n\nCurrent Status: {CROSS} Disabled\n\nTo Enable use `antinuke enable`"
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
else:
|
||||
view = CV2(f"{CROSS} Error", "Invalid option. Please use `enable` or `disable`.")
|
||||
await ctx.send(view=view)
|
||||
|
||||
async def _show_punishment(self, interaction: discord.Interaction):
|
||||
view = CV2(
|
||||
"Punishment Types for Unwhitelisted Admins/Mods",
|
||||
"**Anti Ban:** Ban\n"
|
||||
"**Anti Kick:** Ban\n"
|
||||
"**Anti Bot:** Ban the bot Inviter\n"
|
||||
"**Anti Channel Create/Delete/Update:** Ban\n"
|
||||
"**Anti Everyone/Here:** Remove the message & 1 hour timeout\n"
|
||||
"**Anti Role Create/Delete/Update:** Ban\n"
|
||||
"**Anti Member Update:** Ban\n"
|
||||
"**Anti Guild Update:** Ban\n"
|
||||
"**Anti Integration:** Ban\n"
|
||||
"**Anti Webhook Create/Delete/Update:** Ban\n"
|
||||
"**Anti Prune:** Ban\n"
|
||||
"**Auto Recovery:** Automatically recover damaged channels, roles, and settings",
|
||||
"Note: In the case of member updates, action will be taken only if the role contains dangerous permissions such as Ban Members, Administrator, Manage Guild, Manage Channels, Manage Roles, Manage Webhooks, or Mention Everyone"
|
||||
)
|
||||
await interaction.response.send_message(view=view, ephemeral=True)
|
||||
803
bot/cogs/commands/automod.py
Normal file
803
bot/cogs/commands/automod.py
Normal file
@@ -0,0 +1,803 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, DISABLE, ENABLE, TICK, TICK_ALT
|
||||
from discord.ext import commands
|
||||
import aiosqlite
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container, ActionRow
|
||||
from utils.config import *
|
||||
class CV2(LayoutView):
|
||||
def __init__(self, title, *sections):
|
||||
super().__init__(timeout=None)
|
||||
items = [TextDisplay(f"**{title}**")]
|
||||
for s in sections:
|
||||
if s:
|
||||
items.append(Separator(visible=True))
|
||||
items.append(TextDisplay(str(s)))
|
||||
self.add_item(build_container(*items))
|
||||
|
||||
|
||||
class ShowRules(LayoutView):
|
||||
def __init__(self, author, selected_events, title, *sections):
|
||||
super().__init__(timeout=60)
|
||||
self.author = author
|
||||
self.selected_events = selected_events
|
||||
|
||||
show_rules_btn = discord.ui.Button(label="Show Rules", style=discord.ButtonStyle.secondary)
|
||||
show_rules_btn.callback = self._show_rules_callback
|
||||
|
||||
items = [TextDisplay(f"**{title}**")]
|
||||
for s in sections:
|
||||
if s:
|
||||
items.append(Separator(visible=True))
|
||||
items.append(TextDisplay(str(s)))
|
||||
items.append(ActionRow(show_rules_btn))
|
||||
self.add_item(build_container(*items))
|
||||
|
||||
async def _show_rules_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user != self.author:
|
||||
await interaction.response.send_message("You are not allowed to interact with this button.", ephemeral=True)
|
||||
return
|
||||
|
||||
rules = {
|
||||
"Anti NSFW link": "__**Anti NSFW Link**__:\n• Takes action if the message contains a NSFW link.\n• Default punishment: Block message (unchangeable)",
|
||||
"Anti caps": "__**Anti Caps**__:\n• Takes action if the message contains >70% caps.\n• Messages under 45 characters are bypassed\n• Default punishment: Mute (1 minutes)",
|
||||
"Anti link": "__**Anti Link**__:\n• Takes action if the message contains a link.\n• Server invites, Spotify Music and GIF links are bypassed\n• Default punishment: Mute (7 minutes)",
|
||||
"Anti invites": "__**Anti Invites**__:\n• Takes action if the message contains a Discord server invite.\n• Invites from the current server are bypassed\n• Default punishment: Mute (12 minutes)",
|
||||
"Anti emoji spam": "__**Anti Emoji Spam**__:\n• Takes action if a message contains more than 5 emojis.\n• Default punishment: Mute (1 minute)",
|
||||
"Anti mass mention": "__**Anti Mass Mention**__:\n• Takes action if a message contains more than 4 mentions.\n• Default punishment: Mute (3 minutes)",
|
||||
"Anti spam": "__**Anti Spam**__:\n• Takes action if more than 5 messages are sent rapidly in a short time.\n• Default punishment: Mute (12 minutes)",
|
||||
}
|
||||
|
||||
enabled_rules = "\n\n".join([rules[event] for event in self.selected_events if event in rules])
|
||||
|
||||
view = CV2("Enabled Automod Rules", enabled_rules, "Punishment type of each event is changeable except for Anti NSFW.")
|
||||
await interaction.response.send_message(view=view, ephemeral=True)
|
||||
|
||||
|
||||
class ConfirmDisable(LayoutView):
|
||||
def __init__(self, author, title, *sections):
|
||||
super().__init__(timeout=30)
|
||||
self.author = author
|
||||
self.value = None
|
||||
|
||||
yes_btn = discord.ui.Button(label="Yes", style=discord.ButtonStyle.danger)
|
||||
yes_btn.callback = self._confirm_callback
|
||||
no_btn = discord.ui.Button(label="No", style=discord.ButtonStyle.secondary)
|
||||
no_btn.callback = self._cancel_callback
|
||||
|
||||
items = [TextDisplay(f"**{title}**")]
|
||||
for s in sections:
|
||||
if s:
|
||||
items.append(Separator(visible=True))
|
||||
items.append(TextDisplay(str(s)))
|
||||
items.append(ActionRow(yes_btn, no_btn))
|
||||
self.add_item(build_container(*items))
|
||||
|
||||
async def _confirm_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user != self.author:
|
||||
await interaction.response.send_message("You are not allowed to interact with this button.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
self.value = True
|
||||
self.stop()
|
||||
|
||||
async def _cancel_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user != self.author:
|
||||
await interaction.response.send_message("You are not allowed to interact with this button.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
self.value = False
|
||||
self.stop()
|
||||
|
||||
|
||||
|
||||
class Automod(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.default_punishment = "Mute"
|
||||
self.bot.loop.create_task(self.init_db())
|
||||
|
||||
async def get_exempt_roles_channels(self, guild_id):
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
roles_cursor = await db.execute("SELECT id FROM automod_ignored WHERE guild_id = ? AND type = 'role'", (guild_id,))
|
||||
channels_cursor = await db.execute("SELECT id FROM automod_ignored WHERE guild_id = ? AND type = 'channel'", (guild_id,))
|
||||
|
||||
exempt_roles = [discord.Object(id) for (id,) in await roles_cursor.fetchall()]
|
||||
exempt_channels = [discord.Object(id) for (id,) in await channels_cursor.fetchall()]
|
||||
|
||||
return exempt_roles, exempt_channels
|
||||
|
||||
|
||||
async def is_automod_enabled(self, guild_id):
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
cursor = await db.execute("SELECT enabled FROM automod WHERE guild_id = ?", (guild_id,))
|
||||
result = await cursor.fetchone()
|
||||
return result is not None and result[0] == 1
|
||||
|
||||
async def update_punishments(self, guild_id, event, punishment):
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
await db.execute("INSERT OR REPLACE INTO automod_punishments (guild_id, event, punishment) VALUES (?, ?, ?)", (guild_id, event, punishment))
|
||||
await db.commit()
|
||||
|
||||
async def get_current_punishments(self, guild_id):
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
async with db.execute(
|
||||
"SELECT event, punishment FROM automod_punishments WHERE guild_id = ? AND event != 'Anti NSFW link'",
|
||||
(guild_id,)
|
||||
) as cursor:
|
||||
return await cursor.fetchall()
|
||||
|
||||
async def is_anti_nsfw_enabled(self, guild_id):
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
cursor = await db.execute("SELECT punishment FROM automod_punishments WHERE guild_id = ? AND event = 'Anti NSFW link'", (guild_id,))
|
||||
result = await cursor.fetchone()
|
||||
return result is not None
|
||||
|
||||
|
||||
|
||||
async def init_db(self):
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS automod (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
enabled INTEGER DEFAULT 0
|
||||
)
|
||||
""")
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS automod_punishments (
|
||||
guild_id INTEGER,
|
||||
event TEXT,
|
||||
punishment TEXT,
|
||||
PRIMARY KEY (guild_id, event)
|
||||
)
|
||||
""")
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS automod_ignored (
|
||||
guild_id INTEGER,
|
||||
type TEXT,
|
||||
id INTEGER,
|
||||
PRIMARY KEY (guild_id, type, id)
|
||||
)
|
||||
""")
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS automod_logging (
|
||||
guild_id INTEGER,
|
||||
log_channel INTEGER,
|
||||
PRIMARY KEY (guild_id)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
@commands.hybrid_group(invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
async def automod(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@automod.command(name="enable", help="Enable Automod on the server.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.bot_has_permissions(manage_guild=True)
|
||||
async def enable(self, ctx):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"**{CROSS} Your Server already has Automoderation Enabled.**\n\nCurrent Status: {ENABLE} Enabled\nTo Disable use `{ctx.prefix}automod disable`"))
|
||||
return
|
||||
|
||||
events = [
|
||||
"Anti spam",
|
||||
"Anti caps",
|
||||
"Anti link",
|
||||
"Anti invites",
|
||||
"Anti mass mention",
|
||||
"Anti emoji spam",
|
||||
"Anti NSFW link",
|
||||
]
|
||||
|
||||
select_menu = discord.ui.Select(placeholder="Select events to enable", min_values=1, max_values=len(events), options=[
|
||||
discord.SelectOption(label=event, value=event) for event in events
|
||||
])
|
||||
|
||||
async def select_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not allowed to interact with this menu.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
selected_events = select_menu.values
|
||||
await self.enable_automod(ctx, guild_id, selected_events, interaction)
|
||||
select_menu.callback = select_callback
|
||||
|
||||
enable_all_button = discord.ui.Button(label="Enable for All Events", style=discord.ButtonStyle.primary)
|
||||
|
||||
async def enable_all_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not allowed to interact with this button.", ephemeral=True)
|
||||
return
|
||||
|
||||
await interaction.response.defer()
|
||||
await self.enable_automod(ctx, guild_id, events, interaction)
|
||||
|
||||
enable_all_button.callback = enable_all_callback
|
||||
|
||||
cancel_button = discord.ui.Button(label="Cancel", style=discord.ButtonStyle.danger)
|
||||
|
||||
async def cancel_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not allowed to interact with this button.", ephemeral=True)
|
||||
return
|
||||
cancel_view = CV2("Automod Setup Cancelled", "The automod setup has been cancelled.")
|
||||
await interaction.response.edit_message(view=cancel_view)
|
||||
|
||||
cancel_button.callback = cancel_callback
|
||||
|
||||
events_text = "\n".join([f"{DISABLE} : {event}" for event in events])
|
||||
view = LayoutView(timeout=60)
|
||||
view.add_item(build_container(
|
||||
TextDisplay(f"**{ctx.guild.name}'s Automod Setup**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(events_text),
|
||||
ActionRow(select_menu),
|
||||
ActionRow(enable_all_button, cancel_button)
|
||||
))
|
||||
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
async def enable_automod(self, ctx, guild_id, selected_events, interaction):
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
await db.execute("INSERT OR REPLACE INTO automod (guild_id, enabled) VALUES (?, 1)", (guild_id,))
|
||||
for event in selected_events:
|
||||
await db.execute("INSERT OR REPLACE INTO automod_punishments (guild_id, event, punishment) VALUES (?, ?, ?)", (guild_id, event, self.default_punishment))
|
||||
await db.commit()
|
||||
|
||||
|
||||
if "Anti NSFW link" in selected_events:
|
||||
exempt_roles, exempt_channels = await self.get_exempt_roles_channels(guild_id)
|
||||
nsfw_keywords = ["porn", "xxx", "adult", "sex", "nsfw", "xnxx", "onlyfans", "brazzers", "xhamster", "xvideos", "pornhub", "redtube", "livejasmin", "youporn" , "tube8", "pornhat", "swxvid", "ixxx", "pornhat"]
|
||||
|
||||
try:
|
||||
await interaction.guild.create_automod_rule(
|
||||
name="Anti NSFW Links",
|
||||
event_type=discord.AutoModRuleEventType.message_send,
|
||||
trigger=discord.AutoModTrigger(
|
||||
type=discord.AutoModRuleTriggerType.keyword,
|
||||
keyword_filter=nsfw_keywords,
|
||||
),
|
||||
actions=[
|
||||
discord.AutoModRuleAction(type=discord.AutoModRuleActionType.block_message),
|
||||
],
|
||||
enabled=True,
|
||||
exempt_roles=exempt_roles,
|
||||
exempt_channels=exempt_channels,
|
||||
reason="Automod - Anti NSFW Link setup"
|
||||
)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException as e:
|
||||
print(f"Automod rule-create error: {e}")
|
||||
|
||||
|
||||
enable_logging_button = discord.ui.Button(label="Enable Automod Logging", style=discord.ButtonStyle.success)
|
||||
|
||||
async def enable_logging_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not allowed to interact with this button.", ephemeral=True)
|
||||
return
|
||||
|
||||
if not interaction.guild.me.guild_permissions.manage_channels:
|
||||
await interaction.response.send_message("I do not have permission to create channels.", ephemeral=True)
|
||||
return
|
||||
|
||||
overwrites = {
|
||||
interaction.guild.default_role: discord.PermissionOverwrite(view_channel=False),
|
||||
interaction.guild.me: discord.PermissionOverwrite(view_channel=True)
|
||||
}
|
||||
|
||||
try:
|
||||
for channel in interaction.guild.channels:
|
||||
if channel.name == f"{BRAND_NAME.lower()}-automod":
|
||||
await interaction.response.send_message(f"A logging channel with the name \"{BRAND_NAME.lower()}-automod\" already exists.", ephemeral=True)
|
||||
return
|
||||
log_channel = await interaction.guild.create_text_channel(f"{BRAND_NAME.lower()}-automod", overwrites=overwrites)
|
||||
guild_id = interaction.guild.id
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
await db.execute("INSERT OR REPLACE INTO automod_logging (guild_id, log_channel) VALUES (?, ?)", (guild_id, log_channel.id))
|
||||
await db.commit()
|
||||
|
||||
await interaction.response.send_message(f"Logging channel {log_channel.mention} created and set successfully.", ephemeral=True)
|
||||
|
||||
except discord.HTTPException as e:
|
||||
await interaction.response.send_message(f"Failed to create logging channel: {e}", ephemeral=True)
|
||||
|
||||
enable_logging_button.callback = enable_logging_callback
|
||||
|
||||
events_text = "\n".join([f"{ENABLE} : {event}" for event in selected_events] + [f"{CROSS}{TICK} : {event}" for event in ["Anti spam", "Anti caps", "Anti link", "Anti invites", "Anti mass mention", "Anti emoji spam", "Anti NSFW link"] if event not in selected_events])
|
||||
view = ShowRules(ctx.author, selected_events, "Automod Enabled Successfully", events_text)
|
||||
|
||||
await interaction.edit_original_response(content=None, view=view)
|
||||
|
||||
|
||||
|
||||
|
||||
@automod.command(name="punishment", aliases=["punish"], help="Set the punishment for automod events.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def punishment(self, ctx):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
current_punishments = await self.get_current_punishments(guild_id)
|
||||
punishments_list = "\n".join([f"**{event}**: {punishment or 'None'}" for event, punishment in current_punishments])
|
||||
view_title = f"Current Automod Punishments for {ctx.guild.name}"
|
||||
view_desc = punishments_list + "\n\nKeep the default punishment (Mute) to prevent server raids without kicking or banning raiders"
|
||||
|
||||
events = [event for event, _ in current_punishments]
|
||||
select = discord.ui.Select(placeholder="Select events to update punishment", options=[
|
||||
discord.SelectOption(label=event) for event in events
|
||||
], min_values=1, max_values=len(events))
|
||||
|
||||
async def select_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You cannot interact with this menu.", ephemeral=True)
|
||||
return
|
||||
|
||||
selected_events = select.values
|
||||
await interaction.response.send_message("You selected: " + ", ".join(selected_events))
|
||||
|
||||
punishment_buttons = discord.ui.View()
|
||||
for punishment in ["Mute", "Kick", "Ban"]:
|
||||
button = discord.ui.Button(label=punishment, style=discord.ButtonStyle.danger)
|
||||
|
||||
async def punishment_callback(button_interaction, selected_events=selected_events, punishment=punishment):
|
||||
if button_interaction.user != ctx.author:
|
||||
await button_interaction.response.send_message("You cannot interact with this button.", ephemeral=True)
|
||||
return
|
||||
|
||||
|
||||
for event in selected_events:
|
||||
await self.update_punishments(guild_id, event, punishment)
|
||||
|
||||
updated_punishments = await self.get_current_punishments(guild_id)
|
||||
updated_list = "\n".join([f"**{event}**: {punishment or 'None'}" for event, punishment in updated_punishments])
|
||||
await button_interaction.response.edit_message(view=CV2(f"Updated Automod Punishments for {ctx.guild.name}", updated_list, "You can modify the punishments by running the command again."))
|
||||
|
||||
button.callback = punishment_callback
|
||||
punishment_buttons.add_item(button)
|
||||
|
||||
await interaction.edit_original_response(view=punishment_buttons)
|
||||
|
||||
select.callback = select_callback
|
||||
view = CV2(view_title, view_desc)
|
||||
view.add_item(select)
|
||||
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
|
||||
|
||||
@automod.group(name="ignore", aliases=["exempt", "whitelist", "wl"], help="Manage whitelisted roles and channels for Automod.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
async def ignore(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
|
||||
@ignore.command(name="channel", help="Add a channel to the whitelist.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def ignore_channel(self, ctx, channel: discord.TextChannel):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
cursor = await db.execute("SELECT 1 FROM automod_ignored WHERE guild_id = ? AND type = 'channel' AND id = ?", (guild_id, channel.id))
|
||||
if await cursor.fetchone() is not None:
|
||||
await ctx.send(view=CV2("__Channel Already Whitelisted!__", f"{CROSS} The channel {channel.mention} is already in the ignore list.\n\n➜ Use **{ctx.prefix}automod unignore channel {channel.mention}** to remove it."))
|
||||
return
|
||||
|
||||
count_cursor = await db.execute("SELECT COUNT(*) FROM automod_ignored WHERE guild_id = ? AND type = 'channel'", (guild_id,))
|
||||
count = await count_cursor.fetchone()
|
||||
|
||||
if count[0] >= 10:
|
||||
await ctx.send("You can only ignore up to 10 channels.")
|
||||
return
|
||||
|
||||
await db.execute("INSERT OR REPLACE INTO automod_ignored (guild_id, type, id) VALUES (?, 'channel', ?)", (guild_id, channel.id))
|
||||
await db.commit()
|
||||
|
||||
if await self.is_anti_nsfw_enabled(guild_id):
|
||||
try:
|
||||
rules = await ctx.guild.fetch_automod_rules()
|
||||
for rule in rules:
|
||||
if rule.name == "Anti NSFW Links":
|
||||
exempt_channels = list(rule.exempt_channels)
|
||||
exempt_channels.append(channel)
|
||||
await rule.edit(
|
||||
exempt_channels=exempt_channels,
|
||||
reason="Channel exempted from Anti NSFW Links via automod ignore command"
|
||||
)
|
||||
break
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
|
||||
await ctx.send(view=CV2(f"{TICK} Channel Whitelisted", f"The channel {channel.mention} has been added to the ignore list \n\n➜ Use `{ctx.prefix}automod ignore show` to view the ignore list."))
|
||||
|
||||
@ignore.command(name="role", help="Add a role to the whitelist.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def ignore_role(self, ctx, role: discord.Role):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
cursor = await db.execute("SELECT 1 FROM automod_ignored WHERE guild_id = ? AND type = 'role' AND id = ?", (guild_id, role.id))
|
||||
|
||||
if await cursor.fetchone() is not None:
|
||||
await ctx.send(view=CV2("__Role Already Whitelisted!__", f"{CROSS} The role {role.mention} is already in the ignore list.\n\n➜ Use **{ctx.prefix}automod unignore role {role.mention}** to remove it."))
|
||||
return
|
||||
|
||||
count_cursor = await db.execute("SELECT COUNT(*) FROM automod_ignored WHERE guild_id = ? AND type = 'role'", (guild_id,))
|
||||
count = await count_cursor.fetchone()
|
||||
|
||||
|
||||
if count[0] >= 10:
|
||||
await ctx.send("You can only ignore up to 10 roles.")
|
||||
return
|
||||
|
||||
await db.execute("INSERT OR REPLACE INTO automod_ignored (guild_id, type, id) VALUES (?, 'role', ?)", (guild_id, role.id))
|
||||
await db.commit()
|
||||
|
||||
if await self.is_anti_nsfw_enabled(guild_id):
|
||||
try:
|
||||
rules = await ctx.guild.fetch_automod_rules()
|
||||
for rule in rules:
|
||||
if rule.name == "Anti NSFW Links":
|
||||
exempt_roles = list(rule.exempt_roles)
|
||||
exempt_roles.append(role)
|
||||
await rule.edit(
|
||||
exempt_roles=exempt_roles,
|
||||
reason="Role exempted from Anti NSFW Links via automod ignore command"
|
||||
)
|
||||
break
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
|
||||
await ctx.send(view=CV2(f"{TICK} Role Whitelisted", f"The role {role.mention} has been added to the ignore list \n\n➜ Use `{ctx.prefix}automod ignore show` to view the ignore list."))
|
||||
|
||||
@ignore.command(name="show", aliases=["view", "list", "config"], help="Show the whitelisted roles and channels.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def ignore_show(self, ctx):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
cursor = await db.execute("SELECT type, id FROM automod_ignored WHERE guild_id = ?", (guild_id,))
|
||||
ignored_items = await cursor.fetchall()
|
||||
|
||||
if not ignored_items:
|
||||
await ctx.reply("No ignored channels or roles found.")
|
||||
return
|
||||
|
||||
ignored_channels = []
|
||||
ignored_roles = []
|
||||
|
||||
for item_type, item_id in ignored_items:
|
||||
if item_type == "channel":
|
||||
channel = ctx.guild.get_channel(item_id)
|
||||
if channel:
|
||||
ignored_channels.append(f"{channel.mention} (ID: {channel.id})")
|
||||
else:
|
||||
ignored_channels.append(f"Deleted Channel (ID: {item_id})")
|
||||
elif item_type == "role":
|
||||
role = ctx.guild.get_role(item_id)
|
||||
if role:
|
||||
ignored_roles.append(f"{role.mention} (ID: {role.id})")
|
||||
else:
|
||||
ignored_roles.append(f"Deleted Role (ID: {item_id})")
|
||||
|
||||
ch_str = "\n".join(ignored_channels) if ignored_channels else "None"
|
||||
rl_str = "\n".join(ignored_roles) if ignored_roles else "None"
|
||||
await ctx.send(view=CV2("Ignored Channels & Roles for Automod", f"__Ignored Channels:__\n{ch_str}", f"__Ignored Roles:__\n{rl_str}"))
|
||||
|
||||
|
||||
@ignore.command(name="reset", help="Reset the whitelist.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def ignore_reset(self, ctx):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
await db.execute("DELETE FROM automod_ignored WHERE guild_id = ?", (guild_id,))
|
||||
await db.commit()
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"** {TICK} | All ignored channels and roles have been reset!**\n\nTo view current Automod settings use `{ctx.prefix}automod config`"))
|
||||
|
||||
|
||||
@automod.group(name="unignore", aliases=["unwhitelist", "unwl"], invoke_without_command=True, help="Remove channels and roles from the whitelist.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
async def unignore(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@unignore.command(name="channel", help="Remove a channel from the whitelist.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def unignore_channel(self, ctx, channel: discord.TextChannel):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
if await self.is_anti_nsfw_enabled(guild_id):
|
||||
try:
|
||||
rules = await ctx.guild.fetch_automod_rules()
|
||||
for rule in rules:
|
||||
if rule.name == "Anti NSFW Links":
|
||||
exempt_channels = list(rule.exempt_channels)
|
||||
exempt_channels = [ch for ch in exempt_channels if ch.id != channel.id]
|
||||
await rule.edit(
|
||||
exempt_channels=exempt_channels,
|
||||
reason="Channel removed from Anti NSFW Links exemption via automod unignore command"
|
||||
)
|
||||
break
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
result = await db.execute("DELETE FROM automod_ignored WHERE guild_id = ? AND type = 'channel' AND id = ?", (guild_id, channel.id))
|
||||
await db.commit()
|
||||
|
||||
if result.rowcount > 0:
|
||||
await ctx.send(view=CV2(f"{TICK} Success", f"{channel.mention} has been removed from the automod ignore list."))
|
||||
else:
|
||||
await ctx.send(view=CV2(f"{CROSS} Error", f"{channel.mention} is not in the automod ignore list."))
|
||||
|
||||
|
||||
@unignore.command(name="role", help="Remove a role from the whitelist.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def unignore_role(self, ctx, role: discord.Role):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
if await self.is_anti_nsfw_enabled(guild_id):
|
||||
try:
|
||||
rules = await ctx.guild.fetch_automod_rules()
|
||||
for rule in rules:
|
||||
if rule.name == "Anti NSFW Links":
|
||||
exempt_roles = list(rule.exempt_roles)
|
||||
exempt_roles = [ch for ch in exempt_roles if ch.id != role.id]
|
||||
await rule.edit(
|
||||
exempt_roles=exempt_roles,
|
||||
reason="Role removed from Anti NSFW Links exemption via automod unignore command"
|
||||
)
|
||||
break
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
result = await db.execute("DELETE FROM automod_ignored WHERE guild_id = ? AND type = 'role' AND id = ?", (guild_id, role.id))
|
||||
await db.commit()
|
||||
|
||||
if result.rowcount > 0:
|
||||
await ctx.send(view=CV2(f"{TICK} Success", f"{role.mention} has been removed from the automod ignore list."))
|
||||
else:
|
||||
await ctx.send(view=CV2(f"{CROSS} Error", f"{role.mention} is not in the automod ignore list."))
|
||||
|
||||
|
||||
@automod.command(name="disable", help="Disable Automod in the server.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def disable(self, ctx):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
view = ConfirmDisable(ctx.author, "Disable Automod Confirmation", "**Are you sure you want to disable Automod?**\n\nThis will remove all custom event settings, punishments, ignored roles/channels, & logging channel data.", "Click 'Yes' to disable Automod or 'No' to cancel.")
|
||||
message = await ctx.send(view=view)
|
||||
|
||||
await view.wait()
|
||||
|
||||
if view.value is None:
|
||||
await message.edit(view=CV2("Automod Disable Cancelled", "You took too long to respond. Automod disable process has been canceled."))
|
||||
|
||||
elif view.value:
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
await db.execute("DELETE FROM automod WHERE guild_id = ?", (guild_id,))
|
||||
await db.execute("DELETE FROM automod_punishments WHERE guild_id = ?", (guild_id,))
|
||||
await db.execute("DELETE FROM automod_ignored WHERE guild_id = ?", (guild_id,))
|
||||
await db.execute("DELETE FROM automod_logging WHERE guild_id = ?", (guild_id,))
|
||||
await db.commit()
|
||||
|
||||
rules = await ctx.guild.fetch_automod_rules()
|
||||
for rule in rules:
|
||||
if rule.name == "Anti NSFW Links":
|
||||
try:
|
||||
await rule.delete(reason="Automod disabled - removing Anti NSFW Link rule")
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
|
||||
await message.edit(view=CV2(f"{TICK_ALT} Automod Disabled", f"Automod has been successfully disabled for **{ctx.guild.name}.** \nAll settings, punishments, and logs have been removed.\n\nCurrent Status: {DISABLE} Disabled\n➜ To Re-enable use `{ctx.prefix}automod enable`."))
|
||||
|
||||
|
||||
else:
|
||||
await message.edit(view=CV2("Automod Disable Cancelled", "Automod disable process has been canceled."))
|
||||
|
||||
|
||||
|
||||
@automod.command(name="config", aliases=["settings", "show", "view"], help="View Automod settings.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def config(self, ctx):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
current_punishments = await self.get_current_punishments(guild_id)
|
||||
items_str = []
|
||||
for event, punishment in current_punishments:
|
||||
items_str.append(f"**{event}**: {punishment or 'None'}")
|
||||
if await self.is_anti_nsfw_enabled(guild_id):
|
||||
items_str.append("**Anti NSFW Links**: Block Message")
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
cursor = await db.execute("SELECT log_channel FROM automod_logging WHERE guild_id = ?", (guild_id,))
|
||||
log_channel_id = await cursor.fetchone()
|
||||
if log_channel_id and log_channel_id[0]:
|
||||
log_channel = ctx.guild.get_channel(log_channel_id[0])
|
||||
if log_channel:
|
||||
items_str.append(f"**Logging Channel**: {log_channel.mention}")
|
||||
else:
|
||||
items_str.append("**Logging Channel**: Deleted Channel")
|
||||
else:
|
||||
items_str.append("**Logging Channel**: No logging channel")
|
||||
|
||||
await ctx.send(view=CV2(f"Enabled Automod Events & their punishment type for {ctx.guild.name}", "\n".join(items_str), "Manage punishment type for events by executing `automod punishment` command."))
|
||||
|
||||
|
||||
@automod.command(name="logging", help="Set the logging channel for Automod events.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def logging(self, ctx, channel: discord.TextChannel):
|
||||
guild_id = ctx.guild.id
|
||||
if ctx.author != ctx.guild.owner and ctx.author.top_role.position < ctx.guild.me.top_role.position:
|
||||
await ctx.send(view=CV2(f"{CROSS} Access Denied", "Your top role must be at the **same** position or **higher** than my top role."))
|
||||
return
|
||||
if not await self.is_automod_enabled(guild_id):
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"Uhh, looks like your server hasn\'t enabled Automoderation.\n\nCurrent Status: {DISABLE} Disabled\nTo Enable use `{ctx.prefix}automod enable`"))
|
||||
return
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
await db.execute("INSERT OR REPLACE INTO automod_logging (guild_id, log_channel) VALUES (?, ?)", (guild_id, channel.id))
|
||||
await db.commit()
|
||||
await ctx.send(view=CV2(f"Automod Settings for {ctx.guild.name}", f"**{TICK} | Automoderation Logging channel set to {channel.mention}.**\n\n➜ Use `{ctx.prefix}automod config` to view current Automod settings."))
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_guild_remove(self, guild):
|
||||
guild_id = guild.id
|
||||
|
||||
async with aiosqlite.connect("db/automod.db") as db:
|
||||
await db.execute("DELETE FROM automod WHERE guild_id = ?", (guild_id,))
|
||||
await db.execute("DELETE FROM automod_punishments WHERE guild_id = ?", (guild_id,))
|
||||
await db.execute("DELETE FROM automod_ignored WHERE guild_id = ?", (guild_id,))
|
||||
await db.execute("DELETE FROM automod_logging WHERE guild_id = ?", (guild_id,))
|
||||
await db.commit()
|
||||
145
bot/cogs/commands/autoreact.py
Normal file
145
bot/cogs/commands/autoreact.py
Normal file
@@ -0,0 +1,145 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, ICONS_WARNING, TICK
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
import aiosqlite
|
||||
import re
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class AutoReaction(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.db_path = 'db/autoreact.db'
|
||||
self.bot.loop.create_task(self.setup_database())
|
||||
|
||||
async def setup_database(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS autoreact (
|
||||
guild_id INTEGER,
|
||||
trigger TEXT,
|
||||
emojis TEXT
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
async def get_triggers(self, guild_id):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute("SELECT trigger, emojis FROM autoreact WHERE guild_id = ?", (guild_id,))
|
||||
return await cursor.fetchall()
|
||||
|
||||
async def trigger_exists(self, guild_id, trigger):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute("SELECT 1 FROM autoreact WHERE guild_id = ? AND trigger = ?", (guild_id, trigger))
|
||||
return await cursor.fetchone()
|
||||
|
||||
@commands.group(name="react", aliases=["autoreact"], help="Lists all subcommands of autoreact group.", invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def react(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@react.command(name="add", aliases=["set", "create"], help="Adds a trigger and its emojis to the autoreact.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def add(self, ctx, trigger: str, *, emojis: str):
|
||||
if len(trigger.split()) > 1:
|
||||
view = CV2(f"{CROSS} Invalid Trigger", "Triggers can only be one word.")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
|
||||
emoji_list = re.findall(r"<a?:\w+:\d+>|[\u263a-\U0001f645]", emojis)
|
||||
if len(emoji_list) > 10:
|
||||
view = CV2(f"{CROSS} Too Many Emojis", "You can only set up to **10** emojis per trigger.")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
triggers = await self.get_triggers(ctx.guild.id)
|
||||
if len(triggers) >= 10:
|
||||
view = CV2(f"{ICONS_WARNING} Trigger Limit Reached", "You can only set up to 10 triggers for auto-reactions in this guild.")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
if await self.trigger_exists(ctx.guild.id, trigger):
|
||||
view = CV2(f"{ICONS_WARNING} Trigger Exists", f"The trigger '{trigger}' already exists. Remove it before adding it again.")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("INSERT INTO autoreact (guild_id, trigger, emojis) VALUES (?, ?, ?)",
|
||||
(ctx.guild.id, trigger, " ".join(emoji_list)))
|
||||
await db.commit()
|
||||
|
||||
view = CV2(f"{TICK} Trigger Added", f"Successfully added trigger '{trigger}' with emojis {', '.join(emoji_list)}.")
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@react.command(name="remove", aliases=["clear", "delete"], help="Removes a trigger and its emojis from the autoreact.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def remove(self, ctx, trigger: str):
|
||||
if not await self.trigger_exists(ctx.guild.id, trigger):
|
||||
view = CV2(f"{CROSS} Trigger Not Found", f"The trigger '{trigger}' does not exist.")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("DELETE FROM autoreact WHERE guild_id = ? AND trigger = ?", (ctx.guild.id, trigger))
|
||||
await db.commit()
|
||||
|
||||
view = CV2(f"{TICK} Trigger Removed", f"Successfully removed trigger '{trigger}'.")
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@react.command(name="list", aliases=["show", "config"], help="Lists all the triggers and their emojis in the autoreact module.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def list(self, ctx):
|
||||
triggers = await self.get_triggers(ctx.guild.id)
|
||||
if not triggers:
|
||||
view = CV2("No Triggers Set", "There are no auto-reaction triggers set in this guild.")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
trigger_list = "\n".join([f"**{t[0]}:** {t[1]}" for t in triggers])
|
||||
view = CV2("Auto-Reaction Triggers", trigger_list)
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@react.command(name="reset", help="Resets all the triggers and their emojis in the autoreact module.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def reset(self, ctx):
|
||||
triggers = await self.get_triggers(ctx.guild.id)
|
||||
if not triggers:
|
||||
view = CV2(f"{CROSS} No Triggers Set", "There are no auto-reaction triggers set to reset.")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("DELETE FROM autoreact WHERE guild_id = ?", (ctx.guild.id,))
|
||||
await db.commit()
|
||||
|
||||
view = CV2(f"{TICK} All Triggers Reset", "Successfully removed all auto-reaction triggers.")
|
||||
await ctx.reply(view=view)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(AutoReaction(bot))
|
||||
135
bot/cogs/commands/autoresponder.py
Normal file
135
bot/cogs/commands/autoresponder.py
Normal file
@@ -0,0 +1,135 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
import aiosqlite
|
||||
import os
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
DB_PATH = "db/autoresponder.db"
|
||||
|
||||
class AutoResponder(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.initialize_db())
|
||||
|
||||
async def initialize_db(self):
|
||||
if not os.path.exists(os.path.dirname(DB_PATH)):
|
||||
os.makedirs(os.path.dirname(DB_PATH))
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS autoresponses (
|
||||
guild_id INTEGER,
|
||||
name TEXT,
|
||||
message TEXT,
|
||||
PRIMARY KEY (guild_id, name)
|
||||
)
|
||||
''')
|
||||
await db.commit()
|
||||
|
||||
@commands.group(name="autoresponder", invoke_without_command=True, aliases=['ar'], help="Manage autoresponders in the server.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
async def _ar(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_ar.command(name="create", help="Create a new autoresponder.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _create(self, ctx, name, *, message):
|
||||
name_lower = name.lower()
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT COUNT(*) FROM autoresponses WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
count = (await cursor.fetchone())[0]
|
||||
if count >= 20:
|
||||
view = CV2(f"{CROSS} Error!", f"You can't add more than 20 autoresponses in {ctx.guild.name}")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
async with db.execute("SELECT 1 FROM autoresponses WHERE guild_id = ? AND LOWER(name) = ?", (ctx.guild.id, name_lower)) as cursor:
|
||||
if await cursor.fetchone():
|
||||
view = CV2(f"{CROSS} Error!", f"The autoresponse with the name `{name}` already exists in {ctx.guild.name}")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
await db.execute("INSERT INTO autoresponses (guild_id, name, message) VALUES (?, ?, ?)", (ctx.guild.id, name_lower, message))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"Created autoresponder `{name}` in {ctx.guild.name}")
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_ar.command(name="delete", help="Delete an existing autoresponder.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _delete(self, ctx, name):
|
||||
name_lower = name.lower()
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT 1 FROM autoresponses WHERE guild_id = ? AND LOWER(name) = ?", (ctx.guild.id, name_lower)) as cursor:
|
||||
if not await cursor.fetchone():
|
||||
view = CV2(f"{CROSS} Error!", f"No autoresponder found with the name `{name}` in {ctx.guild.name}")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
await db.execute("DELETE FROM autoresponses WHERE guild_id = ? AND LOWER(name) = ?", (ctx.guild.id, name_lower))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"Deleted autoresponder `{name}` in {ctx.guild.name}")
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_ar.command(name="edit", help="Edit an existing autoresponder.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _edit(self, ctx, name, *, message):
|
||||
name_lower = name.lower()
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT 1 FROM autoresponses WHERE guild_id = ? AND LOWER(name) = ?", (ctx.guild.id, name_lower)) as cursor:
|
||||
if not await cursor.fetchone():
|
||||
view = CV2(f"{CROSS} Error!", f"No autoresponder found with the name `{name}` in {ctx.guild.name}")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
await db.execute("UPDATE autoresponses SET message = ? WHERE guild_id = ? AND LOWER(name) = ?", (message, ctx.guild.id, name_lower))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"Edited autoresponder `{name}` in {ctx.guild.name}")
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_ar.command(name="config", help="List all autoresponders in the server.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _config(self, ctx):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT name FROM autoresponses WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
autoresponses = await cursor.fetchall()
|
||||
|
||||
if not autoresponses:
|
||||
view = CV2("No Autoresponders", f"There are no autoresponders in {ctx.guild.name}")
|
||||
return await ctx.reply(view=view)
|
||||
|
||||
ar_list = "\n".join([f"**[{i}]** {name}" for i, (name,) in enumerate(autoresponses, start=1)])
|
||||
view = CV2(f"Autoresponders in {ctx.guild.name}", ar_list)
|
||||
await ctx.send(view=view)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author == self.bot.user:
|
||||
return
|
||||
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT message FROM autoresponses WHERE guild_id = ? AND LOWER(name) = ?", (message.guild.id, message.content.lower())) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if row:
|
||||
await message.channel.send(row[0])
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(AutoResponder(bot))
|
||||
338
bot/cogs/commands/autorole.py
Normal file
338
bot/cogs/commands/autorole.py
Normal file
@@ -0,0 +1,338 @@
|
||||
from __future__ import annotations
|
||||
import discord
|
||||
from utils.emoji import CROSS, ICONS_WARNING, TICK
|
||||
import aiosqlite
|
||||
import logging
|
||||
from discord.ext import commands
|
||||
from typing import List, Dict
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="\x1b[38;5;197m[\x1b[0m%(asctime)s\x1b[38;5;197m]\x1b[0m -> \x1b[38;5;197m%(message)s\x1b[0m",
|
||||
datefmt="%H:%M:%S",
|
||||
)
|
||||
|
||||
DATABASE_PATH = 'db/autorole.db'
|
||||
|
||||
class BasicView(discord.ui.View):
|
||||
def __init__(self, ctx: commands.Context, timeout=60):
|
||||
super().__init__(timeout=timeout)
|
||||
self.ctx = ctx
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.ctx.author.id and interaction.user.id not in [870179991462236170]:
|
||||
await interaction.response.send_message("Uh oh! That message doesn't belong to you.\nYou must run this command to interact with it.", ephemeral=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class AutoRole(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.create_table())
|
||||
self.color = 0xFF0000
|
||||
|
||||
async def create_table(self):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS autorole (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
bots TEXT NOT NULL,
|
||||
humans TEXT NOT NULL
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
async def get_autorole(self, guild_id: int) -> Dict[str, List[int]]:
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT bots, humans FROM autorole WHERE guild_id = ?", (guild_id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row:
|
||||
bots, humans = row
|
||||
|
||||
bots = [int(role_id) for role_id in bots.replace('[', '').replace(']', '').replace(' ', '').split(',') if role_id]
|
||||
humans = [int(role_id) for role_id in humans.replace('[', '').replace(']', '').replace(' ', '').split(',') if role_id]
|
||||
|
||||
return {"bots": bots, "humans": humans}
|
||||
else:
|
||||
return {"bots": [], "humans": []}
|
||||
|
||||
|
||||
|
||||
async def update_autorole(self, guild_id: int, data: Dict[str, List[int]]):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
bots = ','.join(map(str, data['bots']))
|
||||
humans = ','.join(map(str, data['humans']))
|
||||
|
||||
await db.execute("INSERT OR REPLACE INTO autorole (guild_id, bots, humans) VALUES (?, ?, ?)",
|
||||
(guild_id, bots, humans))
|
||||
await db.commit()
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.group(name="autorole", invoke_without_command=True)
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
|
||||
@_autorole.command(name="config", help="Shows the current autorole configuration")
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _ar_config(self, ctx):
|
||||
|
||||
data = await self.get_autorole(ctx.guild.id)
|
||||
if data:
|
||||
fetched_humans = [ctx.guild.get_role(role_id) for role_id in data["humans"] if ctx.guild.get_role(role_id)]
|
||||
fetched_bots = [ctx.guild.get_role(role_id) for role_id in data["bots"] if ctx.guild.get_role(role_id)]
|
||||
|
||||
hums = "\n".join(role.mention for role in fetched_humans) or "None"
|
||||
bos = "\n".join(role.mention for role in fetched_bots) or "None"
|
||||
|
||||
view = CV2(
|
||||
f"Autorole Configuration for {ctx.guild.name}",
|
||||
f"__Humans__\n{hums}",
|
||||
f"__Bots__\n{bos}"
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
else:
|
||||
view = CV2("Autorole Configuration", "No autorole configuration found in this Guild.")
|
||||
await ctx.reply(view=view)
|
||||
|
||||
|
||||
|
||||
@_autorole.group(name="reset", help="Clear autorole config in the Guild")
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_reset(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_autorole_reset.command(name="humans", help="Clear autorole configuration for humans")
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_humans_reset(self, ctx):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT humans FROM autorole WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if data and data[0]:
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("UPDATE autorole SET humans = ? WHERE guild_id = ?", ('[]', ctx.guild.id))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", "Cleared all human autoroles in this Guild.")
|
||||
else:
|
||||
view = CV2(f"{CROSS} Error", "No Autoroles set for humans in this Guild.")
|
||||
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_autorole_reset.command(name="bots", help="Clear autorole configuration for bots")
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_bots_reset(self, ctx):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT bots FROM autorole WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if data and data[0]:
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("UPDATE autorole SET bots = ? WHERE guild_id = ?", ('[]', ctx.guild.id))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", "Cleared all bot autoroles in this Guild.")
|
||||
else:
|
||||
view = CV2(f"{CROSS} Error", "No Autoroles set for Bots in this Guild.")
|
||||
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_autorole_reset.command(name="all", help="Clear all autorole configuration in the Guild")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_reset_all(self, ctx):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT humans, bots FROM autorole WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if data and (data[0] or data[1]):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("UPDATE autorole SET humans = ?, bots = ? WHERE guild_id = ?", ('[]', '[]', ctx.guild.id))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", "Cleared all autoroles in this Gudild.")
|
||||
else:
|
||||
view = CV2(f"{CROSS} Error", "No Autoroles set in this Guild.")
|
||||
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_autorole.group(name="humans", help="Setup autoroles for human")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_humans(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_autorole_humans.command(name="add", help="Add role to list of human Autoroles.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_humans_add(self, ctx, *, role: discord.Role):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT humans FROM autorole WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if data:
|
||||
humans = eval(data[0])
|
||||
if role.id in humans:
|
||||
view = CV2(f"{ICONS_WARNING} Access Denied", f"{role.mention} is already in human autoroles.")
|
||||
elif len(humans) >= 10:
|
||||
view = CV2(f"{ICONS_WARNING} Access Denied", "You can only add upto 10 human autoroles.")
|
||||
else:
|
||||
humans.append(role.id)
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("UPDATE autorole SET humans = ? WHERE guild_id = ?", (str(humans), ctx.guild.id))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"{role.mention} has been added to human autoroles.")
|
||||
else:
|
||||
humans = [role.id]
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("INSERT INTO autorole (guild_id, humans, bots) VALUES (?, ?, ?)", (ctx.guild.id, str(humans), '[]'))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"{role.mention} has been added to human autoroles.")
|
||||
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_autorole_humans.command(name="remove", help="Remove a role from human Autoroles.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_humans_remove(self, ctx, *, role: discord.Role):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT humans FROM autorole WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if data:
|
||||
humans = eval(data[0])
|
||||
if role.id not in humans:
|
||||
view = CV2(f"{CROSS} Error", f"{role.mention} is not in human autoroles.")
|
||||
else:
|
||||
humans.remove(role.id)
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("UPDATE autorole SET humans = ? WHERE guild_id = ?", (str(humans), ctx.guild.id))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"{role.mention} has been removed from human autoroles.")
|
||||
else:
|
||||
view = CV2(f"{CROSS} Error", "No Autoroles set in this guild for humans.")
|
||||
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_autorole.group(name="bots", help="Setup autoroles for bots")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_bots(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_autorole_bots.command(name="add", help="Add role to bot Autoroles.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_bots_add(self, ctx, *, role: discord.Role):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT bots FROM autorole WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if data:
|
||||
bots = eval(data[0])
|
||||
if role.id in bots:
|
||||
view = CV2(f"{ICONS_WARNING} Access Denied", f"{role.mention} is already in bot autoroles.")
|
||||
elif len(bots) >= 10:
|
||||
view = CV2(f"{ICONS_WARNING} Access Denied", "You can only add upto 10 bot autoroles")
|
||||
else:
|
||||
bots.append(role.id)
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("UPDATE autorole SET bots = ? WHERE guild_id = ?", (str(bots), ctx.guild.id))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"{role.mention} has been added to bot autoroles.")
|
||||
else:
|
||||
bots = [role.id]
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("INSERT INTO autorole (guild_id, humans, bots) VALUES (?, ?, ?)", (ctx.guild.id, '[]', str(bots)))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"{role.mention} has been added to bot autoroles.")
|
||||
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@_autorole_bots.command(name="remove", help="Remove a role from bot Autoroles.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _autorole_bots_remove(self, ctx, *, role: discord.Role):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT bots FROM autorole WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
|
||||
if data:
|
||||
bots = eval(data[0])
|
||||
if role.id not in bots:
|
||||
view = CV2(f"{CROSS} Error", f"{role.mention} is not in bot autoroles.")
|
||||
else:
|
||||
bots.remove(role.id)
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("UPDATE autorole SET bots = ? WHERE guild_id = ?", (str(bots), ctx.guild.id))
|
||||
await db.commit()
|
||||
view = CV2(f"{TICK} Success", f"{role.mention} has been removed from bot autoroles.")
|
||||
else:
|
||||
view = CV2(f"{CROSS} Error", "No Autoroles set in this guild for bots.")
|
||||
|
||||
await ctx.reply(view=view)
|
||||
|
||||
|
||||
229
bot/cogs/commands/blackjack.py
Normal file
229
bot/cogs/commands/blackjack.py
Normal file
@@ -0,0 +1,229 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import os
|
||||
import random
|
||||
from typing import List, Tuple, Union
|
||||
from PIL import Image
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
|
||||
class CV2(LayoutView):
|
||||
def __init__(self, title, *sections):
|
||||
super().__init__(timeout=None)
|
||||
items = [TextDisplay(f"**{title}**")]
|
||||
for s in sections:
|
||||
if s:
|
||||
items.append(Separator(visible=True))
|
||||
items.append(TextDisplay(str(s)))
|
||||
self.add_item(build_container(*items))
|
||||
|
||||
CARDS_PATH = 'data/cards/'
|
||||
|
||||
class Card:
|
||||
suits = ["clubs", "diamonds", "hearts", "spades"]
|
||||
|
||||
def __init__(self, suit: str, value: int, down=False):
|
||||
self.suit = suit
|
||||
self.value = value
|
||||
self.down = down
|
||||
self.symbol = self.name[0].upper()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
if self.value <= 10:
|
||||
return str(self.value)
|
||||
else:
|
||||
return {
|
||||
11: 'jack',
|
||||
12: 'queen',
|
||||
13: 'king',
|
||||
14: 'ace',
|
||||
}[self.value]
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
return (
|
||||
f"{self.symbol if self.name != '10' else '10'}" \
|
||||
f"{self.suit[0].upper()}.png" \
|
||||
if not self.down else "red_back.png"
|
||||
)
|
||||
|
||||
def flip(self):
|
||||
self.down = not self.down
|
||||
return self
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.name.title()} of {self.suit.title()}'
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self)
|
||||
|
||||
|
||||
class Blackjack(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@staticmethod
|
||||
def hand_to_images(hand: List[Card]) -> List[Image.Image]:
|
||||
return [Image.open(os.path.join(CARDS_PATH, card.image)) for card in hand]
|
||||
|
||||
@staticmethod
|
||||
def center(*hands: Tuple[Image.Image]) -> Image.Image:
|
||||
bg: Image.Image = Image.open(os.path.join(CARDS_PATH, 'table.png'))
|
||||
bg_center_x = bg.size[0] // 2
|
||||
bg_center_y = bg.size[1] // 2
|
||||
|
||||
img_w = hands[0][0].size[0]
|
||||
img_h = hands[0][0].size[1]
|
||||
|
||||
start_y = bg_center_y - (((len(hands) * img_h) + ((len(hands) - 1) * 15)) // 2)
|
||||
|
||||
for hand in hands:
|
||||
start_x = bg_center_x - (((len(hand) * img_w) + ((len(hand) - 1) * 10)) // 2)
|
||||
for card in hand:
|
||||
bg.alpha_composite(card, (start_x, start_y))
|
||||
start_x += img_w + 10
|
||||
start_y += img_h + 15
|
||||
|
||||
return bg
|
||||
|
||||
def output(self, name, *hands: Tuple[List[Card]]) -> None:
|
||||
self.center(*map(self.hand_to_images, hands)).save(f'data/{name}.png')
|
||||
|
||||
@staticmethod
|
||||
def calc_hand(hand: List[Card]) -> int:
|
||||
non_aces = [c for c in hand if c.symbol != 'A']
|
||||
aces = [c for c in hand if c.symbol == 'A']
|
||||
total_sum = 0
|
||||
for card in non_aces:
|
||||
if not card.down:
|
||||
if card.symbol in 'JQK':
|
||||
total_sum += 10
|
||||
else:
|
||||
total_sum += card.value
|
||||
for card in aces:
|
||||
if not card.down:
|
||||
if total_sum <= 10:
|
||||
total_sum += 11
|
||||
else:
|
||||
total_sum += 1
|
||||
return total_sum
|
||||
|
||||
@commands.command(aliases=['bj', 'blackjacks'], help="Play a simple game of blackjack.", usage="blackjack")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def blackjack(self, ctx: commands.Context):
|
||||
try:
|
||||
deck = [Card(suit, num) for num in range(2, 15) for suit in Card.suits]
|
||||
random.shuffle(deck)
|
||||
|
||||
player_hand: List[Card] = []
|
||||
dealer_hand: List[Card] = []
|
||||
|
||||
player_hand.append(deck.pop())
|
||||
dealer_hand.append(deck.pop())
|
||||
player_hand.append(deck.pop())
|
||||
dealer_hand.append(deck.pop())
|
||||
|
||||
dealer_hand[1] = dealer_hand[1].flip()
|
||||
|
||||
player_score = self.calc_hand(player_hand)
|
||||
dealer_score = self.calc_hand(dealer_hand)
|
||||
|
||||
async def out_table(**kwargs) -> discord.Message:
|
||||
self.output(ctx.author.id, dealer_hand, player_hand)
|
||||
file = discord.File(f"data/{ctx.author.id}.png", filename=f"{ctx.author.id}.png")
|
||||
view = CV2(kwargs.get('title', 'Blackjack'), kwargs.get('description', ''))
|
||||
msg: discord.Message = await ctx.send(file=file, view=view)
|
||||
return msg
|
||||
|
||||
def check(reaction: discord.Reaction, user: Union[discord.Member, discord.User]) -> bool:
|
||||
return all((
|
||||
str(reaction.emoji) in ("🇸", "🇭"),
|
||||
user == ctx.author,
|
||||
user != self.bot.user,
|
||||
reaction.message == msg
|
||||
))
|
||||
|
||||
standing = False
|
||||
|
||||
while True:
|
||||
player_score = self.calc_hand(player_hand)
|
||||
dealer_score = self.calc_hand(dealer_hand)
|
||||
|
||||
if player_score == 21:
|
||||
result = ("Blackjack!", 'won')
|
||||
break
|
||||
|
||||
elif player_score > 21:
|
||||
result = ("Player busts", 'lost')
|
||||
break
|
||||
|
||||
msg = await out_table(
|
||||
title="Your Turn",
|
||||
description=f"Your hand: {player_score}\nDealer's hand: {dealer_score}"
|
||||
)
|
||||
|
||||
await msg.add_reaction("🇭")
|
||||
await msg.add_reaction("🇸")
|
||||
|
||||
try:
|
||||
reaction, _ = await self.bot.wait_for('reaction_add', timeout=60, check=check)
|
||||
except asyncio.TimeoutError:
|
||||
await msg.delete()
|
||||
return
|
||||
|
||||
if str(reaction.emoji) == "🇭":
|
||||
player_hand.append(deck.pop())
|
||||
await msg.delete()
|
||||
continue
|
||||
|
||||
elif str(reaction.emoji) == "🇸":
|
||||
standing = True
|
||||
break
|
||||
|
||||
if standing:
|
||||
dealer_hand[1] = dealer_hand[1].flip()
|
||||
player_score = self.calc_hand(player_hand)
|
||||
dealer_score = self.calc_hand(dealer_hand)
|
||||
|
||||
while dealer_score < 17:
|
||||
dealer_hand.append(deck.pop())
|
||||
dealer_score = self.calc_hand(dealer_hand)
|
||||
|
||||
if dealer_score == 21:
|
||||
result = ('Dealer blackjack', 'lost')
|
||||
elif dealer_score > 21:
|
||||
result = ("Dealer busts", 'won')
|
||||
elif dealer_score == player_score:
|
||||
result = ("Tie!", 'kept')
|
||||
elif dealer_score > player_score:
|
||||
result = ("You lose!", 'lost')
|
||||
elif dealer_score < player_score:
|
||||
result = ("You win!", 'won')
|
||||
|
||||
color = (
|
||||
discord.Color.red() if result[1] == 'lost'
|
||||
else discord.Color.green() if result[1] == 'won'
|
||||
else discord.Color.blue()
|
||||
)
|
||||
try:
|
||||
await msg.delete()
|
||||
except:
|
||||
pass
|
||||
msg = await out_table(
|
||||
title=result[0],
|
||||
color=color,
|
||||
description=(
|
||||
f"**You {result[1]}**\nYour hand: {player_score}\n" +
|
||||
f"Dealer's hand: {dealer_score}"
|
||||
)
|
||||
)
|
||||
os.remove(f'data/{ctx.author.id}.png')
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
|
||||
372
bot/cogs/commands/blacklist.py
Normal file
372
bot/cogs/commands/blacklist.py
Normal file
@@ -0,0 +1,372 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK
|
||||
from discord.ext import commands
|
||||
from discord.ext import menus
|
||||
import aiosqlite
|
||||
import os
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
from typing import Union
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
|
||||
class CV2(LayoutView):
|
||||
def __init__(self, title, *sections):
|
||||
super().__init__(timeout=None)
|
||||
items = [TextDisplay(f"**{title}**")]
|
||||
for s in sections:
|
||||
if s:
|
||||
items.append(Separator(visible=True))
|
||||
items.append(TextDisplay(str(s)))
|
||||
self.add_item(build_container(*items))
|
||||
from utils.paginator import Paginator as sonu
|
||||
|
||||
|
||||
class BlacklistWordSource(menus.ListPageSource):
|
||||
def __init__(self, data):
|
||||
super().__init__(data, per_page=4)
|
||||
|
||||
async def format_page(self, menu, entries):
|
||||
embed = discord.Embed(
|
||||
title="Blacklist Word [7]",
|
||||
description="< > Duty | [ ] Optional",
|
||||
color=0xFF0000
|
||||
)
|
||||
for entry in entries:
|
||||
embed.add_field(name="",value=entry, inline=False)
|
||||
|
||||
embed.set_footer(
|
||||
text='Users having Administrator can use Blacklisted Word',
|
||||
icon_url="https://cdn.discordapp.com/avatars/1396114795102470196/198b9bc616ec574f6fd2f7121a1d3abc.webp?size=4096"
|
||||
)
|
||||
return embed
|
||||
|
||||
|
||||
DB_PATH = "db/blword.db"
|
||||
|
||||
|
||||
async def create_blacklist_table():
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS blacklist (
|
||||
guild_id TEXT,
|
||||
word TEXT,
|
||||
PRIMARY KEY (guild_id, word)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def create_bypass_table():
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS bypass (
|
||||
guild_id TEXT,
|
||||
user_id INTEGER,
|
||||
PRIMARY KEY (guild_id, user_id)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def create_bypass_roles_table():
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS bypass_roles (
|
||||
guild_id TEXT,
|
||||
role_id INTEGER,
|
||||
PRIMARY KEY (guild_id, role_id)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
|
||||
|
||||
|
||||
class Blacklist(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(create_blacklist_table())
|
||||
self.bot.loop.create_task(create_bypass_table())
|
||||
self.bot.loop.create_task(create_bypass_roles_table())
|
||||
|
||||
############ FUNCTIONS ############
|
||||
async def is_word_blacklisted(self, guild_id, word):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT * FROM blacklist WHERE guild_id = ? AND word = ?", (guild_id, word)) as cursor:
|
||||
return await cursor.fetchone() is not None
|
||||
|
||||
|
||||
async def add_word_to_blacklist(self, guild_id, word):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("INSERT INTO blacklist (guild_id, word) VALUES (?, ?)", (guild_id, word))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def remove_word_from_blacklist(self, guild_id, word):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("DELETE FROM blacklist WHERE guild_id = ? AND word = ?", (guild_id, word))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def get_blacklisted_words(self, guild_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT word FROM blacklist WHERE guild_id = ?", (guild_id,)) as cursor:
|
||||
return [row[0] async for row in cursor]
|
||||
|
||||
|
||||
async def is_user_bypassed(self, guild_id, user_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT * FROM bypass WHERE guild_id = ? AND user_id = ?", (guild_id, user_id)) as cursor:
|
||||
return await cursor.fetchone() is not None
|
||||
|
||||
|
||||
async def add_user_to_bypass(self, guild_id, user_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("INSERT INTO bypass (guild_id, user_id) VALUES (?, ?)", (guild_id, user_id))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def remove_user_from_bypass(self, guild_id, user_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("DELETE FROM bypass WHERE guild_id = ? AND user_id = ?", (guild_id, user_id))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def get_bypassed_users(self, guild_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT user_id FROM bypass WHERE guild_id = ?", (guild_id,)) as cursor:
|
||||
return [row[0] async for row in cursor]
|
||||
|
||||
|
||||
async def is_role_bypassed(self, guild_id, role_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT * FROM bypass_roles WHERE guild_id = ? AND role_id = ?", (guild_id, role_id)) as cursor:
|
||||
return await cursor.fetchone() is not None
|
||||
|
||||
|
||||
async def add_role_to_bypass(self, guild_id, role_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("INSERT INTO bypass_roles (guild_id, role_id) VALUES (?, ?)", (guild_id, role_id))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def remove_role_from_bypass(self, guild_id, role_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("DELETE FROM bypass_roles WHERE guild_id = ? AND role_id = ?", (guild_id, role_id))
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def get_bypassed_roles(self, guild_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT role_id FROM bypass_roles WHERE guild_id = ?", (guild_id,)) as cursor:
|
||||
return [row[0] async for row in cursor]
|
||||
|
||||
|
||||
async def remove_all_words_from_blacklist(self, guild_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("DELETE FROM blacklist WHERE guild_id = ?", (guild_id,))
|
||||
await db.commit()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.bot:
|
||||
return
|
||||
|
||||
guild_id = str(message.guild.id)
|
||||
words = await self.get_blacklisted_words(guild_id)
|
||||
bypassed_users = await self.get_bypassed_users(guild_id)
|
||||
bypassed_roles = await self.get_bypassed_roles(guild_id)
|
||||
|
||||
if message.author.guild_permissions.administrator or message.author.id in bypassed_users:
|
||||
return
|
||||
|
||||
for role in message.author.roles:
|
||||
if role.id in bypassed_roles:
|
||||
return
|
||||
|
||||
for word in words:
|
||||
if word in message.content.lower():
|
||||
await message.delete()
|
||||
warning_message = await message.channel.send(
|
||||
f"{message.author.mention} watch your language, your message contains a blacklisted word!"
|
||||
)
|
||||
await warning_message.delete(delay=3)
|
||||
break
|
||||
|
||||
@commands.group(name="blacklistword", aliases=["blword"], invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def blacklistword(self, ctx):
|
||||
commands_list = [
|
||||
"➜ `blacklistword add <word>` - Add a word to the blacklist.",
|
||||
"➜ `blacklistword remove <word>` - Remove a word from the blacklist.",
|
||||
"➜ `blacklistword reset` - Clear all blacklisted words for the guild.",
|
||||
"➜ `blacklistword config` - Show the list of blacklisted words for the guild.",
|
||||
"➜ `blacklistword bypass add <role>/<user>` - Add a role/user to the bypass list.",
|
||||
"➜ `blacklistword bypass remove <role>/<user>` - Remove a role/user from the bypass list.",
|
||||
"➜ `blacklistword bypass list` - Show the list of bypassed roles/users."
|
||||
]
|
||||
|
||||
paginator = sonu(
|
||||
source=BlacklistWordSource(commands_list),
|
||||
ctx=ctx
|
||||
)
|
||||
|
||||
await paginator.paginate()
|
||||
@blacklistword.command(name="add")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def add(self, ctx, word: str):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if len(await self.get_blacklisted_words(guild_id)) >= 30:
|
||||
await ctx.reply("The blacklist is full. Maximum 30 words allowed.")
|
||||
return
|
||||
if await self.is_word_blacklisted(guild_id, word.lower()):
|
||||
await ctx.reply(view=CV2(f"{TICK} Access Denied", f"`{word}` is already in the blacklist."))
|
||||
return
|
||||
|
||||
await self.add_word_to_blacklist(guild_id, word.lower())
|
||||
await ctx.reply(view=CV2(f"{TICK} Success", f"Added `{word}` to the blacklist."))
|
||||
|
||||
@blacklistword.command(name="remove")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def remove(self, ctx, word: str):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if not await self.is_word_blacklisted(guild_id, word.lower()):
|
||||
await ctx.reply(view=CV2(f"{CROSS} Error", f"`{word}` is not in the blacklist."))
|
||||
return
|
||||
|
||||
await self.remove_word_from_blacklist(guild_id, word.lower())
|
||||
await ctx.reply(view=CV2(f"{TICK} Success", f"Removed `{word}` from the blacklist."))
|
||||
|
||||
@blacklistword.command(name="reset")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def reset(self, ctx):
|
||||
guild_id = str(ctx.guild.id)
|
||||
words = await self.get_blacklisted_words(guild_id)
|
||||
|
||||
if not words:
|
||||
await ctx.reply(view=CV2(f"{CROSS} Error", "No words are currently blacklisted."))
|
||||
return
|
||||
|
||||
await self.remove_all_words_from_blacklist(guild_id)
|
||||
|
||||
await ctx.reply(view=CV2(f"{TICK} Success", "Cleared all blacklisted words."))
|
||||
|
||||
|
||||
@blacklistword.command(name="config")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def config(self, ctx):
|
||||
guild_id = str(ctx.guild.id)
|
||||
words = await self.get_blacklisted_words(guild_id)
|
||||
if not words:
|
||||
await ctx.reply(view=CV2(f"{CROSS} Error", "No words are currently blacklisted."))
|
||||
return
|
||||
|
||||
await ctx.reply(view=CV2(f"Blacklisted Words for {ctx.guild.name}", "\n".join(words)))
|
||||
|
||||
@blacklistword.group(name="bypass", invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass(self, ctx):
|
||||
await ctx.send(view=CV2("Bypass User Commands", "➜ `blacklistword bypass add <role>/<user>` - Add a role/user to the bypass list.\n\n➜ `blacklistword bypass remove <role>/<user>` - Remove a role/user from the bypass list.\n\n➜ `blacklistword bypass list` - Show the list of bypassed roles/users."))
|
||||
|
||||
|
||||
@bypass.command(name="add")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_add(self, ctx, target: Union[discord.Member, discord.Role]):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if isinstance(target, discord.Member):
|
||||
if len(await self.get_bypassed_users(guild_id)) >= 30:
|
||||
await ctx.reply("The bypass list for users is full. Maximum 30 users allowed.")
|
||||
return
|
||||
if await self.is_user_bypassed(guild_id, target.id):
|
||||
await ctx.reply(view=CV2("Error", f"{CROSS} | `{target}` is already bypassed."))
|
||||
return
|
||||
await self.add_user_to_bypass(guild_id, target.id)
|
||||
await ctx.reply(view=CV2(f"{TICK} Success", f"Added `{target}` to the bypass list."))
|
||||
|
||||
elif isinstance(target, discord.Role):
|
||||
if len(await self.get_bypassed_roles(guild_id)) >= 30:
|
||||
await ctx.reply("The bypass list for roles is full. Maximum 30 roles allowed.")
|
||||
return
|
||||
if await self.is_role_bypassed(guild_id, target.id):
|
||||
await ctx.reply(view=CV2(f"{CROSS} Error", f"`{target}` is already bypassed."))
|
||||
return
|
||||
await self.add_role_to_bypass(guild_id, target.id)
|
||||
await ctx.reply(view=CV2(f"{TICK} Success", f"Added `{target}` to the bypass list."))
|
||||
|
||||
|
||||
|
||||
@bypass.command(name="remove")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_remove(self, ctx, target: Union[discord.Member, discord.Role]):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if isinstance(target, discord.Member):
|
||||
if not await self.is_user_bypassed(guild_id, target.id):
|
||||
await ctx.reply(view=CV2(f"{CROSS} Error", f"`{target}` is not bypassed."))
|
||||
return
|
||||
await self.remove_user_from_bypass(guild_id, target.id)
|
||||
await ctx.reply(view=CV2(f"{TICK} Success", f"Removed `{target}` from the bypass list."))
|
||||
|
||||
elif isinstance(target, discord.Role):
|
||||
if not await self.is_role_bypassed(guild_id, target.id):
|
||||
await ctx.reply(view=CV2(f"{CROSS} Error", f"`{target}` is not bypassed."))
|
||||
return
|
||||
await self.remove_role_from_bypass(guild_id, target.id)
|
||||
await ctx.reply(view=CV2(f"{TICK} Success", f"Removed `{target}` from the bypass list."))
|
||||
|
||||
@bypass.command(name="list")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_list(self, ctx):
|
||||
guild_id = str(ctx.guild.id)
|
||||
users = await self.get_bypassed_users(guild_id)
|
||||
roles = await self.get_bypassed_roles(guild_id)
|
||||
|
||||
if not users and not roles:
|
||||
await ctx.send(view=CV2(f"{CROSS} Error", "No users or roles are currently bypassed."))
|
||||
return
|
||||
|
||||
bypassed_users = [ctx.guild.get_member(user_id) for user_id in users if ctx.guild.get_member(user_id)]
|
||||
bypassed_roles = [ctx.guild.get_role(role_id) for role_id in roles if ctx.guild.get_role(role_id)]
|
||||
sections = []
|
||||
if bypassed_users:
|
||||
sections.append(f"**Users**\n" + ", ".join([user.name for user in bypassed_users]))
|
||||
if bypassed_roles:
|
||||
sections.append(f"**Roles**\n" + ", ".join([role.name for role in bypassed_roles]))
|
||||
await ctx.send(view=CV2(f"Bypassed Users and Roles for {ctx.guild.name}", *sections))
|
||||
|
||||
|
||||
@add.error
|
||||
@remove.error
|
||||
@reset.error
|
||||
@config.error
|
||||
@bypass_add.error
|
||||
@bypass_remove.error
|
||||
async def command_error(self, ctx, error):
|
||||
if isinstance(error, commands.CommandError):
|
||||
if not isinstance(error, commands.CommandOnCooldown):
|
||||
await ctx.reply(view=CV2("Error", f"{CROSS} | An error occurred while processing the command. Make sure you have **Administrator** permission."))
|
||||
178
bot/cogs/commands/block.py
Normal file
178
bot/cogs/commands/block.py
Normal file
@@ -0,0 +1,178 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK
|
||||
from discord.ext import commands
|
||||
import aiosqlite
|
||||
from utils import Paginator, DescriptionEmbedPaginator
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
class CV2(LayoutView):
|
||||
def __init__(self, title, *sections):
|
||||
super().__init__(timeout=None)
|
||||
items = [TextDisplay(f"**{title}**")]
|
||||
for s in sections:
|
||||
if s:
|
||||
items.append(Separator(visible=True))
|
||||
items.append(TextDisplay(str(s)))
|
||||
self.add_item(build_container(*items))
|
||||
|
||||
class Block(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.set_db())
|
||||
|
||||
#@commands.Cog.listener()
|
||||
async def set_db(self):
|
||||
async with aiosqlite.connect('db/block.db') as db:
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS user_blacklist (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS guild_blacklist (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
await db.commit()
|
||||
|
||||
|
||||
|
||||
@commands.group(name="blacklist", aliases=["bl"], invoke_without_command=True)
|
||||
@commands.is_owner()
|
||||
async def blacklist(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
@blacklist.group(name="user", help="Add/Remove a user to the blacklist.", invoke_without_command=True)
|
||||
@commands.is_owner()
|
||||
async def user(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
@user.command(name="add", help="Adds a user to the blacklist.")
|
||||
@commands.is_owner()
|
||||
async def add_user(self, ctx, user: discord.User):
|
||||
async with aiosqlite.connect('db/block.db') as db:
|
||||
cursor = await db.execute('SELECT user_id FROM user_blacklist WHERE user_id = ?', (user.id,))
|
||||
if await cursor.fetchone():
|
||||
await ctx.reply(view=CV2("User Already Blacklisted", f"{user.mention} is already blacklisted."))
|
||||
else:
|
||||
await db.execute('INSERT INTO user_blacklist (user_id) VALUES (?)', (user.id,))
|
||||
await db.commit()
|
||||
await ctx.reply(view=CV2(f"{TICK} User Blacklisted", f"{user.mention} has been added to the blacklist."))
|
||||
|
||||
@user.command(name="remove", help="Remove a user from the blacklist.")
|
||||
@commands.is_owner()
|
||||
async def remove_user(self, ctx, user: discord.User):
|
||||
async with aiosqlite.connect('db/block.db') as db:
|
||||
cursor = await db.execute('SELECT user_id FROM user_blacklist WHERE user_id = ?', (user.id,))
|
||||
if not await cursor.fetchone():
|
||||
await ctx.reply(view=CV2(f"{CROSS} User Not Blacklisted", f"{user.mention} is not in the blacklist."))
|
||||
else:
|
||||
await db.execute('DELETE FROM user_blacklist WHERE user_id = ?', (user.id,))
|
||||
await db.commit()
|
||||
await ctx.reply(view=CV2(f"{TICK} User Unblacklisted", f"{user.mention} has been removed from the blacklist."))
|
||||
|
||||
@user.command(name="show", aliases=["list"], help="Shows all Blacklisted users.")
|
||||
@commands.is_owner()
|
||||
async def show_users(self, ctx):
|
||||
async with aiosqlite.connect('db/block.db') as db:
|
||||
cursor = await db.execute('SELECT user_id FROM user_blacklist')
|
||||
rows = await cursor.fetchall()
|
||||
if not rows:
|
||||
await ctx.reply(view=CV2(f"{CROSS} No Blacklisted Users", "There are no users in the blacklist."))
|
||||
return
|
||||
|
||||
blacklist = []
|
||||
for row in rows:
|
||||
user_id = row[0]
|
||||
try:
|
||||
user = await self.bot.fetch_user(user_id)
|
||||
username = user.name
|
||||
user_link = f"https://discord.com/users/{user_id}"
|
||||
#indexx = [""for index, user in enumerate(blacklist)]
|
||||
|
||||
blacklist.append(f"**[{username}]({user_link})** - ({user_id})")
|
||||
except discord.NotFound:
|
||||
blacklist.append(f"User ID: {user_id} (User not found)")
|
||||
entries = [f"{index+1}. {user}" for index, user in enumerate(blacklist)]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"List of Blacklisted Users - {len(blacklist)}",
|
||||
description="",
|
||||
per_page=10,
|
||||
color=0xFF0000),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
@blacklist.group(name="guild", help="Add/Remove a guild to the blacklist.", invoke_without_command=True)
|
||||
@commands.is_owner()
|
||||
async def guild(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@guild.command(name="add", help="Adds a guild to the blacklist.")
|
||||
@commands.is_owner()
|
||||
async def add_guild(self, ctx, guild_id: int):
|
||||
async with aiosqlite.connect('db/block.db') as db:
|
||||
cursor = await db.execute('SELECT guild_id FROM guild_blacklist WHERE guild_id = ?', (guild_id,))
|
||||
if await cursor.fetchone():
|
||||
await ctx.reply(view=CV2(f"{CROSS} Guild Already Blacklisted", f"Guild with ID `{guild_id}` is already blacklisted."))
|
||||
else:
|
||||
await db.execute('INSERT INTO guild_blacklist (guild_id) VALUES (?)', (guild_id,))
|
||||
await db.commit()
|
||||
await ctx.reply(view=CV2(f"{TICK} Guild Blacklisted", f"Guild with ID `{guild_id}` has been added to the blacklist."))
|
||||
|
||||
@guild.command(name="remove", help="Remove a guild from the blacklist.")
|
||||
@commands.is_owner()
|
||||
async def remove_guild(self, ctx, guild_id: int):
|
||||
async with aiosqlite.connect('db/block.db') as db:
|
||||
cursor = await db.execute('SELECT guild_id FROM guild_blacklist WHERE guild_id = ?', (guild_id,))
|
||||
if not await cursor.fetchone():
|
||||
await ctx.reply(view=CV2(f"{CROSS} Guild Not Blacklisted", f"Guild with ID `{guild_id}` is not in the blacklist."))
|
||||
else:
|
||||
await db.execute('DELETE FROM guild_blacklist WHERE guild_id = ?', (guild_id,))
|
||||
await db.commit()
|
||||
await ctx.reply(view=CV2(f"{TICK} Guild Unblacklisted", f"Guild with ID `{guild_id}` has been removed from the blacklist."))
|
||||
|
||||
|
||||
@guild.command(name="show", aliases=["list"], help="Shows the list of blacklisted guilds")
|
||||
@commands.is_owner()
|
||||
async def show_guilds(self, ctx):
|
||||
async with aiosqlite.connect('db/block.db') as db:
|
||||
cursor = await db.execute('SELECT guild_id FROM guild_blacklist')
|
||||
rows = await cursor.fetchall()
|
||||
if not rows:
|
||||
await ctx.reply(view=CV2(f"{CROSS} No Blacklisted Guilds", "There are no guilds in the blacklist."))
|
||||
return
|
||||
|
||||
blacklist = []
|
||||
for row in rows:
|
||||
guild_id = row[0]
|
||||
try:
|
||||
guild = await self.bot.fetch_guild(guild_id)
|
||||
guild_name = guild.name
|
||||
guild_link = f"https://discord.com/guilds/{guild_id}"
|
||||
blacklist.append(f"[{guild_name}]({guild_link}) - ({guild_id})")
|
||||
except discord.NotFound:
|
||||
blacklist.append(f"Guild ID: {guild_id} (Guild not found)")
|
||||
entries = [f"{index+1}. {guild}" for index, guild in enumerate(blacklist)]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"List of Blacklisted Guilds - {len(blacklist)}",
|
||||
description="",
|
||||
per_page=10,
|
||||
color=0xFF0000),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
|
||||
|
||||
620
bot/cogs/commands/booster.py
Normal file
620
bot/cogs/commands/booster.py
Normal file
@@ -0,0 +1,620 @@
|
||||
from __future__ import annotations
|
||||
import discord
|
||||
from utils.emoji import CROSS, NITRO_BOOST, TICK, TIMER
|
||||
import asyncio
|
||||
import logging
|
||||
import aiosqlite
|
||||
import json
|
||||
from discord .ext import commands
|
||||
from utils .Tools import *
|
||||
from discord .ext .commands import Context
|
||||
from discord import app_commands
|
||||
import time
|
||||
import datetime
|
||||
import re
|
||||
from typing import *
|
||||
from time import strftime
|
||||
from core import Cog ,zyrox ,Context
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
class CV2(LayoutView):
|
||||
def __init__(self, title, *sections):
|
||||
super().__init__(timeout=None)
|
||||
items = [TextDisplay(f"**{title}**")]
|
||||
for s in sections:
|
||||
if s:
|
||||
items.append(Separator(visible=True))
|
||||
items.append(TextDisplay(str(s)))
|
||||
self.add_item(build_container(*items))
|
||||
|
||||
logging .basicConfig (
|
||||
level =logging .INFO ,
|
||||
format ="\x1b[38;5;197m[\x1b[0m%(asctime)s\x1b[38;5;197m]\x1b[0m -> \x1b[38;5;197m%(message)s\x1b[0m",
|
||||
datefmt ="%H:%M:%S",
|
||||
)
|
||||
|
||||
class Booster (Cog ):
|
||||
def __init__ (self ,bot : Zyrox ):
|
||||
self .bot =bot
|
||||
self .color =0xFF0000
|
||||
self .db_path ="db/boost.db"
|
||||
self .bot .loop .create_task (self .setup_database ())
|
||||
|
||||
|
||||
self .url_pattern =re .compile (
|
||||
r'^(?:http|ftp)s?://'
|
||||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
|
||||
r'localhost|'
|
||||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
|
||||
r'(?::\d+)?'
|
||||
r'(?:/?|[/?]\S+)$',re .IGNORECASE
|
||||
)
|
||||
|
||||
|
||||
|
||||
async def setup_database (self ):
|
||||
"""Initialize boost database tables"""
|
||||
async with aiosqlite .connect (self .db_path )as db :
|
||||
await db .execute ("""
|
||||
CREATE TABLE IF NOT EXISTS boost_config (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
config TEXT NOT NULL
|
||||
)
|
||||
""")
|
||||
await db .commit ()
|
||||
|
||||
async def get_boost_config (self ,guild_id :int )->dict :
|
||||
"""Get boost configuration for a guild"""
|
||||
async with aiosqlite .connect (self .db_path )as db :
|
||||
async with db .execute ("SELECT config FROM boost_config WHERE guild_id = ?",(guild_id ,))as cursor :
|
||||
row =await cursor .fetchone ()
|
||||
if row :
|
||||
return json .loads (row [0 ])
|
||||
|
||||
|
||||
default_config ={
|
||||
"boost":{
|
||||
"channel":[],
|
||||
"message":"{user.mention} just boosted {server.name}! 🎉",
|
||||
"embed":True ,
|
||||
"ping":False ,
|
||||
"image":"",
|
||||
"thumbnail":"",
|
||||
"autodel":0
|
||||
},
|
||||
"boost_roles":{
|
||||
"roles":[]
|
||||
}
|
||||
}
|
||||
await self .update_boost_config (guild_id ,default_config )
|
||||
return default_config
|
||||
|
||||
async def update_boost_config (self ,guild_id :int ,config :dict ):
|
||||
"""Update boost configuration for a guild"""
|
||||
async with aiosqlite .connect (self .db_path )as db :
|
||||
await db .execute (
|
||||
"INSERT OR REPLACE INTO boost_config (guild_id, config) VALUES (?, ?)",
|
||||
(guild_id ,json .dumps (config ))
|
||||
)
|
||||
await db .commit ()
|
||||
|
||||
def is_authorized (self ,ctx )->bool :
|
||||
"""Check if user is authorized to use admin commands"""
|
||||
return (
|
||||
ctx .author ==ctx .guild .owner
|
||||
or ctx .author .guild_permissions .administrator
|
||||
or ctx .author .top_role .position >=ctx .guild .me .top_role .position
|
||||
)
|
||||
|
||||
|
||||
def format_boost_message (self ,message :str ,user :discord .Member ,guild :discord .Guild )->str :
|
||||
"""Format boost message with new variable style"""
|
||||
replacements ={
|
||||
|
||||
"{server.name}":guild .name ,
|
||||
"{server.id}":str (guild .id ),
|
||||
"{server.owner}":str (guild .owner ),
|
||||
"{server.icon}":guild .icon .url if guild .icon else "",
|
||||
"{server.boost_count}":str (guild .premium_subscription_count ),
|
||||
"{server.boost_level}":f"Level {guild.premium_tier}",
|
||||
"{server.member_count}":str (guild .member_count ),
|
||||
|
||||
|
||||
"{user.name}":user .display_name ,
|
||||
"{user.mention}":user .mention ,
|
||||
"{user.tag}":str (user ),
|
||||
"{user.id}":str (user .id ),
|
||||
"{user.avatar}":user .display_avatar .url ,
|
||||
"{user.created_at}":f"<t:{int(user.created_at.timestamp())}:F>",
|
||||
"{user.joined_at}":f"<t:{int(user.joined_at.timestamp())}:F>"if user .joined_at else "Unknown",
|
||||
"{user.top_role}":user .top_role .name if user .top_role else "None",
|
||||
"{user.is_booster}":str (bool (user .premium_since )),
|
||||
"{user.is_mobile}":str (user .is_on_mobile ()),
|
||||
"{user.boosted_at}":f"<t:{int(user.premium_since.timestamp())}:F>"if user .premium_since else "Unknown"
|
||||
}
|
||||
|
||||
|
||||
for old ,new in replacements .items ():
|
||||
message =message .replace (old ,new )
|
||||
|
||||
return message
|
||||
|
||||
async def send_permission_error (self ,ctx ):
|
||||
"""Send permission error embed"""
|
||||
await ctx.send(view=CV2("Permission Error", "```diff\n- You must have Administrator permission.\n- Your top role should be above my top role.\n```"))
|
||||
|
||||
@commands .group (name ="boost",aliases =['bst'],invoke_without_command =True ,help ="Boost message configuration commands")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,5 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost (self ,ctx ):
|
||||
if ctx .subcommand_passed is None :
|
||||
await ctx .send_help (ctx .command )
|
||||
ctx .command .reset_cooldown (ctx )
|
||||
|
||||
@_boost .command (name ="thumbnail",help ="Set boost message thumbnail")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,2 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_thumbnail (self ,ctx ,thumbnail_url :str ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
if not self .url_pattern .match (thumbnail_url ):
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} Please provide a valid URL."))
|
||||
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
data ["boost"]["thumbnail"]=thumbnail_url
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Successfully updated the boost thumbnail URL."))
|
||||
|
||||
@_boost .command (name ="image",help ="Set boost message image")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,2 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_image (self ,ctx ,*,image_url :str ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
if not self .url_pattern .match (image_url ):
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} Please provide a valid URL."))
|
||||
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
data ["boost"]["image"]=image_url
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Successfully updated the boost image URL."))
|
||||
|
||||
@_boost .command (name ="autodel",help ="Set auto-delete timer for boost messages (0 to disable)")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,2 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_autodel (self ,ctx ,seconds :int ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
if seconds <0 :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} Auto-delete timer must be 0 or greater."))
|
||||
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
data ["boost"]["autodel"]=seconds
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
description =f"{TICK} Successfully set auto-delete timer to {seconds} seconds."
|
||||
if seconds ==0 :
|
||||
description =f"{TICK} Auto-delete has been disabled."
|
||||
|
||||
await ctx.send(view=CV2("Success", description))
|
||||
|
||||
@_boost .command (name ="message",help ="Set boost message content")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,2 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_message (self ,ctx ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
variables_text = (
|
||||
"Send your boost message in this channel now.\n\n**Available Variables:**\n"
|
||||
"```\n"
|
||||
"{server.name} - Server name\n"
|
||||
"{server.id} - Server ID\n"
|
||||
"{server.owner} - Server owner\n"
|
||||
"{server.icon} - Server icon URL\n"
|
||||
"{server.boost_count} - Current boost count\n"
|
||||
"{server.boost_level} - Boost level (e.g., Level 2)\n"
|
||||
"{server.member_count} - Total member count\n\n"
|
||||
"{user.name} - Booster's display name\n"
|
||||
"{user.mention} - Mention the booster\n"
|
||||
"{user.tag} - Booster's full tag\n"
|
||||
"{user.id} - Booster's ID\n"
|
||||
"{user.avatar} - Booster's avatar URL\n"
|
||||
"{user.created_at} - When the account was created\n"
|
||||
"{user.joined_at} - When user joined the server\n"
|
||||
"{user.top_role} - Booster's top role name\n"
|
||||
"{user.is_booster} - Whether they're a booster\n"
|
||||
"{user.is_mobile} - Whether on mobile\n"
|
||||
"{user.boosted_at} - Boost timestamp\n"
|
||||
"```\n*You have 60 seconds to respond*"
|
||||
)
|
||||
await ctx.send(view=CV2(f"{TICK} Boost Message Setup", variables_text))
|
||||
|
||||
def check (m ):
|
||||
return m .author ==ctx .author and m .channel ==ctx .channel
|
||||
|
||||
try :
|
||||
message =await self .bot .wait_for ('message',check =check ,timeout =60.0 )
|
||||
except asyncio .TimeoutError :
|
||||
await ctx.send(view=CV2("Timeout", f"{TIMER} Timeout! Please try again."))
|
||||
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
data ["boost"]["message"]=message .content
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Successfully updated the boost message."))
|
||||
|
||||
@_boost .command (name ="embed",help ="Toggle embed formatting for boost messages")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,2 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_embed (self ,ctx ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
data ["boost"]["embed"]=not data ["boost"]["embed"]
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
status ="enabled"if data ["boost"]["embed"]else "disabled"
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Embed formatting has been **{status}**."))
|
||||
|
||||
@_boost .command (name ="ping",help ="Toggle pinging the booster")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,2 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_ping (self ,ctx ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
data ["boost"]["ping"]=not data ["boost"]["ping"]
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
status ="enabled"if data ["boost"]["ping"]else "disabled"
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Booster pinging has been **{status}**."))
|
||||
|
||||
@_boost .group (name ="channel",help ="Manage boost notification channels")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_channel (self ,ctx ):
|
||||
if ctx .subcommand_passed is None :
|
||||
await ctx .send_help (ctx .command )
|
||||
ctx .command .reset_cooldown (ctx )
|
||||
|
||||
@_boost_channel .command (name ="add",help ="Add a boost notification channel")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,3 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_channel_add (self ,ctx ,channel :discord .TextChannel ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
channels =data ["boost"]["channel"]
|
||||
|
||||
if len (channels )>=3 :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} Maximum boost channel limit reached (3 channels)."))
|
||||
return
|
||||
|
||||
if str (channel .id )in channels :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} This channel is already in the boost channels list."))
|
||||
return
|
||||
|
||||
channels .append (str (channel .id ))
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Successfully added {channel.mention} to boost channels list."))
|
||||
|
||||
@_boost_channel .command (name ="remove",help ="Remove a boost notification channel")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,3 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_channel_remove (self ,ctx ,channel :discord .TextChannel ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
channels =data ["boost"]["channel"]
|
||||
|
||||
if not channels :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} No boost channels are currently set up."))
|
||||
return
|
||||
|
||||
if str (channel .id )not in channels :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} This channel is not in the boost channels list."))
|
||||
return
|
||||
|
||||
channels .remove (str (channel .id ))
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Successfully removed {channel.mention} from boost channels list."))
|
||||
|
||||
@_boost .command (name ="test",help ="Test how the boost message will look")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_test (self ,ctx ):
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
channels =data ["boost"]["channel"]
|
||||
|
||||
if not channels :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} Please set up a boost channel first using `boost channel add #channel`."))
|
||||
return
|
||||
|
||||
|
||||
formatted_message =self .format_boost_message (data ["boost"]["message"],ctx .author ,ctx .guild )
|
||||
|
||||
|
||||
channel =self .bot .get_channel (int (channels [0 ]))
|
||||
if not channel :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} The configured boost channel no longer exists."))
|
||||
|
||||
return
|
||||
|
||||
try :
|
||||
if data ["boost"]["embed"]:
|
||||
embed =discord .Embed (description =formatted_message ,color =self .color )
|
||||
embed .set_author (name =ctx .author .display_name ,icon_url =ctx .author .display_avatar .url )
|
||||
embed .timestamp =discord .utils .utcnow ()
|
||||
|
||||
if data ["boost"]["image"]:
|
||||
embed .set_image (url =data ["boost"]["image"])
|
||||
|
||||
if data ["boost"]["thumbnail"]:
|
||||
embed .set_thumbnail (url =data ["boost"]["thumbnail"])
|
||||
|
||||
if ctx .guild .icon :
|
||||
embed .set_footer (text =ctx .guild .name ,icon_url =ctx .guild .icon .url )
|
||||
|
||||
ping_content =ctx .author .mention if data ["boost"]["ping"]else ""
|
||||
await channel .send (ping_content ,embed =embed )
|
||||
else :
|
||||
await channel .send (formatted_message )
|
||||
|
||||
except discord .Forbidden :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} I don't have permission to send messages in the boost channel."))
|
||||
|
||||
except Exception as e :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} An error occurred: `{str(e)}`"))
|
||||
|
||||
|
||||
@_boost .command (name ="config",help ="View current boost configuration")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_config (self ,ctx ):
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
channels =data ["boost"]["channel"]
|
||||
|
||||
if not channels :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} Please set up a boost channel first using `boost channel add #channel`."))
|
||||
|
||||
return
|
||||
|
||||
channel_mentions =[]
|
||||
for channel_id in channels :
|
||||
channel =self .bot .get_channel (int (channel_id ))
|
||||
if channel :
|
||||
channel_mentions .append (channel .mention )
|
||||
|
||||
embed_status = f"{TICK} Enabled" if data["boost"]["embed"] else f"{CROSS} Disabled"
|
||||
ping_status = f"{TICK} Enabled" if data["boost"]["ping"] else f"{CROSS} Disabled"
|
||||
autodel_status = f"{data['boost']['autodel']}s" if data["boost"]["autodel"] else "Disabled"
|
||||
channels_str = "\n".join(channel_mentions) if channel_mentions else "None"
|
||||
|
||||
config_text = (
|
||||
f"**Channels**\n{channels_str}\n\n"
|
||||
f"**Message**\n```{data['boost']['message']}```\n"
|
||||
f"**Embed:** {embed_status}\n"
|
||||
f"**Ping:** {ping_status}\n"
|
||||
f"**Auto-delete:** {autodel_status}"
|
||||
)
|
||||
|
||||
await ctx.send(view=CV2(f"{NITRO_BOOST} Boost Configuration for {ctx.guild.name}", config_text))
|
||||
|
||||
@_boost .command (name ="reset",help ="Reset boost configuration")
|
||||
@commands .cooldown (1 ,5 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boost_reset (self ,ctx ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
|
||||
if not data ["boost"]["channel"]:
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} No boost configuration found to reset."))
|
||||
return
|
||||
|
||||
|
||||
data ["boost"]["channel"]=[]
|
||||
data ["boost"]["image"]=""
|
||||
data ["boost"]["message"]="{user.mention} just boosted {server.name}! 🎉"
|
||||
data ["boost"]["thumbnail"]=""
|
||||
data ["boost"]["embed"]=True
|
||||
data ["boost"]["ping"]=False
|
||||
data ["boost"]["autodel"]=0
|
||||
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Successfully reset all boost configuration."))
|
||||
|
||||
@commands .group (name ="boostrole",invoke_without_command =True ,help ="Manage boost roles")
|
||||
@commands .cooldown (1 ,5 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boostrole (self ,ctx ):
|
||||
if ctx .subcommand_passed is None :
|
||||
await ctx .send_help (ctx .command )
|
||||
ctx .command .reset_cooldown (ctx )
|
||||
|
||||
@_boostrole .command (name ="config",help ="View boost role configuration")
|
||||
@commands .cooldown (1 ,5 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boostrole_config (self ,ctx ):
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
role_ids =data ["boost_roles"]["roles"]
|
||||
|
||||
if not role_ids :
|
||||
await ctx.send(view=CV2(f"Boost Roles - {ctx.guild.name}", "No boost roles configured."))
|
||||
return
|
||||
|
||||
roles =[]
|
||||
for role_id in role_ids :
|
||||
role =ctx .guild .get_role (int (role_id ))
|
||||
if role :
|
||||
roles .append (role .mention )
|
||||
|
||||
roles_str = "\n".join(roles) if roles else "No valid roles found"
|
||||
await ctx.send(view=CV2(f"Boost Roles - {ctx.guild.name}", roles_str))
|
||||
|
||||
@_boostrole .command (name ="add",help ="Add a boost role")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,3 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boostrole_add (self ,ctx ,role :discord .Role ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
roles =data ["boost_roles"]["roles"]
|
||||
|
||||
if len (roles )>=10 :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} Maximum boost role limit reached (10 roles)."))
|
||||
return
|
||||
|
||||
if str (role .id )in roles :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} {role.mention} is already a boost role."))
|
||||
return
|
||||
|
||||
roles .append (str (role .id ))
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} {role.mention} has been added as a boost role."))
|
||||
|
||||
@_boostrole .command (name ="remove",help ="Remove a boost role")
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .cooldown (1 ,3 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boostrole_remove (self ,ctx ,role :discord .Role ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
roles =data ["boost_roles"]["roles"]
|
||||
|
||||
if not roles :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} No boost roles are currently configured."))
|
||||
return
|
||||
|
||||
if str (role .id )not in roles :
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} {role.mention} is not a boost role."))
|
||||
return
|
||||
|
||||
roles .remove (str (role .id ))
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} {role.mention} has been removed from boost roles."))
|
||||
|
||||
@_boostrole .command (name ="reset",help ="Reset boost role configuration")
|
||||
@commands .cooldown (1 ,3 ,commands .BucketType .user )
|
||||
@commands .max_concurrency (1 ,per =commands .BucketType .default ,wait =False )
|
||||
@commands .guild_only ()
|
||||
@blacklist_check ()
|
||||
@ignore_check ()
|
||||
@commands .has_permissions (administrator =True )
|
||||
async def _boostrole_reset (self ,ctx ):
|
||||
if not self .is_authorized (ctx ):
|
||||
await self .send_permission_error (ctx )
|
||||
return
|
||||
|
||||
data =await self .get_boost_config (ctx .guild .id )
|
||||
|
||||
if not data ["boost_roles"]["roles"]:
|
||||
await ctx.send(view=CV2("Error", f"{CROSS} No boost roles are currently configured."))
|
||||
return
|
||||
|
||||
data ["boost_roles"]["roles"]=[]
|
||||
await self .update_boost_config (ctx .guild .id ,data )
|
||||
|
||||
await ctx.send(view=CV2("Success", f"{TICK} Successfully cleared all boost roles."))
|
||||
|
||||
async def setup (bot ):
|
||||
await bot .add_cog (Booster(bot ))
|
||||
122
bot/cogs/commands/calc.py
Normal file
122
bot/cogs/commands/calc.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from discord.ext import commands
|
||||
from discord.ui import View, Button, button
|
||||
import discord
|
||||
|
||||
|
||||
class CalculatorView(View):
|
||||
def __init__(self, author: discord.Member):
|
||||
super().__init__()
|
||||
self.author = author
|
||||
self.value = ""
|
||||
self.message = None
|
||||
|
||||
# Button interactions
|
||||
@button(label="1", style=discord.ButtonStyle.grey, row=0)
|
||||
async def one(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "1")
|
||||
|
||||
@button(label="2", style=discord.ButtonStyle.grey, row=0)
|
||||
async def two(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "2")
|
||||
|
||||
@button(label="3", style=discord.ButtonStyle.grey, row=0)
|
||||
async def three(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "3")
|
||||
|
||||
@button(label="4", style=discord.ButtonStyle.grey, row=1)
|
||||
async def four(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "4")
|
||||
|
||||
@button(label="5", style=discord.ButtonStyle.grey, row=1)
|
||||
async def five(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "5")
|
||||
|
||||
@button(label="6", style=discord.ButtonStyle.grey, row=1)
|
||||
async def six(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "6")
|
||||
|
||||
@button(label="7", style=discord.ButtonStyle.grey, row=2)
|
||||
async def seven(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "7")
|
||||
|
||||
@button(label="8", style=discord.ButtonStyle.grey, row=2)
|
||||
async def eight(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "8")
|
||||
|
||||
@button(label="9", style=discord.ButtonStyle.grey, row=2)
|
||||
async def nine(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "9")
|
||||
|
||||
@button(label="0", style=discord.ButtonStyle.grey, row=3)
|
||||
async def zero(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "0")
|
||||
|
||||
@button(label="+", style=discord.ButtonStyle.blurple, row=3)
|
||||
async def add(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "+")
|
||||
|
||||
@button(label="-", style=discord.ButtonStyle.blurple, row=3)
|
||||
async def subtract(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "-")
|
||||
|
||||
@button(label="*", style=discord.ButtonStyle.blurple, row=3)
|
||||
async def multiply(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "*")
|
||||
|
||||
@button(label="/", style=discord.ButtonStyle.blurple, row=3)
|
||||
async def divide(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "/")
|
||||
|
||||
@button(label="=", style=discord.ButtonStyle.green, row=4)
|
||||
async def equals(self, interaction: discord.Interaction, button: Button):
|
||||
if interaction.user != self.author:
|
||||
return await interaction.response.send_message(
|
||||
"This is not your embed.", ephemeral=True
|
||||
)
|
||||
try:
|
||||
expression = self.value.strip().replace("\n", "")
|
||||
result = str(eval(expression))
|
||||
await self.update_embed(interaction, result)
|
||||
self.value = result # Store the result for possible further calculations
|
||||
except:
|
||||
await self.update_embed(interaction, "Error")
|
||||
|
||||
@button(label="Clear", style=discord.ButtonStyle.red, row=4)
|
||||
async def clear(self, interaction: discord.Interaction, button: Button):
|
||||
await self.update_value(interaction, "Clear")
|
||||
|
||||
async def update_value(self, interaction: discord.Interaction, value: str):
|
||||
# Check if the person interacting is the author of the embed
|
||||
if interaction.user != self.author:
|
||||
return await interaction.response.send_message(
|
||||
"This content does not appear to be part of your embedded materials.", ephemeral=True
|
||||
)
|
||||
# Append the value or clear if "Clear"
|
||||
if value == "Clear":
|
||||
self.value = ""
|
||||
else:
|
||||
self.value += value
|
||||
# Update the embed with the new value
|
||||
await self.update_embed(interaction, self.value)
|
||||
|
||||
async def update_embed(self, interaction: discord.Interaction, result: str):
|
||||
content = f"**Calculator** | `{self.author.display_name}`\n```\n{result}\n```"
|
||||
await interaction.response.edit_message(content=content, view=self)
|
||||
self.message = interaction.message
|
||||
|
||||
class calculator(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(name='calculator', help='Starts a calculator session', aliases=['calc', 'calculate', 'math'])
|
||||
async def calculator(self, ctx):
|
||||
"""Starts a new calculator session."""
|
||||
# Ensure we pass the author to the view so it knows who triggered it
|
||||
view = CalculatorView(author=ctx.author)
|
||||
# We store the message so we know what to edit and update later
|
||||
view.message = await ctx.send(content="**Calculator**\n```\n \n```", view=view)
|
||||
|
||||
# Add the cog to the bot
|
||||
def setup(bot):
|
||||
bot.add_cog(calculator(bot))
|
||||
|
||||
194
bot/cogs/commands/counting.py
Normal file
194
bot/cogs/commands/counting.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import discord
|
||||
from utils.emoji import ARROWRED, CROSS, NEXT_ALT1, REDRULESBOOK, RED_BUTTON, RED_PIN, STAR, TICK, ZBACK, ZPAUSE, ZPLAY, ZWARNING
|
||||
from discord.ext import commands
|
||||
import json
|
||||
import os
|
||||
import asyncio
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
|
||||
# Emoji Variables
|
||||
CROSS = CROSS
|
||||
TICK = TICK
|
||||
WARNING = ZWARNING
|
||||
WARNING = ZWARNING
|
||||
BOOK = REDRULESBOOK
|
||||
PLAY = ZPLAY
|
||||
PAUSE = ZPAUSE
|
||||
STOP = RED_BUTTON
|
||||
NEXT = NEXT_ALT1
|
||||
BACK = ZBACK
|
||||
ARROW = ARROWRED
|
||||
PIN = RED_PIN
|
||||
STAR = STAR
|
||||
|
||||
class Counting(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.data_file = "db/counting.json"
|
||||
if not os.path.exists(self.data_file):
|
||||
with open(self.data_file, 'w') as f:
|
||||
json.dump({}, f)
|
||||
with open(self.data_file, 'r') as f:
|
||||
self.counting_data = json.load(f)
|
||||
|
||||
def save_data(self):
|
||||
with open(self.data_file, 'w') as f:
|
||||
json.dump(self.counting_data, f, indent=4)
|
||||
|
||||
def is_enabled(self, guild_id):
|
||||
guild_id = str(guild_id)
|
||||
return self.counting_data.get(guild_id, {}).get("enabled", False)
|
||||
|
||||
async def not_enabled_embed(self, ctx):
|
||||
await ctx.send(view=CV2(
|
||||
f"{BOOK} Counting Settings For {ctx.guild.name}",
|
||||
f"**Current Status:** {CROSS} Disabled",
|
||||
"**How to Enable:** Use `counting enable` to enable counting."
|
||||
))
|
||||
|
||||
async def send_help_embed(self, ctx):
|
||||
await ctx.send(view=CV2(
|
||||
f"{BOOK} Counting Commands",
|
||||
"Manage and control the counting game settings.\n\n"
|
||||
"**counting enable/disable** — Enable or Disable counting in server\n"
|
||||
"**counting channel #channel** — Set counting channel\n"
|
||||
"**counting config reset/continue** — Set reset mode on mistake\n"
|
||||
"**counting reset** — Reset counting back to 0\n"
|
||||
"**counting stats** — View current counting stats"
|
||||
))
|
||||
|
||||
@commands.group(name="counting", invoke_without_command=True)
|
||||
async def counting(self, ctx):
|
||||
if not self.is_enabled(ctx.guild.id):
|
||||
await self.not_enabled_embed(ctx)
|
||||
else:
|
||||
await self.send_help_embed(ctx)
|
||||
|
||||
@counting.command(name="enable")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def enable(self, ctx):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if guild_id not in self.counting_data:
|
||||
self.counting_data[guild_id] = {"enabled": True, "channel": None, "count": 0, "reset_on_fail": False}
|
||||
else:
|
||||
self.counting_data[guild_id]["enabled"] = True
|
||||
self.save_data()
|
||||
await ctx.send(view=CV2("Counting", f"{TICK} Counting has been Enabled!"))
|
||||
|
||||
@counting.command(name="disable")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def disable(self, ctx):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if guild_id not in self.counting_data:
|
||||
await self.not_enabled_embed(ctx)
|
||||
return
|
||||
self.counting_data[guild_id]["enabled"] = False
|
||||
self.save_data()
|
||||
await ctx.send(view=CV2("Counting", f"{STOP} Counting has been Disabled!"))
|
||||
|
||||
@counting.command(name="channel")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def channel(self, ctx, channel: discord.TextChannel):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if not self.is_enabled(guild_id):
|
||||
await self.not_enabled_embed(ctx)
|
||||
return
|
||||
self.counting_data[guild_id]["channel"] = channel.id
|
||||
self.save_data()
|
||||
await ctx.send(view=CV2("Counting", f"{PIN} Counting channel set to {channel.mention}"))
|
||||
|
||||
@counting.command(name="config")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def config(self, ctx, mode: str):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if not self.is_enabled(guild_id):
|
||||
await self.not_enabled_embed(ctx)
|
||||
return
|
||||
if mode.lower() in ["reset", "true", "on"]:
|
||||
self.counting_data[guild_id]["reset_on_fail"] = True
|
||||
msg = f"{TICK} Counting will now reset on mistakes."
|
||||
elif mode.lower() in ["continue", "false", "off"]:
|
||||
self.counting_data[guild_id]["reset_on_fail"] = False
|
||||
msg = f"{TICK} Counting will now continue on mistakes."
|
||||
else:
|
||||
await ctx.send(f"{CROSS} Invalid mode! Use `reset` or `continue`.")
|
||||
return
|
||||
self.save_data()
|
||||
await ctx.send(view=CV2("Counting", msg))
|
||||
|
||||
@counting.command(name="reset")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def reset(self, ctx):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if not self.is_enabled(guild_id):
|
||||
await self.not_enabled_embed(ctx)
|
||||
return
|
||||
self.counting_data[guild_id]["count"] = 0
|
||||
self.save_data()
|
||||
await ctx.send(view=CV2("Counting", f"{NEXT} Counting has been reset to 0!"))
|
||||
|
||||
@counting.command(name="stats")
|
||||
async def stats(self, ctx):
|
||||
guild_id = str(ctx.guild.id)
|
||||
if not self.is_enabled(guild_id):
|
||||
await self.not_enabled_embed(ctx)
|
||||
return
|
||||
data = self.counting_data[guild_id]
|
||||
channel = ctx.guild.get_channel(data["channel"]) if data["channel"] else None
|
||||
channel_str = channel.mention if channel else "Not Set"
|
||||
reset_str = f"{TICK} Yes" if data["reset_on_fail"] else f"{CROSS} No"
|
||||
await ctx.send(view=CV2(
|
||||
f"{BOOK} Counting Stats",
|
||||
f"**Current Count:** {data['count']}\n"
|
||||
f"**Channel:** {channel_str}\n"
|
||||
f"**Reset on Mistake:** {reset_str}"
|
||||
))
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.bot:
|
||||
return
|
||||
|
||||
guild_id = str(message.guild.id)
|
||||
if guild_id not in self.counting_data:
|
||||
return
|
||||
|
||||
data = self.counting_data[guild_id]
|
||||
if not data.get("enabled", False):
|
||||
return
|
||||
|
||||
if message.channel.id != data.get("channel"):
|
||||
return
|
||||
|
||||
content = message.content.strip()
|
||||
|
||||
if not content.isdigit():
|
||||
msg = await message.channel.send(f"{WARNING} Alphabet not allowed!")
|
||||
await asyncio.sleep(3)
|
||||
await msg.delete()
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
number = int(content)
|
||||
expected_number = data.get("count", 0) + 1
|
||||
|
||||
if number != expected_number:
|
||||
msg = await message.channel.send(f"{CROSS} Wrong number entered! Expected number is **{expected_number}**")
|
||||
await asyncio.sleep(3)
|
||||
await msg.delete()
|
||||
await message.delete()
|
||||
if data.get("reset_on_fail", False):
|
||||
self.counting_data[guild_id]["count"] = 0
|
||||
self.save_data()
|
||||
return
|
||||
|
||||
# Correct number
|
||||
self.counting_data[guild_id]["count"] = number
|
||||
self.save_data()
|
||||
await message.add_reaction(TICK)
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Counting(bot))
|
||||
543
bot/cogs/commands/customrole.py
Normal file
543
bot/cogs/commands/customrole.py
Normal file
@@ -0,0 +1,543 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK, ZWARNING
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
import aiosqlite
|
||||
import asyncio
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
from typing import List, Tuple
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from utils.config import *
|
||||
|
||||
|
||||
|
||||
DATABASE_PATH = 'db/customrole.db'
|
||||
DATABASE_PATH2 = 'db/np.db'
|
||||
|
||||
|
||||
class Customrole(commands.Cog):
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.cooldown = {}
|
||||
self.rate_limit = {}
|
||||
self.rate_limit_timeout = 5
|
||||
|
||||
|
||||
self.bot.loop.create_task(self.create_tables())
|
||||
|
||||
|
||||
async def reset_rate_limit(self, user_id):
|
||||
await asyncio.sleep(self.rate_limit_timeout)
|
||||
self.rate_limit.pop(user_id, None)
|
||||
|
||||
|
||||
|
||||
|
||||
async def add_role(self, *, role_id: int, member: discord.Member):
|
||||
if member.guild.me.guild_permissions.manage_roles:
|
||||
role = discord.Object(id=role_id)
|
||||
await member.add_roles(role, reason=f"{BRAND_NAME} Customrole | Role Added")
|
||||
else:
|
||||
raise discord.Forbidden("Bot does not have permission to manage roles.")
|
||||
|
||||
|
||||
|
||||
async def remove_role(self, *, role_id: int, member: discord.Member):
|
||||
if member.guild.me.guild_permissions.manage_roles:
|
||||
role = discord.Object(id=role_id)
|
||||
await member.remove_roles(role, reason=f"{BRAND_NAME} Customrole | Role Removed")
|
||||
else:
|
||||
raise discord.Forbidden("Bot does not have permission to manage roles.")
|
||||
|
||||
|
||||
|
||||
async def add_role2(self, *, role: int, member: discord.Member):
|
||||
if member.guild.me.guild_permissions.manage_roles:
|
||||
role = discord.Object(id=int(role))
|
||||
await member.add_roles(role, reason=f"{BRAND_NAME} Customrole | Role Added ")
|
||||
|
||||
async def remove_role2(self, *, role: int, member: discord.Member):
|
||||
if member.guild.me.guild_permissions.manage_roles:
|
||||
role = discord.Object(id=int(role))
|
||||
await member.remove_roles(role, reason=f"{BRAND_NAME} Customrole| Role Removed")
|
||||
|
||||
|
||||
|
||||
async def handle_role_command(self, context: Context, member: discord.Member, role_type: str):
|
||||
async with aiosqlite.connect('db/customrole.db') as db:
|
||||
async with db.execute(f"SELECT reqrole, {role_type} FROM roles WHERE guild_id = ?", (context.guild.id,)) as cursor:
|
||||
data = await cursor.fetchone()
|
||||
if data:
|
||||
reqrole_id, role_id = data
|
||||
reqrole = context.guild.get_role(reqrole_id)
|
||||
role = context.guild.get_role(role_id)
|
||||
|
||||
if reqrole:
|
||||
if context.author == context.guild.owner or reqrole in context.author.roles:
|
||||
if role:
|
||||
if role not in member.roles:
|
||||
await self.add_role2(role=role_id, member=member)
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"**Given** <@&{role.id}> To {member.mention}"))
|
||||
else:
|
||||
await self.remove_role2(role=role_id, member=member)
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"**Removed** <@&{role.id}> From {member.mention}"))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{CROSS} Error", f"{role_type.capitalize()} role is not set up in {context.guild.name}"))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", f"You need {reqrole.mention} to run this command."))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", f"Required role is not set up in {context.guild.name}"))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{CROSS} Error", f"Roles configuration is not set up in {context.guild.name}"))
|
||||
|
||||
|
||||
|
||||
|
||||
async def create_tables(self):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
staff INTEGER,
|
||||
girl INTEGER,
|
||||
vip INTEGER,
|
||||
guest INTEGER,
|
||||
frnd INTEGER,
|
||||
reqrole INTEGER
|
||||
)
|
||||
''')
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS custom_roles (
|
||||
guild_id INTEGER,
|
||||
name TEXT,
|
||||
role_id INTEGER,
|
||||
PRIMARY KEY (guild_id, name)
|
||||
)
|
||||
''')
|
||||
await db.commit()
|
||||
|
||||
|
||||
@commands.hybrid_group(name="setup",
|
||||
description="Setups custom roles for the server.",
|
||||
help="Setups custom roles for the server.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def set(self, context: Context):
|
||||
if context.subcommand_passed is None:
|
||||
await context.send_help(context.command)
|
||||
context.command.reset_cooldown(context)
|
||||
|
||||
async def fetch_role_data(self, guild_id):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT staff, girl, vip, guest, frnd, reqrole FROM roles WHERE guild_id = ?", (guild_id,)) as cursor:
|
||||
return await cursor.fetchone()
|
||||
|
||||
|
||||
|
||||
|
||||
async def update_role_data(self, guild_id, column, value):
|
||||
try:
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute(f"INSERT OR REPLACE INTO roles (guild_id, {column}) VALUES (?, ?) ON CONFLICT(guild_id) DO UPDATE SET {column} = ?",
|
||||
(guild_id, value, value))
|
||||
await db.commit()
|
||||
except Exception as e:
|
||||
print(f"Error updating role data: {e}")
|
||||
|
||||
|
||||
async def fetch_custom_role_data(self, guild_id):
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT name, role_id FROM custom_roles WHERE guild_id = ?", (guild_id,)) as cursor:
|
||||
return await cursor.fetchall()
|
||||
|
||||
|
||||
@set.command(name="staff",
|
||||
description="Setup staff role in guild",
|
||||
help="Setup staff role in Guild")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@app_commands.describe(role="Role to be added")
|
||||
async def staff(self, context: Context, role: discord.Role) -> None:
|
||||
if context.author == context.guild.owner or context.author.top_role.position > context.guild.me.top_role.position:
|
||||
await self.update_role_data(context.guild.id, 'staff', role.id)
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"Added {role.mention} to `Staff` Role\n\n__**How to Use?**__\nUse `staff <user>` Command to **Add {role.mention}** role to User & use again to the same user to **Remove role**."))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", "Your role should be above my top role."))
|
||||
|
||||
@set.command(name="girl",
|
||||
description="Setup girl role in the Guild",
|
||||
help="Setup girl role in the Guild")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@app_commands.describe(role="Role to be added")
|
||||
async def girl(self, context: Context, role: discord.Role) -> None:
|
||||
if context.author == context.guild.owner or context.author.top_role.position > context.guild.me.top_role.position:
|
||||
await self.update_role_data(context.guild.id, 'girl', role.id)
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"Added {role.mention} to `Girl` Role\n\n__**How to Use?**__\nUse `girl <user>` Command to **Add {role.mention}** role to User & use again to the same user to **Remove role**."))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", "Your role should be above my top role."))
|
||||
|
||||
@set.command(name="vip",
|
||||
description="Setups vip role in the Guild",
|
||||
help="Setups vip role in the Guild")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@app_commands.describe(role="Role to be added")
|
||||
async def vip(self, context: Context, role: discord.Role) -> None:
|
||||
if context.author == context.guild.owner or context.author.top_role.position > context.guild.me.top_role.position:
|
||||
await self.update_role_data(context.guild.id, 'vip', role.id)
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"Added {role.mention} to `VIP` Role\n\n__**How to Use?**__\nUse `vip <user>` Command to **Add {role.mention}** role to User & use again to the same user to **Remove role**."))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", "Your role should be above my top role."))
|
||||
|
||||
@set.command(name="guest",
|
||||
description="Setup guest role in the Guild",
|
||||
help="Setup guest role in the Guild")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@app_commands.describe(role="Role to be added")
|
||||
async def guest(self, context: Context, role: discord.Role) -> None:
|
||||
if context.author == context.guild.owner or context.author.top_role.position > context.guild.me.top_role.position:
|
||||
await self.update_role_data(context.guild.id, 'guest', role.id)
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"Added {role.mention} to `Guest` Role\n\n__**How to Use?**__\nUse `guest <user>` Command to **Add {role.mention}** role to User & use again to the same user to **Remove role**."))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", "Your role should be above my top role."))
|
||||
|
||||
@set.command(name="friend",
|
||||
description="Setup friend role in the Guild",
|
||||
help="Setup friend role in the Guild")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@app_commands.describe(role="Role to be added")
|
||||
async def friend(self, context: Context, role: discord.Role) -> None:
|
||||
if context.author == context.guild.owner or context.author.top_role.position > context.guild.me.top_role.position:
|
||||
await self.update_role_data(context.guild.id, 'frnd', role.id)
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"Added {role.mention} to `Friend` Role\n\n__**How to Use?**__\nUse `friend <user>` Command to **Add {role.mention}** role to User & use again to the same user to **Remove role**."))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", "Your role should be above my top role."))
|
||||
|
||||
@set.command(name="reqrole",
|
||||
description="Setup required role for custom role commands",
|
||||
help="Setup required role for custom role commands")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@app_commands.describe(role="Role to be added")
|
||||
async def req_role(self, context: Context, role: discord.Role) -> None:
|
||||
if context.author == context.guild.owner or context.author.top_role.position > context.guild.me.top_role.position:
|
||||
await self.update_role_data(context.guild.id, 'reqrole', role.id)
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"Added {role.mention} for Required role to run custom role commands in {context.guild.name}"))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", "Your role should be above my top role."))
|
||||
|
||||
|
||||
|
||||
@set.command(name="config",
|
||||
description="Shows the current custom role configuration in the Guild.",
|
||||
help="Shows the current custom role configuration in the Guild.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def config(self, context: Context) -> None:
|
||||
role_data = await self.fetch_role_data(context.guild.id)
|
||||
if role_data:
|
||||
staff = context.guild.get_role(role_data[0]).mention if role_data[0] else "None"
|
||||
girl = context.guild.get_role(role_data[1]).mention if role_data[1] else "None"
|
||||
vip = context.guild.get_role(role_data[2]).mention if role_data[2] else "None"
|
||||
guest = context.guild.get_role(role_data[3]).mention if role_data[3] else "None"
|
||||
friend = context.guild.get_role(role_data[4]).mention if role_data[4] else "None"
|
||||
reqrole = context.guild.get_role(role_data[5]).mention if role_data[5] else "None"
|
||||
config_text = (
|
||||
f"**Staff Role:** {staff}\n"
|
||||
f"**Girl Role:** {girl}\n"
|
||||
f"**VIP Role:** {vip}\n"
|
||||
f"**Guest Role:** {guest}\n"
|
||||
f"**Friend Role:** {friend}\n"
|
||||
f"**Required Role:** {reqrole}\n\n"
|
||||
"Use Commands to assign role & use again to the same user to remove role."
|
||||
)
|
||||
await context.reply(view=CV2("Custom Role Configuration", config_text))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{CROSS} Error", "No custom role configuration found in this Guild."))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@set.command(name="create",
|
||||
description="Creates a custom role command.",
|
||||
help="Creates a custom role command")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@app_commands.describe(name="Command name", role="Role to be assigned")
|
||||
async def create(self, context: Context, name: str, role: discord.Role) -> None:
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT COUNT(*) FROM custom_roles WHERE guild_id = ?", (context.guild.id,)) as cursor:
|
||||
count = await cursor.fetchone()
|
||||
if count[0] >= 56:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Limit Reached", "You have reached the maximum limit of 56 custom role commands for this guild."))
|
||||
return
|
||||
|
||||
async with db.execute("SELECT name FROM custom_roles WHERE guild_id = ?", (context.guild.id,)) as cursor:
|
||||
existing_role = await cursor.fetchall()
|
||||
if any(name == row[0] for row in existing_role):
|
||||
await context.reply(view=CV2(f"{CROSS} Error", f"A custom role command with the name `{name}` already exists in this guild. Remove it before creating a new one."))
|
||||
return
|
||||
|
||||
await db.execute("INSERT INTO custom_roles (guild_id, name, role_id) VALUES (?, ?, ?)",
|
||||
(context.guild.id, name, role.id))
|
||||
await db.commit()
|
||||
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"Custom role command `{name}` created to assign the role {role.mention}.\n\n__**How to Use?**__\nUse `{name} <user>` Command to Assign/Remove {role.mention} role to User.\n> This will work for the users having `Manage Roles` permissions."))
|
||||
|
||||
|
||||
@set.command(name="delete", aliases=["remove"],
|
||||
description="Deletes a custom role command.",
|
||||
help="Deletes a custom role command.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@app_commands.describe(name="Command name to be deleted")
|
||||
async def delete(self, context: Context, name: str) -> None:
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT name FROM custom_roles WHERE guild_id = ? AND name = ?", (context.guild.id, name)) as cursor:
|
||||
existing_role = await cursor.fetchone()
|
||||
|
||||
if not existing_role:
|
||||
await context.reply(view=CV2(f"{CROSS} Error", f"No custom role command with the name `{name}` was found in this guild."))
|
||||
return
|
||||
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("DELETE FROM custom_roles WHERE guild_id = ? AND name = ?", (context.guild.id, name))
|
||||
await db.commit()
|
||||
|
||||
await context.reply(view=CV2(f"{TICK} Success", f"Custom role command `{name}` has been deleted."))
|
||||
|
||||
|
||||
@set.command(
|
||||
name="list",
|
||||
description="List all the custom roles setup for the server.",
|
||||
help="List all the custom roles setup for the server."
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def list(self, context: Context) -> None:
|
||||
custom_roles = await self.fetch_custom_role_data(context.guild.id)
|
||||
|
||||
if not custom_roles:
|
||||
await context.reply(view=CV2(f"{CROSS} Error", "No custom roles have been created for this server."))
|
||||
return
|
||||
|
||||
|
||||
def chunk_list(data: List[Tuple[str, int]], chunk_size: int):
|
||||
"""Yield successive chunks of `chunk_size` from `data`."""
|
||||
for i in range(0, len(data), chunk_size):
|
||||
yield data[i:i + chunk_size]
|
||||
|
||||
|
||||
chunks = list(chunk_list(custom_roles, 7))
|
||||
|
||||
for i, chunk in enumerate(chunks):
|
||||
roles_text = ""
|
||||
for name, role_id in chunk:
|
||||
role = context.guild.get_role(role_id)
|
||||
if role:
|
||||
roles_text += f"**{name}** → {role.mention}\n"
|
||||
footer = f"Page {i+1}/{len(chunks)} | These commands are usable by Members having Manage Role permissions."
|
||||
await context.reply(view=CV2("Custom Roles", roles_text, footer))
|
||||
|
||||
|
||||
@set.command(name="reset",
|
||||
description="Resets custom role configuration for the server.",
|
||||
help="Resets custom role configuration for the server.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def reset(self, context: Context) -> None:
|
||||
if context.author == context.guild.owner or context.author.top_role.position > context.guild.me.top_role.position:
|
||||
removed_roles = []
|
||||
role_data = await self.fetch_role_data(context.guild.id)
|
||||
if role_data:
|
||||
roles = ["staff", "girl", "vip", "guest", "frnd", "reqrole"]
|
||||
for i, role_name in enumerate(roles):
|
||||
role_id = role_data[i]
|
||||
if role_id:
|
||||
role = context.guild.get_role(role_id)
|
||||
if role:
|
||||
removed_roles.append(f"**{role_name.capitalize()}:** {role.mention}")
|
||||
await self.update_role_data(context.guild.id, role_name, None)
|
||||
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
await db.execute("DELETE FROM custom_roles WHERE guild_id = ?", (context.guild.id,))
|
||||
await db.commit()
|
||||
reset_desc = f"Deleted All Custom Role commands {TICK}\n\n**Removed Roles:**\n" + "\n".join(removed_roles) if removed_roles else "No roles were previously set."
|
||||
await context.reply(view=CV2("Custom Role Configuration Reset", reset_desc))
|
||||
else:
|
||||
await context.reply(view=CV2("Info", "No configuration found for this server."))
|
||||
else:
|
||||
await context.reply(view=CV2(f"{ZWARNING} Access Denied", "Your role should be above my top role."))
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message: discord.Message):
|
||||
|
||||
if message.author.bot or not message.content:
|
||||
return
|
||||
|
||||
prefixes = await self.bot.get_prefix(message)
|
||||
|
||||
|
||||
if not prefixes:
|
||||
return
|
||||
|
||||
|
||||
if not any(message.content.startswith(prefix) for prefix in prefixes):
|
||||
return
|
||||
|
||||
|
||||
for prefix in prefixes:
|
||||
if message.content.startswith(prefix):
|
||||
command_name = message.content[len(prefix):].split()[0]
|
||||
break
|
||||
else:
|
||||
return
|
||||
|
||||
guild_id = message.guild.id
|
||||
|
||||
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT role_id FROM custom_roles WHERE guild_id = ? AND name = ?", (guild_id, command_name)) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
|
||||
if result:
|
||||
role_id = result[0]
|
||||
role = message.guild.get_role(role_id)
|
||||
|
||||
|
||||
async with aiosqlite.connect(DATABASE_PATH) as db:
|
||||
async with db.execute("SELECT reqrole FROM roles WHERE guild_id = ?", (guild_id,)) as cursor:
|
||||
reqrole_result = await cursor.fetchone()
|
||||
|
||||
reqrole_id = reqrole_result[0] if reqrole_result else None
|
||||
reqrole = message.guild.get_role(reqrole_id) if reqrole_id else None
|
||||
|
||||
|
||||
if reqrole is None:
|
||||
await message.channel.send(f"{ZWARNING} The required role is not set up in this server. Please set it up using `setup reqrole`.")
|
||||
return
|
||||
|
||||
|
||||
if reqrole not in message.author.roles:
|
||||
await message.channel.send(view=CV2(f"{ZWARNING} Access Denied", f"You need the {reqrole.mention} role to use this command."))
|
||||
return
|
||||
|
||||
|
||||
member = message.mentions[0] if message.mentions else None
|
||||
if not member:
|
||||
await message.channel.send("Please mention a user to assign the role.")
|
||||
return
|
||||
|
||||
|
||||
now = asyncio.get_event_loop().time()
|
||||
if guild_id not in self.cooldown or now - self.cooldown[guild_id] >= 10:
|
||||
self.cooldown[guild_id] = now
|
||||
else:
|
||||
await message.channel.send("You're on a cooldown of 5 seconds. Please wait before sending another command.", delete_after=5)
|
||||
return
|
||||
|
||||
try:
|
||||
if role in member.roles:
|
||||
await self.remove_role(role_id=role_id, member=member)
|
||||
await message.channel.send(view=CV2(f"{TICK} Success", f"**Removed** the role {role.mention} from {member.mention}."))
|
||||
else:
|
||||
await self.add_role(role_id=role_id, member=member)
|
||||
await message.channel.send(view=CV2(f"{TICK} Success", f"**Added** the role {role.mention} to {member.mention}."))
|
||||
except discord.Forbidden as e:
|
||||
await message.channel.send("I do not have permission to manage this role to the given user.")
|
||||
print(f"Error: {e}")
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
|
||||
@commands.hybrid_command(name="staff",
|
||||
description="Gives the staff role to the user.",
|
||||
aliases=['official'],
|
||||
help="Gives the staff role to the user.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
#@commands.has_permissions(manage_roles=True)
|
||||
async def _staff(self, context: Context, member: discord.Member) -> None:
|
||||
await self.handle_role_command(context, member, 'staff')
|
||||
|
||||
@commands.hybrid_command(name="girl",
|
||||
description="Gives the girl role to the user.",
|
||||
aliases=['qt'],
|
||||
help="Gives the girl role to the user.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
#@commands.has_permissions(manage_roles=True)
|
||||
async def _girl(self, context: Context, member: discord.Member) -> None:
|
||||
await self.handle_role_command(context, member, 'girl')
|
||||
|
||||
@commands.hybrid_command(name="vip",
|
||||
description="Gives the VIP role to the user.",
|
||||
help="Gives the VIP role to the user.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
#@commands.has_permissions(manage_roles=True)
|
||||
async def _vip(self, context: Context, member: discord.Member) -> None:
|
||||
await self.handle_role_command(context, member, 'vip')
|
||||
|
||||
@commands.hybrid_command(name="guest",
|
||||
description="Gives the guest role to the user.",
|
||||
help="Gives the guest role to the user.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
#@commands.has_permissions(manage_roles=True)
|
||||
async def _guest(self, context: Context, member: discord.Member) -> None:
|
||||
await self.handle_role_command(context, member, 'guest')
|
||||
|
||||
@commands.hybrid_command(name="friend",
|
||||
description="Gives the friend role to the user.",
|
||||
aliases=['frnd'],
|
||||
help="Gives the friend role to the user.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
#@commands.has_permissions(manage_roles=True)
|
||||
async def _friend(self, context: Context, member: discord.Member) -> None:
|
||||
await self.handle_role_command(context, member, 'frnd')
|
||||
|
||||
|
||||
|
||||
105
bot/cogs/commands/dms.py
Normal file
105
bot/cogs/commands/dms.py
Normal file
@@ -0,0 +1,105 @@
|
||||
import discord
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container, Button, ActionRow
|
||||
from discord.ext import commands
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
authorized_staff_ids = [
|
||||
1263404140965396555,
|
||||
767979794411028491
|
||||
]
|
||||
|
||||
|
||||
class SuccessView(LayoutView):
|
||||
def __init__(self, member):
|
||||
super().__init__(timeout=None)
|
||||
self.member = member
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Message Sent**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
f"✅ Your message has been successfully sent to **{member.name}**"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ErrorView(LayoutView):
|
||||
def __init__(self, member):
|
||||
super().__init__(timeout=None)
|
||||
self.member = member
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Delivery Failed**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
f"❌ Could not send the message. **{member.name}** may have their DMs disabled."
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class GenericErrorView(LayoutView):
|
||||
def __init__(self, error):
|
||||
super().__init__(timeout=None)
|
||||
self.error = error
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Error Occurred**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"🤔 Something went wrong. Error: {error}"),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class PermissionErrorView(LayoutView):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Permission Denied**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("❌ You do not have permission to use this command."),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class StaffDMCog(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(name="dmstaff")
|
||||
async def dm_staff(self, ctx, member: discord.Member, *, message: str):
|
||||
if ctx.author.id not in authorized_staff_ids:
|
||||
view = PermissionErrorView()
|
||||
await ctx.reply(view=view)
|
||||
return
|
||||
|
||||
try:
|
||||
embed = discord.Embed(
|
||||
title="📢 A Message from the Staff Team",
|
||||
description=message,
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed.set_footer(text=f"This message was sent by {ctx.author.name}.")
|
||||
|
||||
await member.send(embed=embed)
|
||||
|
||||
view = SuccessView(member)
|
||||
await ctx.reply(view=view)
|
||||
|
||||
except discord.Forbidden:
|
||||
view = ErrorView(member)
|
||||
await ctx.reply(view=view)
|
||||
except Exception as e:
|
||||
view = GenericErrorView(str(e))
|
||||
await ctx.reply(view=view)
|
||||
print(f"Error in dmstaff command: {e}")
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(StaffDMCog(bot))
|
||||
794
bot/cogs/commands/emergency.py
Normal file
794
bot/cogs/commands/emergency.py
Normal file
@@ -0,0 +1,794 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, CROSS_ALT, ML_CROSS, TICK, TICK_ALT, ZWARNING
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container, Button, ActionRow
|
||||
from discord.ext import commands
|
||||
import aiosqlite
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
class EmergencyRestoreConfirmView(LayoutView):
|
||||
def __init__(self, ctx):
|
||||
super().__init__(timeout=60)
|
||||
self.ctx = ctx
|
||||
self.value = None
|
||||
|
||||
self.yes_btn = Button(label="Yes", style=discord.ButtonStyle.green)
|
||||
self.no_btn = Button(label="No", style=discord.ButtonStyle.danger)
|
||||
|
||||
self.yes_btn.callback = self.confirm_callback
|
||||
self.no_btn.callback = self.cancel_callback
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Confirm Restoration**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
"This will restore previously disabled permissions for emergency roles. Do you want to proceed?"
|
||||
),
|
||||
ActionRow(self.yes_btn, self.no_btn),
|
||||
)
|
||||
)
|
||||
|
||||
async def confirm_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
return await interaction.response.send_message(
|
||||
"Only the Server Owner can use this button.", ephemeral=True
|
||||
)
|
||||
self.value = True
|
||||
await interaction.response.defer()
|
||||
self.stop()
|
||||
|
||||
async def cancel_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
return await interaction.response.send_message(
|
||||
"Only the Server Owner can use this button.", ephemeral=True
|
||||
)
|
||||
self.value = False
|
||||
await interaction.response.defer()
|
||||
self.stop()
|
||||
|
||||
|
||||
class EmergencyMainView(LayoutView):
|
||||
def __init__(self, ctx, prefix):
|
||||
super().__init__(timeout=None)
|
||||
self.prefix = prefix
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("__Emergency Situation__"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
f"The `emergency` command group is designed to protect your server from malicious activity or accidental damage.\n\n"
|
||||
f"**The command group has several subcommands:**\n\n"
|
||||
f"`{prefix}emergency enable` - Enable emergency mode\n"
|
||||
f"`{prefix}emergency disable` - Disable emergency mode\n"
|
||||
f"`{prefix}emergency authorise` - Manage authorized users\n"
|
||||
f"`{prefix}emergency role` - Manage roles in emergency list\n"
|
||||
f"`{prefix}emergency-situation` - Execute emergency situation"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EnableSuccessView(LayoutView):
|
||||
def __init__(self, roles_added):
|
||||
super().__init__(timeout=None)
|
||||
content = (
|
||||
"\n".join([f"{r.mention}" for r in roles_added])
|
||||
if roles_added
|
||||
else "No new roles with dangerous permissions were found."
|
||||
)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{TICK} Success**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(content),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EnableErrorView(LayoutView):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{CROSS} Error**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("Only the server owner can enable emergency mode."),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class DisableSuccessView(LayoutView):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{TICK_ALT} Success**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
"Emergency mode has been disabled, and all emergency roles have been cleared."
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class DisableErrorView(LayoutView):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{CROSS_ALT} Error**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("Only the server owner can disable emergency mode."),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class AuthoriseSuccessView(LayoutView):
|
||||
def __init__(self, member_name, action):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{TICK_ALT} Success**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"**{member_name}** has been {action}."),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class AuthoriseErrorView(LayoutView):
|
||||
def __init__(self, error_type):
|
||||
super().__init__(timeout=None)
|
||||
content = {
|
||||
"owner_add": "Only the server owner can add authorised users.",
|
||||
"owner_remove": "Only the server owner can remove authorised users.",
|
||||
"owner_list": "Only the server owner can view the list.",
|
||||
"limit": "Only up to 5 authorised users can be added.",
|
||||
"exists": "This user is already authorised.",
|
||||
"not_found": "This user is not authorised.",
|
||||
}.get(error_type, "An error occurred.")
|
||||
is_warning = error_type == "limit"
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(
|
||||
f"**{ZWARNING} Access Denied**"
|
||||
if is_warning
|
||||
else f"**{CROSS_ALT} Error**"
|
||||
),
|
||||
Separator(visible=True),
|
||||
TextDisplay(content),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class AuthoriseListView(LayoutView):
|
||||
def __init__(self, ctx, users, is_owner):
|
||||
super().__init__(timeout=None)
|
||||
if not is_owner:
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{ZWARNING} Access Denied**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("Only the server owner can view the list."),
|
||||
)
|
||||
)
|
||||
elif not users:
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Authorized Users**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("No authorized users found."),
|
||||
)
|
||||
)
|
||||
else:
|
||||
desc = "\n".join(
|
||||
[
|
||||
f"{i + 1}. [{ctx.guild.get_member(u[0]).name}](https://discord.com/users/{u[0]}) - {u[0]}"
|
||||
for i, u in enumerate(users)
|
||||
]
|
||||
)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Authorized Users**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(desc),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class RoleSuccessView(LayoutView):
|
||||
def __init__(self, role_name, action):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{TICK_ALT} Success**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"**{role_name}** has been {action} the emergency list."),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class RoleErrorView(LayoutView):
|
||||
def __init__(self, error_type):
|
||||
super().__init__(timeout=None)
|
||||
content = {
|
||||
"owner_add": "Only the server owner can add role for emergency situation.",
|
||||
"owner_remove": "Only the server owner can remove roles from emergency list.",
|
||||
"owner_list": "You are not authorised to view list of roles.",
|
||||
"limit": "Only up to 25 roles can be added.",
|
||||
"exists": "This role is already in the emergency list.",
|
||||
"not_found": "This role is not in the emergency list.",
|
||||
}.get(error_type, "An error occurred.")
|
||||
is_warning = error_type == "limit"
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(
|
||||
f"**{ZWARNING} Error**"
|
||||
if is_warning
|
||||
else f"**{CROSS_ALT} Error**"
|
||||
),
|
||||
Separator(visible=True),
|
||||
TextDisplay(content),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class RoleListView(LayoutView):
|
||||
def __init__(self, roles, is_authorised):
|
||||
super().__init__(timeout=None)
|
||||
if not is_authorised:
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{ZWARNING} Access Denied**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("You are not authorised to view list of roles."),
|
||||
)
|
||||
)
|
||||
elif not roles:
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Emergency Roles**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("No roles added for emergency situation."),
|
||||
)
|
||||
)
|
||||
else:
|
||||
desc = "\n".join(
|
||||
[f"{i + 1}. <@&{r[0]}> - {r[0]}" for i, r in enumerate(roles)]
|
||||
)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Emergency Roles**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(desc),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EmergencySituationErrorView(LayoutView):
|
||||
def __init__(self, error_type):
|
||||
super().__init__(timeout=None)
|
||||
content = (
|
||||
"You are not authorised to execute the emergency situation."
|
||||
if error_type == "access"
|
||||
else "No roles have been added for the emergency situation."
|
||||
)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(
|
||||
f"**{ZWARNING} Access Denied**"
|
||||
if error_type == "access"
|
||||
else f"**{CROSS_ALT} Error**"
|
||||
),
|
||||
Separator(visible=True),
|
||||
TextDisplay(content),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EmergencySituationResultView(LayoutView):
|
||||
def __init__(
|
||||
self,
|
||||
success_msg,
|
||||
error_msg,
|
||||
moved_role=None,
|
||||
move_failed=False,
|
||||
move_error=None,
|
||||
):
|
||||
super().__init__(timeout=None)
|
||||
desc = f"**{TICK_ALT} Roles Modified**:\n{success_msg}\n\n"
|
||||
if moved_role:
|
||||
desc += f"**{ZWARNING} Role Moved**: {moved_role.mention} moved below bot's top role.\n\n"
|
||||
elif move_failed:
|
||||
desc += "**ℹ️ Role Couldn't Moved**: Permission error.\n\n"
|
||||
elif move_error:
|
||||
desc += f"**ℹ️ Role Couldn't Moved**: {move_error}\n\n"
|
||||
desc += f"**Errors**: {error_msg}"
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Emergency Situation**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(desc),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EmergencyRestoreAccessErrorView(LayoutView):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{ZWARNING} Access Denied**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
"Only the server owner can execute the emergency restore command."
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EmergencyRestoreNoRolesView(LayoutView):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"**{CROSS_ALT} Error**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
"No roles were found with disabled permissions for restore."
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EmergencyRestoreResultView(LayoutView):
|
||||
def __init__(self, success_msg, error_msg):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("**Emergency Restore**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
f"**{TICK_ALT} Permissions Restored**:\n{success_msg}\n\n**{ML_CROSS} Errors**:\n{error_msg}\n\nDatabase cleared."
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Emergency(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.db_path = "db/emergency.db"
|
||||
self.bot.loop.create_task(self.initialize_database())
|
||||
|
||||
async def initialize_database(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS authorised_users (guild_id INTEGER, user_id INTEGER)"
|
||||
)
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS emergency_roles (guild_id INTEGER, role_id INTEGER)"
|
||||
)
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS restore_roles (guild_id INTEGER NOT NULL, role_id INTEGER NOT NULL, disabled_perms TEXT NOT NULL, PRIMARY KEY (guild_id, role_id))"
|
||||
)
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS role_positions (guild_id INTEGER, role_id INTEGER, previous_position INTEGER)"
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
async def is_guild_owner(self, ctx):
|
||||
return ctx.guild and ctx.author.id == ctx.guild.owner_id
|
||||
|
||||
async def is_guild_owner_or_authorised(self, ctx):
|
||||
if await self.is_guild_owner(ctx):
|
||||
return True
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM authorised_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, ctx.author.id),
|
||||
) as cursor:
|
||||
return await cursor.fetchone() is not None
|
||||
|
||||
@commands.group(name="emergency", aliases=["emg"], invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.guild_only()
|
||||
async def emergency(self, ctx):
|
||||
await ctx.reply(view=EmergencyMainView(ctx, ctx.prefix))
|
||||
|
||||
@emergency.command(name="enable")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.guild_only()
|
||||
async def enable(self, ctx):
|
||||
if ctx.author.id != ctx.guild.owner_id and str(ctx.author.id) not in [
|
||||
"870179991462236170"
|
||||
]:
|
||||
return await ctx.reply(view=EnableErrorView())
|
||||
dangerous_perms = [
|
||||
"administrator",
|
||||
"ban_members",
|
||||
"kick_members",
|
||||
"manage_channels",
|
||||
"manage_roles",
|
||||
"manage_guild",
|
||||
]
|
||||
roles_added = []
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
for role in ctx.guild.roles:
|
||||
if (
|
||||
role.managed
|
||||
or role.is_bot_managed()
|
||||
or role.position >= ctx.guild.me.top_role.position
|
||||
):
|
||||
continue
|
||||
if any(getattr(role.permissions, p, False) for p in dangerous_perms):
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM emergency_roles WHERE guild_id = ? AND role_id = ?",
|
||||
(ctx.guild.id, role.id),
|
||||
) as cursor:
|
||||
if not await cursor.fetchone():
|
||||
await db.execute(
|
||||
"INSERT INTO emergency_roles (guild_id, role_id) VALUES (?, ?)",
|
||||
(ctx.guild.id, role.id),
|
||||
)
|
||||
roles_added.append(role)
|
||||
await db.commit()
|
||||
await ctx.reply(view=EnableSuccessView(roles_added))
|
||||
|
||||
@emergency.command(name="disable")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 4, commands.BucketType.user)
|
||||
@commands.guild_only()
|
||||
async def disable(self, ctx):
|
||||
if ctx.author.id != ctx.guild.owner_id and str(ctx.author.id) not in [
|
||||
"870179991462236170"
|
||||
]:
|
||||
return await ctx.reply(view=DisableErrorView())
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute(
|
||||
"DELETE FROM emergency_roles WHERE guild_id = ?", (ctx.guild.id,)
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(view=DisableSuccessView())
|
||||
|
||||
@emergency.group(name="authorise", aliases=["ath"], invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
async def authorise(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@authorise.command(name="add")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
async def authorise_add(self, ctx, member: discord.Member):
|
||||
if not await self.is_guild_owner(ctx):
|
||||
return await ctx.reply(view=AuthoriseErrorView("owner_add"))
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT COUNT(*) FROM authorised_users WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
) as cursor:
|
||||
if (await cursor.fetchone())[0] >= 5:
|
||||
return await ctx.reply(view=AuthoriseErrorView("limit"))
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM authorised_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, member.id),
|
||||
) as cursor:
|
||||
if await cursor.fetchone():
|
||||
return await ctx.reply(view=AuthoriseErrorView("exists"))
|
||||
await db.execute(
|
||||
"INSERT INTO authorised_users (guild_id, user_id) VALUES (?, ?)",
|
||||
(ctx.guild.id, member.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(view=AuthoriseSuccessView(member.display_name, "authorised"))
|
||||
|
||||
@authorise.command(name="remove")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
async def authorise_remove(self, ctx, member: discord.Member):
|
||||
if not await self.is_guild_owner(ctx):
|
||||
return await ctx.reply(view=AuthoriseErrorView("owner_remove"))
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM authorised_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, member.id),
|
||||
) as cursor:
|
||||
if not await cursor.fetchone():
|
||||
return await ctx.reply(view=AuthoriseErrorView("not_found"))
|
||||
await db.execute(
|
||||
"DELETE FROM authorised_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, member.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(view=AuthoriseSuccessView(member.display_name, "removed"))
|
||||
|
||||
@authorise.command(name="list", aliases=["view"])
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
async def list_authorized(self, ctx):
|
||||
is_owner = await self.is_guild_owner(ctx)
|
||||
if not is_owner:
|
||||
return await ctx.reply(view=AuthoriseErrorView("owner_list"))
|
||||
async with aiosqlite.connect("db/emergency.db") as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT user_id FROM authorised_users WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
users = await cursor.fetchall()
|
||||
await ctx.reply(view=AuthoriseListView(ctx, users, is_owner))
|
||||
|
||||
@emergency.group(name="role", invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
async def role(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@role.command(name="add")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
async def role_add(self, ctx, role: discord.Role):
|
||||
if not await self.is_guild_owner(ctx):
|
||||
return await ctx.reply(view=RoleErrorView("owner_add"))
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT COUNT(*) FROM emergency_roles WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
) as cursor:
|
||||
if (await cursor.fetchone())[0] >= 25:
|
||||
return await ctx.reply(view=RoleErrorView("limit"))
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM emergency_roles WHERE guild_id = ? AND role_id = ?",
|
||||
(ctx.guild.id, role.id),
|
||||
) as cursor:
|
||||
if await cursor.fetchone():
|
||||
return await ctx.reply(view=RoleErrorView("exists"))
|
||||
await db.execute(
|
||||
"INSERT INTO emergency_roles (guild_id, role_id) VALUES (?, ?)",
|
||||
(ctx.guild.id, role.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(view=RoleSuccessView(role.name, "added to"))
|
||||
|
||||
@role.command(name="remove")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
async def role_remove(self, ctx, role: discord.Role):
|
||||
if not await self.is_guild_owner(ctx):
|
||||
return await ctx.reply(view=RoleErrorView("owner_remove"))
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM emergency_roles WHERE guild_id = ? AND role_id = ?",
|
||||
(ctx.guild.id, role.id),
|
||||
) as cursor:
|
||||
if not await cursor.fetchone():
|
||||
return await ctx.reply(view=RoleErrorView("not_found"))
|
||||
await db.execute(
|
||||
"DELETE FROM emergency_roles WHERE guild_id = ? AND role_id = ?",
|
||||
(ctx.guild.id, role.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(view=RoleSuccessView(role.name, "removed from"))
|
||||
|
||||
@role.command(name="list", aliases=["view"])
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.guild_only()
|
||||
async def list_roles(self, ctx):
|
||||
is_auth = await self.is_guild_owner_or_authorised(ctx)
|
||||
if not is_auth:
|
||||
return await ctx.reply(view=RoleErrorView("owner_list"))
|
||||
async with aiosqlite.connect("db/emergency.db") as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT role_id FROM emergency_roles WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
roles = await cursor.fetchall()
|
||||
await ctx.reply(view=RoleListView(roles, is_auth))
|
||||
|
||||
@commands.command(name="emergencysituation", aliases=["emgs"])
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 40, commands.BucketType.user)
|
||||
@commands.guild_only()
|
||||
@commands.bot_has_permissions(manage_roles=True)
|
||||
async def emergencysituation(self, ctx):
|
||||
if not await self.is_guild_owner_or_authorised(ctx) and str(
|
||||
ctx.author.id
|
||||
) not in ["870179991462236170"]:
|
||||
return await ctx.reply(view=EmergencySituationErrorView("access"))
|
||||
|
||||
proc_msg = await ctx.reply(
|
||||
view=LayoutView(build_container(TextDisplay("Processing Emergency Situation...")))
|
||||
)
|
||||
guild_id = ctx.guild.id
|
||||
|
||||
antinuke_enabled = False
|
||||
async with aiosqlite.connect("db/anti.db") as anti:
|
||||
cursor = await anti.execute(
|
||||
"SELECT status FROM antinuke WHERE guild_id = ?", (guild_id,)
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
if row:
|
||||
antinuke_enabled = True
|
||||
await anti.execute(
|
||||
"DELETE FROM antinuke WHERE guild_id = ?", (guild_id,)
|
||||
)
|
||||
await anti.commit()
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute(
|
||||
"DELETE FROM restore_roles WHERE guild_id = ?", (ctx.guild.id,)
|
||||
)
|
||||
await db.commit()
|
||||
cursor = await db.execute(
|
||||
"SELECT role_id FROM emergency_roles WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
emergency_roles = await cursor.fetchall()
|
||||
|
||||
if not emergency_roles:
|
||||
await proc_msg.delete()
|
||||
return await ctx.reply(view=EmergencySituationErrorView("no_roles"))
|
||||
|
||||
bot_top = ctx.guild.me.top_role
|
||||
dangerous_perms = [
|
||||
"administrator",
|
||||
"ban_members",
|
||||
"kick_members",
|
||||
"manage_channels",
|
||||
"manage_roles",
|
||||
"manage_guild",
|
||||
]
|
||||
modified, unchanged = [], []
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
for (role_id,) in emergency_roles:
|
||||
role = ctx.guild.get_role(role_id)
|
||||
if not role or role.position >= bot_top.position or role.managed:
|
||||
unchanged.append(role) if role else None
|
||||
continue
|
||||
perms = role.permissions
|
||||
disabled = []
|
||||
for p in dangerous_perms:
|
||||
if getattr(perms, p, False):
|
||||
setattr(perms, p, False)
|
||||
disabled.append(p)
|
||||
if disabled:
|
||||
try:
|
||||
await role.edit(permissions=perms, reason="Emergency Situation")
|
||||
modified.append(role)
|
||||
await db.execute(
|
||||
"INSERT INTO restore_roles VALUES (?, ?, ?)",
|
||||
(ctx.guild.id, role.id, ",".join(disabled)),
|
||||
)
|
||||
await db.commit()
|
||||
except:
|
||||
unchanged.append(role)
|
||||
|
||||
success = "\n".join([r.mention for r in modified]) or "No roles modified."
|
||||
errors = "\n".join([r.mention for r in unchanged]) or "No errors."
|
||||
|
||||
most_mem = max(
|
||||
[
|
||||
r
|
||||
for r in ctx.guild.roles
|
||||
if not r.managed
|
||||
and r.position < bot_top.position
|
||||
and r != ctx.guild.default_role
|
||||
],
|
||||
key=lambda r: len(r.members),
|
||||
default=None,
|
||||
)
|
||||
|
||||
if most_mem:
|
||||
try:
|
||||
await most_mem.edit(
|
||||
position=bot_top.position - 1, reason="Emergency Situation"
|
||||
)
|
||||
result_view = EmergencySituationResultView(
|
||||
success, errors, moved_role=most_mem
|
||||
)
|
||||
except discord.Forbidden:
|
||||
result_view = EmergencySituationResultView(
|
||||
success, errors, move_failed=True
|
||||
)
|
||||
except Exception as e:
|
||||
result_view = EmergencySituationResultView(
|
||||
success, errors, move_error=str(e)
|
||||
)
|
||||
else:
|
||||
result_view = EmergencySituationResultView(success, errors)
|
||||
|
||||
await ctx.reply(view=result_view)
|
||||
|
||||
if antinuke_enabled:
|
||||
async with aiosqlite.connect("db/anti.db") as anti:
|
||||
await anti.execute("INSERT INTO antinuke VALUES (?, 1)", (guild_id,))
|
||||
await anti.commit()
|
||||
await proc_msg.delete()
|
||||
|
||||
@commands.command(name="emergencyrestore", aliases=["emgrestore"])
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 30, commands.BucketType.user)
|
||||
@commands.guild_only()
|
||||
@commands.bot_has_permissions(manage_roles=True)
|
||||
async def emergencyrestore(self, ctx):
|
||||
if ctx.author.id != ctx.guild.owner_id and str(ctx.author.id) not in [
|
||||
"870179991462236170"
|
||||
]:
|
||||
return await ctx.reply(view=EmergencyRestoreAccessErrorView())
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT role_id, disabled_perms FROM restore_roles WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
restore_roles = await cursor.fetchall()
|
||||
|
||||
if not restore_roles:
|
||||
return await ctx.reply(view=EmergencyRestoreNoRolesView())
|
||||
|
||||
view = EmergencyRestoreConfirmView(ctx)
|
||||
await ctx.send(view=view)
|
||||
await view.wait()
|
||||
|
||||
if view.value is None:
|
||||
return await ctx.reply(
|
||||
view=LayoutView(
|
||||
build_container(TextDisplay("**Restore Cancelled** - Timed out."))
|
||||
)
|
||||
)
|
||||
if view.value is False:
|
||||
return await ctx.reply(
|
||||
view=LayoutView(build_container(TextDisplay("**Restore Cancelled**")))
|
||||
)
|
||||
|
||||
modified, unchanged = [], []
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
for role_id, perms in restore_roles:
|
||||
role = ctx.guild.get_role(role_id)
|
||||
if not role:
|
||||
continue
|
||||
rp = role.permissions
|
||||
restored = False
|
||||
for p in perms.split(","):
|
||||
if hasattr(rp, p):
|
||||
setattr(rp, p, True)
|
||||
restored = True
|
||||
if restored:
|
||||
try:
|
||||
await role.edit(permissions=rp, reason="Emergency Restore")
|
||||
modified.append(role)
|
||||
except:
|
||||
unchanged.append(role)
|
||||
await db.execute(
|
||||
"DELETE FROM restore_roles WHERE guild_id = ?", (ctx.guild.id,)
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
success = "\n".join([r.mention for r in modified]) or "No roles restored."
|
||||
errors = "\n".join([r.mention for r in unchanged]) or "No errors."
|
||||
await ctx.reply(view=EmergencyRestoreResultView(success, errors))
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Emergency(bot))
|
||||
210
bot/cogs/commands/encryption.py
Normal file
210
bot/cogs/commands/encryption.py
Normal file
@@ -0,0 +1,210 @@
|
||||
import base64
|
||||
import binascii
|
||||
import codecs
|
||||
import secrets
|
||||
|
||||
import discord
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from discord.ext import commands
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
class EncryptResultView(LayoutView):
|
||||
def __init__(self, convert, txtinput):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
try:
|
||||
display_text = txtinput.decode("UTF-8")
|
||||
except AttributeError:
|
||||
display_text = str(txtinput)
|
||||
|
||||
if len(display_text) > 500:
|
||||
display_text = display_text[:500] + "..."
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"📑 **{convert}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(display_text),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class DecodeErrorView(LayoutView):
|
||||
def __init__(self, codec_name):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay(f"❌ **Invalid {codec_name}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"The provided string is not valid {codec_name} encoding."),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class PasswordSentView(LayoutView):
|
||||
def __init__(self, author_name):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("🔐 **Password Generated**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
f"Sending you a DM with your random generated password **{author_name}**"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class PasswordDMView(LayoutView):
|
||||
def __init__(self, password):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("🎁 **Here is your password:**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(password),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class encryption(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.group(invoke_without_command=True)
|
||||
async def encode(self, ctx):
|
||||
"""All encode methods"""
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
@commands.group(invoke_without_command=True)
|
||||
async def decode(self, ctx):
|
||||
"""All decode methods"""
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
async def encryptout(self, ctx, convert, txtinput):
|
||||
view = EncryptResultView(convert, txtinput)
|
||||
await ctx.send(view=view)
|
||||
|
||||
@encode.command(name="base32", aliases=["b32"])
|
||||
async def encode_base32(self, ctx, *, txtinput: commands.clean_content):
|
||||
"""Encode in base32"""
|
||||
await self.encryptout(
|
||||
ctx, "Text -> base32", base64.b32encode(txtinput.encode("UTF-8"))
|
||||
)
|
||||
|
||||
@decode.command(name="base32", aliases=["b32"])
|
||||
async def decode_base32(self, ctx, *, txtinput: str):
|
||||
"""Decode in base32"""
|
||||
try:
|
||||
await self.encryptout(
|
||||
ctx, "base32 -> Text", base64.b32decode(txtinput.encode("UTF-8"))
|
||||
)
|
||||
except Exception:
|
||||
await ctx.send(view=DecodeErrorView("base32"))
|
||||
|
||||
@encode.command(name="base64", aliases=["b64"])
|
||||
async def encode_base64(self, ctx, *, txtinput: commands.clean_content):
|
||||
"""Encode in base64"""
|
||||
await self.encryptout(
|
||||
ctx, "Text -> base64", base64.urlsafe_b64encode(txtinput.encode("UTF-8"))
|
||||
)
|
||||
|
||||
@decode.command(name="base64", aliases=["b64"])
|
||||
async def decode_base64(self, ctx, *, txtinput: str):
|
||||
"""Decode in base64"""
|
||||
try:
|
||||
await self.encryptout(
|
||||
ctx,
|
||||
"base64 -> Text",
|
||||
base64.urlsafe_b64decode(txtinput.encode("UTF-8")),
|
||||
)
|
||||
except Exception:
|
||||
await ctx.send(view=DecodeErrorView("base64"))
|
||||
|
||||
@encode.command(name="rot13", aliases=["r13"])
|
||||
async def encode_rot13(self, ctx, *, txtinput: commands.clean_content):
|
||||
"""Encode in rot13"""
|
||||
await self.encryptout(ctx, "Text -> rot13", codecs.decode(txtinput, "rot_13"))
|
||||
|
||||
@decode.command(name="rot13", aliases=["r13"])
|
||||
async def decode_rot13(self, ctx, *, txtinput: str):
|
||||
"""Decode in rot13"""
|
||||
try:
|
||||
await self.encryptout(
|
||||
ctx, "rot13 -> Text", codecs.decode(txtinput, "rot_13")
|
||||
)
|
||||
except Exception:
|
||||
await ctx.send(view=DecodeErrorView("rot13"))
|
||||
|
||||
@encode.command(name="hex")
|
||||
async def encode_hex(self, ctx, *, txtinput: commands.clean_content):
|
||||
"""Encode in hex"""
|
||||
await self.encryptout(
|
||||
ctx, "Text -> hex", binascii.hexlify(txtinput.encode("UTF-8"))
|
||||
)
|
||||
|
||||
@decode.command(name="hex")
|
||||
async def decode_hex(self, ctx, *, txtinput: str):
|
||||
"""Decode in hex"""
|
||||
try:
|
||||
await self.encryptout(
|
||||
ctx, "hex -> Text", binascii.unhexlify(txtinput.encode("UTF-8"))
|
||||
)
|
||||
except Exception:
|
||||
await ctx.send(view=DecodeErrorView("hex"))
|
||||
|
||||
@encode.command(name="base85", aliases=["b85"])
|
||||
async def encode_base85(self, ctx, *, txtinput: commands.clean_content):
|
||||
"""Encode in base85"""
|
||||
await self.encryptout(
|
||||
ctx, "Text -> base85", base64.b85encode(txtinput.encode("UTF-8"))
|
||||
)
|
||||
|
||||
@decode.command(name="base85", aliases=["b85"])
|
||||
async def decode_base85(self, ctx, *, txtinput: str):
|
||||
"""Decode in base85"""
|
||||
try:
|
||||
await self.encryptout(
|
||||
ctx, "base85 -> Text", base64.b85decode(txtinput.encode("UTF-8"))
|
||||
)
|
||||
except Exception:
|
||||
await ctx.send(view=DecodeErrorView("base85"))
|
||||
|
||||
@encode.command(name="ascii85", aliases=["a85"])
|
||||
async def encode_ascii85(self, ctx, *, txtinput: commands.clean_content):
|
||||
"""Encode in ASCII85"""
|
||||
await self.encryptout(
|
||||
ctx, "Text -> ASCII85", base64.a85encode(txtinput.encode("UTF-8"))
|
||||
)
|
||||
|
||||
@decode.command(name="ascii85", aliases=["a85"])
|
||||
async def decode_ascii85(self, ctx, *, txtinput: str):
|
||||
"""Decode in ASCII85"""
|
||||
try:
|
||||
await self.encryptout(
|
||||
ctx, "ASCII85 -> Text", base64.a85decode(txtinput.encode("UTF-8"))
|
||||
)
|
||||
except Exception:
|
||||
await ctx.send(view=DecodeErrorView("ASCII85"))
|
||||
|
||||
@commands.command(name="password")
|
||||
async def password(self, ctx):
|
||||
"""Generates a random secure password for you"""
|
||||
if hasattr(ctx, "guild") and ctx.guild is not None:
|
||||
await ctx.send(view=PasswordSentView(ctx.author.name))
|
||||
|
||||
password = secrets.token_urlsafe(18)
|
||||
try:
|
||||
await ctx.author.send(view=PasswordDMView(password))
|
||||
except discord.Forbidden:
|
||||
await ctx.send(
|
||||
f"❌ Could not send DM. Here is your password: **{password}**"
|
||||
)
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(encryption(bot))
|
||||
972
bot/cogs/commands/extra.py
Normal file
972
bot/cogs/commands/extra.py
Normal file
@@ -0,0 +1,972 @@
|
||||
import os
|
||||
import discord
|
||||
from utils.emoji import BOOST, CODEBASE, CROSS, KING, TICK, UPTIME, ZWARNING
|
||||
from discord.ext import commands
|
||||
import datetime
|
||||
import sys
|
||||
from discord.ui import Button, View, LayoutView, TextDisplay, Separator, Container, ActionRow, MediaGallery
|
||||
from utils.cv2 import CV2, build_container
|
||||
import psutil
|
||||
import time
|
||||
from utils.Tools import *
|
||||
from discord.ext import commands, menus
|
||||
from discord.ext.commands import BucketType, cooldown
|
||||
import requests
|
||||
from typing import *
|
||||
from utils import *
|
||||
from utils.config import BotName, serverLink
|
||||
from utils import Paginator, DescriptionEmbedPaginator, FieldPagePaginator, TextPaginator
|
||||
from core import Cog, zyrox, Context
|
||||
from typing import Optional
|
||||
import aiosqlite
|
||||
import asyncio
|
||||
import aiohttp
|
||||
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
def datetime_to_seconds(thing: datetime.datetime):
|
||||
current_time = datetime.datetime.fromtimestamp(time.time())
|
||||
return round(
|
||||
round(time.time()) +
|
||||
(current_time - thing.replace(tzinfo=None)).total_seconds())
|
||||
|
||||
tick = f"{TICK}>"
|
||||
cross = CROSS
|
||||
|
||||
|
||||
|
||||
|
||||
class RoleInfoView(View):
|
||||
def __init__(self, role: discord.Role, author_id):
|
||||
super().__init__(timeout=180)
|
||||
self.role = role
|
||||
self.author_id = author_id
|
||||
|
||||
@discord.ui.button(label='Show Permissions', emoji=f"{CODEBASE} ", style=discord.ButtonStyle.secondary)
|
||||
async def show_permissions(self, interaction: discord.Interaction, button: Button):
|
||||
if interaction.user.id != self.author_id:
|
||||
await interaction.response.send_message("Uh oh! That message doesn't belong to you. You must run this command to interact with it.", ephemeral=True)
|
||||
return
|
||||
|
||||
permissions = [perm.replace("_", " ").title() for perm, value in self.role.permissions if value]
|
||||
permission_text = ", ".join(permissions) if permissions else "None"
|
||||
await interaction.response.send_message(view=CV2(f"Permissions for {self.role.name}", permission_text or "No permissions."), ephemeral=True)
|
||||
|
||||
|
||||
class OverwritesView(View):
|
||||
def __init__(self, channel, author_id):
|
||||
super().__init__(timeout=180)
|
||||
self.channel = channel
|
||||
self.author_id = author_id
|
||||
|
||||
@discord.ui.button(label='Show Overwrites', style=discord.ButtonStyle.primary)
|
||||
async def show_overwrites(self, interaction: discord.Interaction, button: Button):
|
||||
if interaction.user.id != self.author_id:
|
||||
await interaction.response.send_message("Uh oh! That message doesn't belong to you. You must run this command to interact with it.", ephemeral=True)
|
||||
return
|
||||
|
||||
overwrites = []
|
||||
for target, perms in self.channel.overwrites.items():
|
||||
permissions = {
|
||||
"View Channel": perms.view_channel,
|
||||
"Send Messages": perms.send_messages,
|
||||
"Read Message History": perms.read_message_history,
|
||||
"Manage Messages": perms.manage_messages,
|
||||
"Embed Links": perms.embed_links,
|
||||
"Attach Files": perms.attach_files,
|
||||
"Manage Channels": perms.manage_channels,
|
||||
"Manage Permissions": perms.manage_permissions,
|
||||
"Manage Webhooks": perms.manage_webhooks,
|
||||
"Create Instant Invite": perms.create_instant_invite,
|
||||
"Add Reactions": perms.add_reactions,
|
||||
"Mention Everyone": perms.mention_everyone,
|
||||
"Kick Members": perms.kick_members,
|
||||
"Ban Members": perms.ban_members,
|
||||
"Moderate Members": perms.moderate_members,
|
||||
"Send TTS Messages": perms.send_tts_messages,
|
||||
"Use External Emojis": perms.external_emojis,
|
||||
"Use External Stickers": perms.external_stickers,
|
||||
"View Audit Log": perms.view_audit_log,
|
||||
"Voice Mute Members": perms.mute_members,
|
||||
"Voice Deafen Members": perms.deafen_members,
|
||||
"Administrator": perms.administrator
|
||||
}
|
||||
|
||||
overwrites.append(f"**For {target.name}**\n" +
|
||||
"\n".join(f" * **{perm}:** {'{TICK}>' if value else '{CROSS}' if value is False else '⛔'}" for perm, value in permissions.items()))
|
||||
|
||||
ow_text = "\n".join(overwrites) if overwrites else "No overwrites for this channel."
|
||||
footer = f"{TICK}> = Allowed, {CROSS} = Denied, ⛔ = None"
|
||||
await interaction.response.send_message(view=CV2(f"Overwrites for {self.channel.name}", ow_text, footer), ephemeral=True)
|
||||
|
||||
|
||||
|
||||
|
||||
class Extra(commands.Cog):
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.color = 0xFF0000
|
||||
self.start_time = datetime.datetime.now()
|
||||
|
||||
@commands.hybrid_group(name="banner")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def banner(self, ctx):
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
@banner.command(name="server")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def server(self, ctx):
|
||||
if not ctx.guild.banner:
|
||||
await ctx.reply(view=CV2(f"{cross} Error", "This server doesn't have a banner."))
|
||||
else:
|
||||
webp = ctx.guild.banner.replace(format='webp')
|
||||
jpg = ctx.guild.banner.replace(format='jpg')
|
||||
png = ctx.guild.banner.replace(format='png')
|
||||
links = f"[`PNG`]({png}) | [`JPG`]({jpg}) | [`WEBP`]({webp})"
|
||||
if ctx.guild.banner.is_animated():
|
||||
links += f" | [`GIF`]({ctx.guild.banner.replace(format='gif')})"
|
||||
view = LayoutView(timeout=None)
|
||||
gallery = MediaGallery()
|
||||
gallery.add_item(media=str(ctx.guild.banner.url))
|
||||
view.add_item(build_container(
|
||||
TextDisplay(f"**{ctx.guild.name}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(links),
|
||||
gallery
|
||||
))
|
||||
await ctx.reply(view=view)
|
||||
|
||||
|
||||
@banner.command(name="user")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _user(self,
|
||||
ctx,
|
||||
member: Optional[Union[discord.Member,
|
||||
discord.User]] = None):
|
||||
if member == None or member == "":
|
||||
member = ctx.author
|
||||
bannerUser = await self.bot.fetch_user(member.id)
|
||||
if not bannerUser.banner:
|
||||
await ctx.reply(view=CV2(f"{cross} Error", f"{member} doesn't have a banner."))
|
||||
else:
|
||||
webp = bannerUser.banner.replace(format='webp')
|
||||
jpg = bannerUser.banner.replace(format='jpg')
|
||||
png = bannerUser.banner.replace(format='png')
|
||||
links = f"[`PNG`]({png}) | [`JPG`]({jpg}) | [`WEBP`]({webp})"
|
||||
if bannerUser.banner.is_animated():
|
||||
links += f" | [`GIF`]({bannerUser.banner.replace(format='gif')})"
|
||||
view = LayoutView(timeout=None)
|
||||
gallery = MediaGallery()
|
||||
gallery.add_item(media=str(bannerUser.banner.url))
|
||||
view.add_item(build_container(
|
||||
TextDisplay(f"**{member}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(links),
|
||||
gallery
|
||||
))
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.command(name="uptime", description="Shows the Bot's Uptime.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def uptime(self, ctx):
|
||||
pfp = ctx.author.display_avatar.url
|
||||
|
||||
uptime_seconds = int(round(time.time() - start_time))
|
||||
uptime_timedelta = datetime.timedelta(seconds=uptime_seconds)
|
||||
|
||||
uptime_string = f"Up since {datetime.datetime.utcfromtimestamp(start_time).strftime('%Y-%m-%d %H:%M:%S')} UTC"
|
||||
uptime_duration_string = f"{uptime_timedelta.days} days, {uptime_timedelta.seconds // 3600} hours, {(uptime_timedelta.seconds // 60) % 60} minutes, {uptime_timedelta.seconds % 60} seconds"
|
||||
|
||||
uptime_text = (
|
||||
f"**__UTC__**\n{ZWARNING} {uptime_string}\n\n"
|
||||
f"**__Online Duration__**\n{UPTIME} {uptime_duration_string}"
|
||||
)
|
||||
await ctx.send(view=CV2(f"{BRAND_NAME} Manager Uptime", uptime_text))
|
||||
|
||||
|
||||
|
||||
@commands.hybrid_command(name="serverinfo",
|
||||
aliases=["sinfo", "si"],
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def serverinfo(self, ctx):
|
||||
c_at = ctx.guild.created_at.strftime("%Y-%m-%d %H:%M:%S")
|
||||
about = (
|
||||
f"**Name:** {ctx.guild.name}\n"
|
||||
f"**ID:** {ctx.guild.id}\n"
|
||||
f"**Owner {KING}:** {ctx.guild.owner} (<@{ctx.guild.owner_id}>)\n"
|
||||
f"**Created At:** {c_at}\n"
|
||||
f"**Members:** {len(ctx.guild.members)}"
|
||||
)
|
||||
desc_section = f"\n\n**__Description__**\n{ctx.guild.description}" if ctx.guild.description else ""
|
||||
stats = (
|
||||
f"**Verification Level:** {ctx.guild.verification_level}\n"
|
||||
f"**Channels:** {len(ctx.guild.channels)}\n"
|
||||
f"**Roles:** {len(ctx.guild.roles)}\n"
|
||||
f"**Emojis:** {len(ctx.guild.emojis)}\n"
|
||||
f"**Boost Status:** Level {ctx.guild.premium_tier} (Boosts: {ctx.guild.premium_subscription_count})"
|
||||
)
|
||||
features_text = ""
|
||||
if ctx.guild.features:
|
||||
features = "\n".join([f"{TICK}>: {f[:1].upper() + f[1:].lower().replace('_', ' ')}" for f in ctx.guild.features])
|
||||
features_text = f"\n\n**__Features__**\n{features[:1000] if len(features) > 1024 else features}"
|
||||
regular_emojis = [e for e in ctx.guild.emojis if not e.animated]
|
||||
animated_emojis = [e for e in ctx.guild.emojis if e.animated]
|
||||
emoji_info = f"Regular: {len(regular_emojis)}/100 | Animated: {len(animated_emojis)}/100 | Total: {len(ctx.guild.emojis)}/200"
|
||||
roles = ctx.guild.roles
|
||||
roles_display = "\n".join([r.mention for r in roles[:10]])
|
||||
if len(roles) > 10:
|
||||
roles_display += f"\n...and {len(roles) - 10} more"
|
||||
full_text = (
|
||||
f"**__About__**\n{about}{desc_section}\n\n"
|
||||
f"**__General Stats__**\n{stats}{features_text}\n\n"
|
||||
f"**__Channels__**\n**Total:** {len(ctx.guild.channels)} — {len(ctx.guild.text_channels)} text, {len(ctx.guild.voice_channels)} voice\n\n"
|
||||
f"**__Emoji Info__**\n{emoji_info}\n\n"
|
||||
f"**__Boost Status__**\nLevel: {ctx.guild.premium_tier} [{BOOST}{ctx.guild.premium_subscription_count} boosts]\n\n"
|
||||
f"**__Server Roles__ [{len(roles)}]**\n{roles_display}"
|
||||
)
|
||||
icon_url = ctx.guild.icon.url if ctx.guild.icon else None
|
||||
view = LayoutView(timeout=None)
|
||||
items = [TextDisplay(f"# {ctx.guild.name}'s Information"), Separator(visible=True), TextDisplay(full_text)]
|
||||
if icon_url or ctx.guild.banner:
|
||||
gallery = MediaGallery()
|
||||
if icon_url:
|
||||
gallery.add_item(media=str(icon_url))
|
||||
if ctx.guild.banner:
|
||||
gallery.add_item(media=str(ctx.guild.banner.url))
|
||||
items.append(gallery)
|
||||
view.add_item(build_container(*items))
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
|
||||
@commands.hybrid_command(name="userinfo",
|
||||
aliases=["whois", "ui"],
|
||||
usage="Userinfo [user]",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def _userinfo(self,
|
||||
ctx,
|
||||
member: Optional[Union[discord.Member,
|
||||
discord.User]] = None):
|
||||
if member == None or member == "":
|
||||
member = ctx.author
|
||||
elif member not in ctx.guild.members:
|
||||
member = await self.bot.fetch_user(member.id)
|
||||
|
||||
badges = ""
|
||||
if member.public_flags.hypesquad:
|
||||
badges += "HypeSquad Events, "
|
||||
if member.public_flags.hypesquad_balance:
|
||||
badges += "HypeSquad Balance, "
|
||||
if member.public_flags.hypesquad_bravery:
|
||||
badges += "HypeSquad Bravery, "
|
||||
if member.public_flags.hypesquad_brilliance:
|
||||
badges += "HypeSquad Brilliance, "
|
||||
if member.public_flags.early_supporter:
|
||||
badges += "Early Supporter, "
|
||||
if member.public_flags.active_developer:
|
||||
badges += "Active Developer, "
|
||||
if member.public_flags.verified_bot_developer:
|
||||
badges += "Early Verified Bot Developer, "
|
||||
if member.public_flags.discord_certified_moderator:
|
||||
badges += "Moderators Program Alumni, "
|
||||
if member.public_flags.staff:
|
||||
badges += "Discord Staff, "
|
||||
if member.public_flags.partner:
|
||||
badges += "Partnered Server Owner "
|
||||
if badges == None or badges == "":
|
||||
badges += f"{cross}"
|
||||
|
||||
if member in ctx.guild.members:
|
||||
nickk = f"{member.nick if member.nick else 'None'}"
|
||||
joinedat = f"<t:{round(member.joined_at.timestamp())}:R>"
|
||||
else:
|
||||
nickk = "None"
|
||||
joinedat = "None"
|
||||
|
||||
kp = ""
|
||||
if member in ctx.guild.members:
|
||||
if member.guild_permissions.kick_members:
|
||||
kp += "Kick Members"
|
||||
if member.guild_permissions.ban_members:
|
||||
kp += " , Ban Members"
|
||||
if member.guild_permissions.administrator:
|
||||
kp += " , Administrator"
|
||||
if member.guild_permissions.manage_channels:
|
||||
kp += " , Manage Channels"
|
||||
|
||||
if member.guild_permissions.manage_guild:
|
||||
kp += " , Manage Server"
|
||||
|
||||
if member.guild_permissions.manage_messages:
|
||||
kp += " , Manage Messages"
|
||||
if member.guild_permissions.mention_everyone:
|
||||
kp += " , Mention Everyone"
|
||||
if member.guild_permissions.manage_nicknames:
|
||||
kp += " , Manage Nicknames"
|
||||
if member.guild_permissions.manage_roles:
|
||||
kp += " , Manage Roles"
|
||||
if member.guild_permissions.manage_webhooks:
|
||||
kp += " , Manage Webhooks"
|
||||
if member.guild_permissions.manage_emojis:
|
||||
kp += " , Manage Emojis"
|
||||
|
||||
if kp is None or kp == "":
|
||||
kp = "None"
|
||||
|
||||
if member in ctx.guild.members:
|
||||
if member == ctx.guild.owner:
|
||||
aklm = "Server Owner"
|
||||
elif member.guild_permissions.administrator:
|
||||
aklm = "Server Admin"
|
||||
elif member.guild_permissions.ban_members or member.guild_permissions.kick_members:
|
||||
aklm = "Server Moderator"
|
||||
else:
|
||||
aklm = "Server Member"
|
||||
|
||||
bannerUser = await self.bot.fetch_user(member.id)
|
||||
embed = discord.Embed(color=self.color)
|
||||
embed.timestamp = discord.utils.utcnow()
|
||||
if not bannerUser.banner:
|
||||
pass
|
||||
else:
|
||||
embed.set_image(url=bannerUser.banner)
|
||||
embed.set_author(name=f"{member.name}'s Information",
|
||||
icon_url=member.avatar.url
|
||||
if member.avatar else member.default_avatar.url)
|
||||
embed.set_thumbnail(
|
||||
url=member.avatar.url if member.avatar else member.default_avatar.url)
|
||||
embed.add_field(name="__General Information__",
|
||||
value=f"""
|
||||
**Name:** {member}
|
||||
**ID:** {member.id}
|
||||
**Nickname:** {nickk}
|
||||
**Bot?:** {'{TICK}> Yes' if member.bot else '{CROSS} No'}
|
||||
**Badges:** {badges}
|
||||
**Account Created:** <t:{round(member.created_at.timestamp())}:R>
|
||||
**Server Joined:** {joinedat}
|
||||
""",
|
||||
inline=False)
|
||||
if member in ctx.guild.members:
|
||||
r = (', '.join(role.mention for role in member.roles[1:][::-1])
|
||||
if len(member.roles) > 1 else 'None.')
|
||||
embed.add_field(name="__Role Info__",
|
||||
value=f"""
|
||||
**Highest Role:** {member.top_role.mention if len(member.roles) > 1 else 'None'}
|
||||
**Roles [{f'{len(member.roles) - 1}' if member.roles else '0'}]:** {r if len(r) <= 1024 else r[0:1006] + ' and more...'}
|
||||
**Color:** {member.color if member.color else '99aab5'}
|
||||
""",
|
||||
inline=False)
|
||||
if member in ctx.guild.members:
|
||||
embed.add_field(
|
||||
name="__Extra__",
|
||||
value=
|
||||
f"**Boosting:** {f'<t:{round(member.premium_since.timestamp())}:R>' if member in ctx.guild.premium_subscribers else 'None'}\n**Voice :** {'None' if not member.voice else member.voice.channel.mention}",
|
||||
inline=False)
|
||||
if member in ctx.guild.members:
|
||||
embed.add_field(name="__Key Permissions__",
|
||||
value=", ".join([kp]),
|
||||
inline=False)
|
||||
if member in ctx.guild.members:
|
||||
embed.add_field(name="__Acknowledgement__",
|
||||
value=f"{aklm}",
|
||||
inline=False)
|
||||
if member in ctx.guild.members:
|
||||
embed.set_footer(text=f"Requested by {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url
|
||||
if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
else:
|
||||
if member not in ctx.guild.members:
|
||||
embed.set_footer(text=f"{member.name} not in this server.",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar
|
||||
else ctx.author.default_avatar.url)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
|
||||
@commands.hybrid_command(name='roleinfo', aliases=["ri"], help="Displays information about a specified role.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def roleinfo(self, ctx, role: discord.Role):
|
||||
members = role.members
|
||||
created_at = role.created_at.strftime("%Y-%m-%d %H:%M:%S")
|
||||
total_roles = len(ctx.guild.roles)
|
||||
role_position = total_roles - role.position
|
||||
role_text = (
|
||||
f"**__General Information__**\n"
|
||||
f"**ID:** {role.id}\n**Name:** {role.name}\n**Mention:** <@&{role.id}>\n"
|
||||
f"**Color:** {str(role.color)}\n**Total Members:** {len(role.members)}\n\n"
|
||||
f"**Position:** {role_position}\n**Mentionable:** {role.mentionable}\n"
|
||||
f"**Hoisted:** {role.hoist}\n**Managed:** {role.managed}\n**Created At:** {created_at}"
|
||||
)
|
||||
view = RoleInfoView(role, ctx.author.id)
|
||||
await ctx.send(content=role_text, view=view)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.command(name="boostcount",
|
||||
help="Shows boosts count",
|
||||
usage="boosts",
|
||||
aliases=["bco"],
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def boosts(self, ctx):
|
||||
await ctx.send(view=CV2(f"{BOOST} Boosts Count Of {ctx.guild.name}", f"**Total `{ctx.guild.premium_subscription_count}` boosts**"))
|
||||
|
||||
@commands.hybrid_group(name="list",
|
||||
invoke_without_command=True,
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def __list_(self, ctx: commands.Context):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@__list_.command(name="boosters",
|
||||
aliases=["boost", "booster"],
|
||||
usage="List boosters",
|
||||
help="List of boosters in the Guild",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_boost(self, ctx):
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` [{mem}](https://discord.com/users/{mem.id}) [{mem.mention}] - <t:{round(mem.premium_since.timestamp())}:R>"
|
||||
for no, mem in enumerate(guild.premium_subscribers, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=
|
||||
f"List of Boosters in {guild.name} - {len(guild.premium_subscribers)}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="bans", help= "List of all banned members in Guild", aliases=["ban"], with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(view_audit_log=True)
|
||||
@commands.bot_has_permissions(view_audit_log=True)
|
||||
async def list_ban(self, ctx):
|
||||
bans = [member async for member in ctx.guild.bans()]
|
||||
if len(bans) == 0:
|
||||
return await ctx.reply("There aren't any banned users in this guild.", mention_author=False)
|
||||
else:
|
||||
mems = ([
|
||||
member async for member in ctx.guild.bans()
|
||||
])
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` {mem}"
|
||||
for no, mem in enumerate(mems, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Banned Users in {guild.name} - {len(bans)}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(
|
||||
name="inrole",
|
||||
aliases=["inside-role"],
|
||||
help="List of members that are in the specified role",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_inrole(self, ctx, role: discord.Role):
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` [{mem}](https://discord.com/users/{mem.id}) [{mem.mention}] - <t:{int(mem.created_at.timestamp())}:D>"
|
||||
for no, mem in enumerate(role.members, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"List of Members in {role} - {len(role.members)}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="emojis",
|
||||
aliases=["emoji"],
|
||||
help="List of emojis in the Guild with ids",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_emojis(self, ctx):
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` {e} - `{e}`"
|
||||
for no, e in enumerate(ctx.guild.emojis, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"List of Emojis in {guild.name} - {len(ctx.guild.emojis)}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="roles",
|
||||
aliases=["role"],
|
||||
help="List of all roles in the server with ids",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def list_roles(self, ctx):
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` {e.mention} - `[{e.id}]`"
|
||||
for no, e in enumerate(ctx.guild.roles, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"List of Roles in {guild.name} - {len(ctx.guild.roles)}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="bots",
|
||||
aliases=["bot"],
|
||||
help="List of All Bots in a server",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_bots(self, ctx):
|
||||
guild = ctx.guild
|
||||
people = filter(lambda member: member.bot, ctx.guild.members)
|
||||
people = sorted(people, key=lambda member: member.joined_at)
|
||||
entries = [
|
||||
f"`#{no}.` [{mem}](https://discord.com/users/{mem.id}) [{mem.mention}]"
|
||||
for no, mem in enumerate(people, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Bots in {guild.name} - {len(people)}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="admins",
|
||||
aliases=["admin"],
|
||||
help="List of all Admins of the Guild",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_admin(self, ctx):
|
||||
mems = ([
|
||||
mem for mem in ctx.guild.members
|
||||
if mem.guild_permissions.administrator
|
||||
])
|
||||
mems = sorted(mems, key=lambda mem: not mem.bot)
|
||||
admins = len([
|
||||
mem for mem in ctx.guild.members
|
||||
if mem.guild_permissions.administrator
|
||||
])
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` [{mem}](https://discord.com/users/{mem.id}) [{mem.mention}] - <t:{int(mem.created_at.timestamp())}:D>"
|
||||
for no, mem in enumerate(mems, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Admins in {guild.name} - {admins}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="invoice", help="List of all users in a voice channel", aliases=["invc"], with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def listusers(self, ctx):
|
||||
if not ctx.author.voice:
|
||||
return await ctx.send("You are not connected to a voice channel")
|
||||
members = ctx.author.voice.channel.members
|
||||
entries = [
|
||||
f"`[{n}]` | {member} [{member.mention}]"
|
||||
for n, member in enumerate(members, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
description="",
|
||||
title=f"Voice List of {ctx.author.voice.channel.name} - {len(members)}",
|
||||
color=self.color),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="moderators", help= "List of All Admins of a server", aliases=["mods"], with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_mod(self, ctx):
|
||||
membs = ([
|
||||
mem for mem in ctx.guild.members
|
||||
if mem.guild_permissions.ban_members
|
||||
or mem.guild_permissions.kick_members
|
||||
])
|
||||
mems = filter(lambda member: member.bot, ctx.guild.members)
|
||||
mems = sorted(membs, key=lambda mem: mem.joined_at)
|
||||
admins = len([
|
||||
mem for mem in ctx.guild.members
|
||||
if mem.guild_permissions.ban_members
|
||||
or mem.guild_permissions.kick_members
|
||||
])
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` [{mem}](https://discord.com/users/{mem.id}) [{mem.mention}] - <t:{int(mem.created_at.timestamp())}:D>"
|
||||
for no, mem in enumerate(mems, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Mods in {guild.name} - {admins}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="early", aliases=["sup"], help= "List of members that have Early Supporter badge.", with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_early(self, ctx):
|
||||
mems = ([
|
||||
memb for memb in ctx.guild.members
|
||||
if memb.public_flags.early_supporter
|
||||
])
|
||||
mems = sorted(mems, key=lambda memb: memb.created_at)
|
||||
admins = len([
|
||||
memb for memb in ctx.guild.members
|
||||
if memb.public_flags.early_supporter
|
||||
])
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` [{mem}](https://discord.com/users/{mem.id}) [{mem.mention}] - <t:{int(mem.created_at.timestamp())}:D>"
|
||||
for no, mem in enumerate(mems, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Early Supporters Id's in {guild.name} - {admins}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="activedeveloper", help= "List of members that have Active Developer badge.",
|
||||
aliases=["activedev"],
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_activedeveloper(self, ctx):
|
||||
mems = ([
|
||||
memb for memb in ctx.guild.members
|
||||
if memb.public_flags.active_developer
|
||||
])
|
||||
mems = sorted(mems, key=lambda memb: memb.created_at)
|
||||
admins = len([
|
||||
memb for memb in ctx.guild.members
|
||||
if memb.public_flags.active_developer
|
||||
])
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` [{mem}](https://discord.com/users/{mem.id}) [{mem.mention}] - <t:{int(mem.created_at.timestamp())}:D>"
|
||||
for no, mem in enumerate(mems, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Active Developer Id's in {guild.name} - {admins}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="createdat", help= "List of Account Creation Date of all Users", with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_cpos(self, ctx):
|
||||
mems = ([memb for memb in ctx.guild.members])
|
||||
mems = sorted(mems, key=lambda memb: memb.created_at)
|
||||
admins = len([memb for memb in ctx.guild.members])
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`[{no}]` | [{mem}](https://discord.com/users/{mem.id}) - <t:{int(mem.created_at.timestamp())}:D>"
|
||||
for no, mem in enumerate(mems, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Creation every id in {guild.name} - {admins}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@__list_.command(name="joinedat", help= "List of Guild Joined date of all Users", with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def list_joinpos(self, ctx):
|
||||
mems = ([memb for memb in ctx.guild.members])
|
||||
mems = sorted(mems, key=lambda memb: memb.joined_at)
|
||||
admins = len([memb for memb in ctx.guild.members])
|
||||
guild = ctx.guild
|
||||
entries = [
|
||||
f"`#{no}.` [{mem}](https://discord.com/users/{mem.id}) Joined At - <t:{int(mem.joined_at.timestamp())}:D>"
|
||||
for no, mem in enumerate(mems, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Join Position of every user in {guild.name} - {admins}",
|
||||
description="",
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.command(name="joined-at",
|
||||
help="Shows when a user joined",
|
||||
usage="joined-at [user]",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def joined_at(self, ctx):
|
||||
joined = ctx.author.joined_at.strftime("%a, %d %b %Y %I:%M %p")
|
||||
await ctx.send(view=CV2("joined-at", f"**`{joined}`**"))
|
||||
|
||||
@commands.command(name="github", usage="github [search]")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def github(self, ctx, *, search_query):
|
||||
json = requests.get(
|
||||
f"https://api.github.com/search/repositories?q={search_query}").json()
|
||||
|
||||
if json["total_count"] == 0:
|
||||
await ctx.send(f"No matching repositories found with the name: {search_query}")
|
||||
else:
|
||||
await ctx.send(
|
||||
f"Found result for '{search_query}':\n{json['items'][0]['html_url']}")
|
||||
|
||||
@commands.hybrid_command(name="vcinfo",
|
||||
description="View information about a voice channel.",
|
||||
help="View information about a voice channel.",
|
||||
usage="<VoiceChannel>",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def vcinfo(self, ctx, channel: discord.VoiceChannel = None):
|
||||
if channel is None:
|
||||
await ctx.reply(view=CV2(f"{cross} Error", "Please provide a valid voice channel."))
|
||||
return
|
||||
vc_text = (
|
||||
f"**ID:** {channel.id}\n**Members:** {len(channel.members)}\n"
|
||||
f"**Bitrate:** {channel.bitrate/1000} kbps\n"
|
||||
f"**Created At:** {channel.created_at.strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
f"**Category:** {channel.category.name if channel.category else 'None'}\n"
|
||||
f"**Region:** {channel.rtc_region}"
|
||||
)
|
||||
if channel.user_limit:
|
||||
vc_text += f"\n**User Limit:** {channel.user_limit}"
|
||||
view = LayoutView(timeout=None)
|
||||
join_btn = Button(label="Join", style=discord.ButtonStyle.link, url=f"https://discord.com/channels/{ctx.guild.id}/{channel.id}")
|
||||
view.add_item(build_container(
|
||||
TextDisplay(f"**Voice Channel Info — {channel.name}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(vc_text),
|
||||
ActionRow(join_btn)
|
||||
))
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
@commands.hybrid_command(name="channelinfo",
|
||||
aliases=['cinfo', 'ci'],
|
||||
description='Get information about a channel.',
|
||||
help='Get information about a channel.',
|
||||
usage="<Channel>",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def channelinfo(self, ctx, channel: discord.TextChannel = None):
|
||||
if channel is None:
|
||||
channel = ctx.channel
|
||||
|
||||
ch_text = (
|
||||
f"**ID:** {channel.id}\n**Created At:** {channel.created_at.strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
f"**Category:** {channel.category.name if channel.category else 'None'}\n"
|
||||
f"**Topic:** {channel.topic if channel.topic else 'None'}\n"
|
||||
f"**Slowmode:** {f'{channel.slowmode_delay} seconds' if channel.slowmode_delay else 'None'}\n"
|
||||
f"**NSFW:** {channel.is_nsfw()}"
|
||||
)
|
||||
view = OverwritesView(channel, ctx.author.id)
|
||||
view.add_item(Button(label="Redirect Channel", style=discord.ButtonStyle.green, url=f"https://discord.com/channels/{ctx.guild.id}/{channel.id}"))
|
||||
await ctx.send(content=ch_text, view=view)
|
||||
|
||||
|
||||
@commands.hybrid_command(name="ping", aliases=['latency'], help="Checks the bot's latencies.")
|
||||
@ignore_check()
|
||||
@blacklist_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def ping(self, ctx: commands.Context):
|
||||
"""Shows the bot's WebSocket, API, and Database latencies."""
|
||||
|
||||
# 1. Start timer and send an initial "Pinging..." message
|
||||
start_time = time.monotonic()
|
||||
msg = await ctx.send(view=CV2("Checking Latency...", "Calculating response times, please wait."))
|
||||
end_time = time.monotonic()
|
||||
|
||||
bot_latency = round(self.bot.latency * 1000)
|
||||
api_latency = round((end_time - start_time) * 1000)
|
||||
|
||||
db_latency = "Not Available"
|
||||
try:
|
||||
async with aiosqlite.connect("db/afk.db") as db:
|
||||
db_start_time = time.perf_counter()
|
||||
await db.execute("SELECT 1")
|
||||
db_end_time = time.perf_counter()
|
||||
db_latency = f"{round((db_end_time - db_start_time) * 1000)}ms"
|
||||
except Exception as e:
|
||||
print(f"Database latency check failed: {e}")
|
||||
|
||||
latency_text = (
|
||||
f"**Bot (WebSocket):** `{bot_latency}ms`\n"
|
||||
f"**API (Roundtrip):** `{api_latency}ms`\n"
|
||||
f"**Database:** `{db_latency}`"
|
||||
)
|
||||
await msg.edit(view=CV2("System Latency Report", latency_text))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.command(name="permissions", aliases= ["perms"],
|
||||
help="Check and list the key permissions of a specific user",
|
||||
usage="perms <user>",
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def keyperms(self, ctx, member: discord.Member):
|
||||
key_permissions = []
|
||||
|
||||
if member.guild_permissions.create_instant_invite:
|
||||
key_permissions.append("Create Instant Invite")
|
||||
if member.guild_permissions.kick_members:
|
||||
key_permissions.append("Kick Members")
|
||||
if member.guild_permissions.ban_members:
|
||||
key_permissions.append("Ban Members")
|
||||
if member.guild_permissions.administrator:
|
||||
key_permissions.append("Administrator")
|
||||
if member.guild_permissions.manage_channels:
|
||||
key_permissions.append("Manage Channels")
|
||||
if member.guild_permissions.manage_messages:
|
||||
key_permissions.append("Manage Messages")
|
||||
if member.guild_permissions.mention_everyone:
|
||||
key_permissions.append("Mention Everyone")
|
||||
if member.guild_permissions.manage_nicknames:
|
||||
key_permissions.append("Manage Nicknames")
|
||||
if member.guild_permissions.manage_roles:
|
||||
key_permissions.append("Manage Roles")
|
||||
if member.guild_permissions.manage_webhooks:
|
||||
key_permissions.append("Manage Webhooks")
|
||||
if member.guild_permissions.manage_emojis:
|
||||
key_permissions.append("Manage Emojis")
|
||||
if member.guild_permissions.manage_guild:
|
||||
key_permissions.append("Manage Server")
|
||||
if member.guild_permissions.manage_permissions:
|
||||
key_permissions.append("Manage Permissions")
|
||||
if member.guild_permissions.manage_threads:
|
||||
key_permissions.append("Manage Threads")
|
||||
if member.guild_permissions.moderate_members:
|
||||
key_permissions.append("Moderate Members")
|
||||
if member.guild_permissions.move_members:
|
||||
key_permissions.append("Move Members")
|
||||
if member.guild_permissions.mute_members:
|
||||
key_permissions.append("Mute Members (VC)")
|
||||
if member.guild_permissions.deafen_members:
|
||||
key_permissions.append("Deafen Members")
|
||||
if member.guild_permissions.priority_speaker:
|
||||
key_permissions.append("Priority Speaker")
|
||||
if member.guild_permissions.stream:
|
||||
key_permissions.append("Stream")
|
||||
|
||||
|
||||
|
||||
|
||||
permissions_list = ", ".join(key_permissions) if key_permissions else "None"
|
||||
|
||||
await ctx.reply(view=CV2(f"Key Permissions of {member}", f"__**Key Permissions**__\n{permissions_list}"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.hybrid_command(name="report",
|
||||
aliases=["bug"],
|
||||
usage='Report <bug>',
|
||||
description='Report a bug to the Development team.',
|
||||
help='Report a bug to the Development team.',
|
||||
with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 30, commands.BucketType.channel)
|
||||
async def report(self, ctx, *, bug):
|
||||
channel = self.bot.get_channel(1396813063642153030)
|
||||
report_text = f"{bug}\n\n**Reported By:** {ctx.author.name}\n**Server:** {ctx.guild.name}\n**Channel:** {ctx.channel.name}"
|
||||
await channel.send(view=CV2("Bug Reported", report_text))
|
||||
await ctx.reply(view=CV2(f"{TICK} Bug Reported", "Thank you for reporting the bug. We will look into it."))
|
||||
|
||||
154
bot/cogs/commands/extraown.py
Normal file
154
bot/cogs/commands/extraown.py
Normal file
@@ -0,0 +1,154 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK, ZWARNING
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container, ActionRow, Button
|
||||
import aiosqlite
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ConfirmView(LayoutView):
|
||||
def __init__(self, ctx):
|
||||
super().__init__(timeout=60)
|
||||
self.ctx = ctx
|
||||
self.value = None
|
||||
|
||||
self.yes_btn = Button(label="Confirm", style=discord.ButtonStyle.green)
|
||||
self.no_btn = Button(label="Cancel", style=discord.ButtonStyle.red)
|
||||
|
||||
self.yes_btn.callback = self.confirm_callback
|
||||
self.no_btn.callback = self.cancel_callback
|
||||
|
||||
container = build_container(
|
||||
TextDisplay("**Confirm Action**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(self._desc),
|
||||
ActionRow(self.yes_btn, self.no_btn),
|
||||
)
|
||||
self.add_item(container)
|
||||
|
||||
@property
|
||||
def _desc(self):
|
||||
return ""
|
||||
|
||||
async def confirm_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user != self.ctx.author:
|
||||
return await interaction.response.send_message("You cannot interact with this confirmation.", ephemeral=True)
|
||||
self.value = True
|
||||
await interaction.response.defer()
|
||||
self.stop()
|
||||
|
||||
async def cancel_callback(self, interaction: discord.Interaction):
|
||||
if interaction.user != self.ctx.author:
|
||||
return await interaction.response.send_message("You cannot interact with this confirmation.", ephemeral=True)
|
||||
self.value = False
|
||||
await interaction.response.defer()
|
||||
self.stop()
|
||||
|
||||
|
||||
class SetConfirmView(ConfirmView):
|
||||
def __init__(self, ctx, user):
|
||||
self._user = user
|
||||
super().__init__(ctx)
|
||||
|
||||
@property
|
||||
def _desc(self):
|
||||
return f"**Are you sure you want to set {self._user.mention} as the Extra Owner?**"
|
||||
|
||||
|
||||
class ResetConfirmView(ConfirmView):
|
||||
@property
|
||||
def _desc(self):
|
||||
return "**Are you sure you want to reset the Extra Owner?**"
|
||||
|
||||
|
||||
class Extraowner(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.initialize_db())
|
||||
|
||||
async def initialize_db(self):
|
||||
self.db = await aiosqlite.connect('db/anti.db')
|
||||
await self.db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS extraowners (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
owner_id INTEGER
|
||||
)
|
||||
''')
|
||||
await self.db.commit()
|
||||
|
||||
@commands.hybrid_command(name='extraowner', aliases=["owner"], help="Adds Extraowner to the server")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def extraowner(self, ctx, option: str = None, user: discord.Member = None):
|
||||
guild_id = ctx.guild.id
|
||||
|
||||
if ctx.guild.member_count < 2:
|
||||
return await ctx.send(view=CV2(f"{CROSS} Error", "Your Server Doesn't Meet My 30 Member Criteria"))
|
||||
|
||||
Ray = ['870179991462236170','767979794411028491']
|
||||
if ctx.author.id != ctx.guild.owner_id and str(ctx.author.id) not in Ray:
|
||||
return await ctx.send(view=CV2(f"{ZWARNING} Access Denied", "Only Server Owner Can Run This Command"))
|
||||
|
||||
if option is None:
|
||||
pre = ctx.prefix
|
||||
info_text = (
|
||||
"Extraowners can adjust server antinuke settings & manage whitelist events, "
|
||||
"so careful consideration is essential before assigning it to someone.\n\n"
|
||||
f"**Extraowner Set** — `{pre}extraowner set @user`\n"
|
||||
f"**Extraowner Reset** — `{pre}extraowner reset`\n"
|
||||
f"**Extraowner View** — `{pre}extraowner view`"
|
||||
)
|
||||
return await ctx.reply(view=CV2("__Extra Owner__", info_text))
|
||||
|
||||
if option.lower() == 'set':
|
||||
if user is None or user.bot:
|
||||
return await ctx.reply(view=CV2(f"{CROSS} Error", "Please Provide a Valid User Mention or ID to Set as Extra Owner!"))
|
||||
|
||||
view = SetConfirmView(ctx, user)
|
||||
message = await ctx.reply(view=view)
|
||||
await view.wait()
|
||||
|
||||
if view.value is None:
|
||||
await message.edit(view=CV2("⏰ Timed Out", "Confirmation timed out."))
|
||||
elif view.value:
|
||||
await self.db.execute('INSERT OR REPLACE INTO extraowners (guild_id, owner_id) VALUES (?, ?)', (guild_id, user.id))
|
||||
await self.db.commit()
|
||||
await message.edit(view=CV2(f"{TICK} Success", f"Added {user.mention} As Extraowner"))
|
||||
else:
|
||||
await message.edit(view=CV2(f"{CROSS} Cancelled", "Action cancelled."))
|
||||
|
||||
elif option.lower() == 'reset':
|
||||
async with self.db.execute('SELECT owner_id FROM extraowners WHERE guild_id = ?', (guild_id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
await ctx.reply(view=CV2(f"{CROSS} Error", "No extra owner has been designated for this guild."))
|
||||
else:
|
||||
view = ResetConfirmView(ctx)
|
||||
message = await ctx.reply(view=view)
|
||||
await view.wait()
|
||||
|
||||
if view.value is None:
|
||||
await message.edit(view=CV2("⏰ Timed Out", "Confirmation timed out."))
|
||||
elif view.value:
|
||||
await self.db.execute('DELETE FROM extraowners WHERE guild_id = ?', (guild_id,))
|
||||
await self.db.commit()
|
||||
await message.edit(view=CV2(f"{TICK} Success", "Disabled Extraowner Configuration!"))
|
||||
else:
|
||||
await message.edit(view=CV2(f"{CROSS} Cancelled", "Action cancelled."))
|
||||
|
||||
elif option.lower() == 'view':
|
||||
async with self.db.execute('SELECT owner_id FROM extraowners WHERE guild_id = ?', (guild_id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
await ctx.reply(view=CV2(f"{CROSS} Error", "No extra owner is currently assigned."))
|
||||
else:
|
||||
await ctx.reply(view=CV2("Extra Owner", f"Current Extraowner is <@{row[0]}>"))
|
||||
78
bot/cogs/commands/fastgreet.py
Normal file
78
bot/cogs/commands/fastgreet.py
Normal file
@@ -0,0 +1,78 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import sqlite3
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
DB_PATH = "./db/fastgreet.db"
|
||||
|
||||
class FastGreet(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
os.makedirs("./db", exist_ok=True)
|
||||
self.init_db()
|
||||
|
||||
def init_db(self):
|
||||
with sqlite3.connect(DB_PATH) as conn:
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS greet_channels (
|
||||
guild_id INTEGER,
|
||||
channel_id INTEGER,
|
||||
PRIMARY KEY (guild_id, channel_id)
|
||||
)
|
||||
""")
|
||||
|
||||
@commands.command(name="fastgreet_add")
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def add_greet_channel(self, ctx, channel: discord.TextChannel):
|
||||
with sqlite3.connect(DB_PATH) as conn:
|
||||
conn.execute("""
|
||||
INSERT OR IGNORE INTO greet_channels (guild_id, channel_id)
|
||||
VALUES (?, ?)
|
||||
""", (ctx.guild.id, channel.id))
|
||||
await ctx.send(f"✅ {channel.mention} added as a greet channel.")
|
||||
|
||||
@commands.command(name="fastgreet_remove")
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def remove_greet_channel(self, ctx, channel: discord.TextChannel):
|
||||
with sqlite3.connect(DB_PATH) as conn:
|
||||
conn.execute("""
|
||||
DELETE FROM greet_channels WHERE guild_id = ? AND channel_id = ?
|
||||
""", (ctx.guild.id, channel.id))
|
||||
await ctx.send(f"❌ {channel.mention} removed from greet channels.")
|
||||
|
||||
@commands.command(name="fastgreet_list")
|
||||
async def list_greet_channels(self, ctx):
|
||||
with sqlite3.connect(DB_PATH) as conn:
|
||||
cursor = conn.execute("""
|
||||
SELECT channel_id FROM greet_channels WHERE guild_id = ?
|
||||
""", (ctx.guild.id,))
|
||||
rows = cursor.fetchall()
|
||||
|
||||
if not rows:
|
||||
await ctx.send("⚠️ No greet channels configured.")
|
||||
return
|
||||
|
||||
channels = [f"<#{cid[0]}>" for cid in rows]
|
||||
await ctx.send("📋 Greet Channels: " + ", ".join(channels))
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_join(self, member):
|
||||
with sqlite3.connect(DB_PATH) as conn:
|
||||
cursor = conn.execute("""
|
||||
SELECT channel_id FROM greet_channels WHERE guild_id = ?
|
||||
""", (member.guild.id,))
|
||||
channels = [row[0] for row in cursor.fetchall()]
|
||||
|
||||
for channel_id in channels:
|
||||
channel = self.bot.get_channel(channel_id)
|
||||
if channel:
|
||||
try:
|
||||
msg = await channel.send(f"{member.mention} Welcome!")
|
||||
await asyncio.sleep(2)
|
||||
await msg.delete()
|
||||
except discord.Forbidden:
|
||||
continue # Missing permissions
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(FastGreet(bot))
|
||||
134
bot/cogs/commands/filters.py
Normal file
134
bot/cogs/commands/filters.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from typing import Union
|
||||
import wavelink
|
||||
from utils.Tools import *
|
||||
|
||||
class FilterCog(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
self.active_filters = {}
|
||||
|
||||
async def apply_filter(self, ctx: commands.Context, filter_name: str):
|
||||
player: Union[wavelink.Player, None] = ctx.voice_client
|
||||
if not player or not player.playing:
|
||||
await ctx.send("I'm not playing anything.")
|
||||
return
|
||||
|
||||
if ctx.author.voice is None or ctx.author.voice.channel != player.channel:
|
||||
await ctx.send("You need to be in the same voice channel as me.")
|
||||
return
|
||||
|
||||
filters = wavelink.Filters()
|
||||
|
||||
if filter_name == "nightcore":
|
||||
filters.timescale.set(pitch=1.2, speed=1.2, rate=1)
|
||||
elif filter_name == "bassboost":
|
||||
filters.equalizer.set(bands=[{"band": 0, "gain": 0.5}, {"band": 1, "gain": 0.5}, {"band": 2, "gain": 0.5}])
|
||||
elif filter_name == "vaporwave":
|
||||
filters.timescale.set(rate=0.85, pitch=0.85)
|
||||
elif filter_name == "karaoke":
|
||||
filters.karaoke.set(level=1.0, mono_level=1.0, filter_band=220.0, filter_width=100.0)
|
||||
elif filter_name == "tremolo":
|
||||
filters.tremolo.set(depth=0.5, frequency=14.0)
|
||||
elif filter_name == "vibrato":
|
||||
filters.vibrato.set(depth=0.5, frequency=14.0)
|
||||
elif filter_name == "rotation":
|
||||
filters.rotation.set(rotation_hz=5.0)
|
||||
elif filter_name == "distortion":
|
||||
filters.distortion.set(
|
||||
sin_offset=0.0,
|
||||
sin_scale=1.0,
|
||||
cos_offset=0.0,
|
||||
cos_scale=1.0,
|
||||
tan_offset=0.0,
|
||||
tan_scale=1.0,
|
||||
offset=0.0,
|
||||
scale=1.0
|
||||
)
|
||||
elif filter_name == "channelmix":
|
||||
filters.channel_mix.set(left_to_left=0.5, left_to_right=0.5, right_to_left=0.5, right_to_right=0.5)
|
||||
|
||||
await player.set_filters(filters)
|
||||
self.active_filters[ctx.guild.id] = filter_name
|
||||
await ctx.send(embed=discord.Embed(description=f"Filter set to **{filter_name}**.", color=discord.Color.green()))
|
||||
|
||||
@commands.hybrid_group(invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def filter(self, ctx: commands.Context):
|
||||
await ctx.send("Use `filter enable` to enable a filter or `filter disable` to disable the current filter.")
|
||||
|
||||
@filter.command(help="Enable a filter.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def enable(self, ctx: commands.Context):
|
||||
player: Union[wavelink.Player, None] = ctx.voice_client
|
||||
if not player or not player.playing:
|
||||
await ctx.send("I'm not connected to a voice channel.")
|
||||
return
|
||||
|
||||
if ctx.author.voice is None or ctx.author.voice.channel != player.channel:
|
||||
await ctx.send("You need to be in the same voice channel as me.")
|
||||
return
|
||||
|
||||
filter_options = [
|
||||
|
||||
discord.SelectOption(label="Vaporwave", description="Apply vaporwave effect"),
|
||||
discord.SelectOption(label="Nightcore", description="Apply nightcore effect"),
|
||||
discord.SelectOption(label="Vibrato", description="Apply vibrato effect"),
|
||||
discord.SelectOption(label="Tremolo", description="Apply tremolo effect"),
|
||||
discord.SelectOption(label="Bassboost", description="Apply bass boost effect"),
|
||||
discord.SelectOption(label="Karaoke", description="Apply karaoke effect"),
|
||||
discord.SelectOption(label="Rotation", description="Apply rotation effect"),
|
||||
discord.SelectOption(label="Distortion", description="Apply distortion effect"),
|
||||
discord.SelectOption(label="Channelmix", description="Apply channel mix effect"),
|
||||
]
|
||||
|
||||
class FilterSelect(discord.ui.View):
|
||||
@discord.ui.select(placeholder="Choose a filter...", options=filter_options)
|
||||
async def select_filter(self, interaction: discord.Interaction, select: discord.ui.Select):
|
||||
await interaction.response.defer()
|
||||
selected_filter = select.values[0].lower()
|
||||
await self.cog.apply_filter(ctx, selected_filter)
|
||||
#await interaction.message.delete()
|
||||
self.disable_all()
|
||||
|
||||
@discord.ui.button(label="Cancel", style=discord.ButtonStyle.red)
|
||||
async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
await interaction.message.delete()
|
||||
self.disable_all()
|
||||
def disable_all(self):
|
||||
for child in self.children:
|
||||
child.disabled = True
|
||||
self.stop()
|
||||
|
||||
view = FilterSelect()
|
||||
view.cog = self
|
||||
|
||||
current_filter = self.active_filters.get(ctx.guild.id, "None")
|
||||
embed = discord.Embed(title="Enable Filter", description="Choose a filter to apply:", color=discord.Color.blue())
|
||||
embed.add_field(name="Current Filter", value=current_filter, inline=False)
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
@filter.command(help="Disable the current filter.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def disable(self, ctx: commands.Context):
|
||||
player: Union[wavelink.Player, None] = ctx.voice_client
|
||||
if not player or not player.playing:
|
||||
await ctx.send("I'm not connected to a voice channel.")
|
||||
return
|
||||
|
||||
if ctx.author.voice is None or ctx.author.voice.channel != player.channel:
|
||||
await ctx.send("You need to be in the same voice channel as me.")
|
||||
return
|
||||
|
||||
filters = wavelink.Filters()
|
||||
await player.set_filters(filters)
|
||||
self.active_filters.pop(ctx.guild.id, None)
|
||||
await ctx.send(embed=discord.Embed(description="Filter disabled.", color=discord.Color.red()))
|
||||
|
||||
174
bot/cogs/commands/fun.py
Normal file
174
bot/cogs/commands/fun.py
Normal file
@@ -0,0 +1,174 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, MediaGallery
|
||||
import random
|
||||
import aiohttp
|
||||
from discord import app_commands
|
||||
from utils.Tools import blacklist_check, ignore_check
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
class Fun(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.giphy_api_key = "y3KcqQTdiS0RYcpNJrWn8hFGglKqX4is"
|
||||
|
||||
async def fetch_giphy(self, query):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api.giphy.com/v1/gifs/search?api_key={self.giphy_api_key}&q={query}&limit=30&rating=pg") as resp:
|
||||
if resp.status != 200:
|
||||
return None
|
||||
data = await resp.json()
|
||||
if data['data']:
|
||||
return random.choice(data['data'])['images']['original']['url']
|
||||
else:
|
||||
return None
|
||||
|
||||
def random_emoji(self):
|
||||
return random.choice(["😂", "🤣", "😆", "😳", "🥴", "🙃", "😜"])
|
||||
|
||||
async def action_command(self, ctx, user: discord.Member, action: str):
|
||||
gif_url = await self.fetch_giphy(action)
|
||||
if not gif_url:
|
||||
await ctx.send(view=CV2("😒 Error", "GIPHY API is sleeping. Try later!"))
|
||||
return
|
||||
view = LayoutView(timeout=None)
|
||||
gallery = MediaGallery()
|
||||
gallery.add_item(media=gif_url)
|
||||
view.add_item(build_container(
|
||||
TextDisplay(f"**{ctx.author.mention} {action}s {user.mention} {self.random_emoji()}**"),
|
||||
gallery
|
||||
))
|
||||
await ctx.send(view=view)
|
||||
|
||||
async def meter_command(self, ctx, title, user, text):
|
||||
await ctx.send(view=CV2(title, text))
|
||||
|
||||
@commands.command(name="shipp")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def shipp(self, ctx, user1: discord.Member, user2: discord.Member):
|
||||
percentage = random.randint(0, 100)
|
||||
await ctx.send(view=CV2(f"{self.random_emoji()} Ship Result", f"**{user1.mention} x {user2.mention} = {percentage}% Love**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def hug(self, ctx, user: discord.Member):
|
||||
await self.action_command(ctx, user, "hug")
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def kiss(self, ctx, user: discord.Member):
|
||||
await self.action_command(ctx, user, "kiss")
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def pat(self, ctx, user: discord.Member):
|
||||
await self.action_command(ctx, user, "pat")
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def slap(self, ctx, user: discord.Member):
|
||||
await self.action_command(ctx, user, "slap")
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def tickle(self, ctx, user: discord.Member):
|
||||
await self.action_command(ctx, user, "tickle")
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def coinflip(self, ctx):
|
||||
result = random.choice(["Heads", "Tails"])
|
||||
await ctx.send(view=CV2("🪙 Coin Flip", f"**Result: {result}**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def dice(self, ctx):
|
||||
result = random.randint(1, 6)
|
||||
await ctx.send(view=CV2("🎲 Dice Roll", f"**You rolled a {result}!**"))
|
||||
|
||||
@commands.command(name="8ball")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def eight_ball(self, ctx, *, question: str):
|
||||
responses = ["It is certain.", "Without a doubt.", "You may rely on it.",
|
||||
"Ask again later.", "Better not tell you now.",
|
||||
"Don't count on it.", "My sources say no.", "Very doubtful."]
|
||||
await ctx.send(view=CV2("🎱 Magic 8Ball", f"**Q:** {question}\n**A:** {random.choice(responses)}"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def roast(self, ctx, user: discord.Member):
|
||||
roasts = [
|
||||
f"{user.mention} you're the reason shampoo has instructions!",
|
||||
f"{user.mention} you have something on your chin... no, the third one down!",
|
||||
f"{user.mention} your secrets are safe with me. I never even listen when you tell me them."
|
||||
]
|
||||
await ctx.send(view=CV2("🔥 Roast Time", random.choice(roasts)))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def iq(self, ctx, user: discord.Member = None):
|
||||
user = user or ctx.author
|
||||
await ctx.send(view=CV2("🧠 IQ Test", f"**{user.mention} has an IQ of {random.randint(50, 200)}!**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def dumb(self, ctx, user: discord.Member = None):
|
||||
user = user or ctx.author
|
||||
await ctx.send(view=CV2("🤪 Dumbness Test", f"**{user.mention} is {random.randint(0, 100)}% dumb!**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def simprate(self, ctx, user: discord.Member = None):
|
||||
user = user or ctx.author
|
||||
await ctx.send(view=CV2("😳 Simp Rate", f"**{user.mention} is {random.randint(0, 100)}% simp!**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def toxic(self, ctx, user: discord.Member = None):
|
||||
user = user or ctx.author
|
||||
await ctx.send(view=CV2("☠️ Toxic Meter", f"**{user.mention} is {random.randint(0, 100)}% toxic!**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def intelligence(self, ctx, user: discord.Member = None):
|
||||
user = user or ctx.author
|
||||
await ctx.send(view=CV2("🧠 Intelligence Meter", f"**{user.mention} has {random.randint(0, 200)} IQ Points!**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def genius(self, ctx, user: discord.Member = None):
|
||||
user = user or ctx.author
|
||||
await ctx.send(view=CV2("🤓 Genius Rate", f"**{user.mention} is {random.randint(0, 100)}% genius!**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def brainrate(self, ctx, user: discord.Member = None):
|
||||
user = user or ctx.author
|
||||
await ctx.send(view=CV2("🧠 Brain Power", f"**{user.mention} is using {random.randint(0, 100)}% of their brain!**"))
|
||||
|
||||
@commands.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def howhot(self, ctx, user: discord.Member = None):
|
||||
user = user or ctx.author
|
||||
await ctx.send(view=CV2("🔥 Hotness Meter", f"**{user.mention} is {random.randint(0, 100)}% hot!**"))
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Fun(bot))
|
||||
384
bot/cogs/commands/general.py
Normal file
384
bot/cogs/commands/general.py
Normal file
@@ -0,0 +1,384 @@
|
||||
import asyncio
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK, ZWARNING, ZYROXCONNECTION, ZYROXLINKS
|
||||
from discord.ext import commands, tasks
|
||||
from discord.utils import get
|
||||
import datetime
|
||||
import random
|
||||
import requests
|
||||
import aiohttp
|
||||
import re
|
||||
from discord.ext.commands.errors import BadArgument
|
||||
from discord.ext.commands import Cog
|
||||
from discord.colour import Color
|
||||
import hashlib
|
||||
from utils.Tools import *
|
||||
from traceback import format_exception
|
||||
from discord import ButtonStyle
|
||||
from discord.ui import Button, View, LayoutView, TextDisplay, Separator, MediaGallery, ActionRow
|
||||
import psutil
|
||||
import time
|
||||
from datetime import datetime, timezone, timedelta
|
||||
import sqlite3
|
||||
from typing import *
|
||||
import string
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
lawda = [
|
||||
'8', '3821', '23', '21', '313', '43', '29', '76', '11', '9',
|
||||
'44', '470', '318' , '26', '69'
|
||||
]
|
||||
|
||||
|
||||
class AvatarView(View):
|
||||
def __init__(self, user, member, author_id, banner_url):
|
||||
super().__init__()
|
||||
self.user = user
|
||||
self.member = member
|
||||
self.author_id = author_id
|
||||
self.banner_url = banner_url
|
||||
|
||||
if self.user.avatar.is_animated():
|
||||
self.add_item(Button(label='GIF', url=self.user.avatar.with_format('gif').url, style=discord.ButtonStyle.link))
|
||||
self.add_item(Button(label='PNG', url=self.user.avatar.with_format('png').url, style=discord.ButtonStyle.link))
|
||||
self.add_item(Button(label='JPEG', url=self.user.avatar.with_format('jpg').url, style=discord.ButtonStyle.link))
|
||||
self.add_item(Button(label='WEBP', url=self.user.avatar.with_format('webp').url, style=discord.ButtonStyle.link))
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||
if interaction.user.id != self.author_id:
|
||||
await interaction.response.send_message(
|
||||
"Uh oh! That message doesn't belong to you. You must run this command to interact with it.",
|
||||
ephemeral=True
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
@discord.ui.button(label='Server Avatar', style=discord.ButtonStyle.success, custom_id='server_avatar_button')
|
||||
async def server_avatar(self, interaction: discord.Interaction, button: Button):
|
||||
if not self.member.guild_avatar:
|
||||
await interaction.response.send_message(
|
||||
"This user doesn't have a different guild avatar.",
|
||||
ephemeral=True
|
||||
)
|
||||
else:
|
||||
embed = interaction.message.embeds[0]
|
||||
embed.set_image(url=self.member.guild_avatar.url)
|
||||
await interaction.response.edit_message(embed=embed)
|
||||
|
||||
@discord.ui.button(label='User Banner', style=discord.ButtonStyle.success, custom_id='banner_button')
|
||||
async def banner(self, interaction: discord.Interaction, button: Button):
|
||||
if not self.banner_url:
|
||||
await interaction.response.send_message(
|
||||
"This user doesn't have a banner.",
|
||||
ephemeral=True
|
||||
)
|
||||
else:
|
||||
embed = interaction.message.embeds[0]
|
||||
embed.set_image(url=self.banner_url)
|
||||
await interaction.response.edit_message(embed=embed)
|
||||
|
||||
|
||||
from utils.config import BotName
|
||||
|
||||
class General(commands.Cog):
|
||||
|
||||
def __init__(self, bot, *args, **kwargs):
|
||||
self.bot = bot
|
||||
self.aiohttp = aiohttp.ClientSession()
|
||||
self._URL_REGEX = r'(?P<url><[^: >]+:\/[^ >]+>|(?:https?|steam):\/\/[^\s<]+[^<.,:;"\'\\]\s])'
|
||||
self.color = 0xFF0000
|
||||
|
||||
|
||||
@commands.hybrid_command(
|
||||
usage="Avatar <member>",
|
||||
name='avatar',
|
||||
aliases=['av'],
|
||||
help="Get User avater/Guild avatar & Banner of a user."
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def _user(self, ctx, member: Optional[Union[discord.Member, discord.User]] = None):
|
||||
try:
|
||||
if member is None:
|
||||
member = ctx.author
|
||||
user = await self.bot.fetch_user(member.id)
|
||||
|
||||
banner_url = user.banner.url if user.banner else None
|
||||
|
||||
# Avatar still uses embed because AvatarView buttons edit embed images
|
||||
description = f"[`PNG`]({user.avatar.with_format('png').url}) | [`JPG`]({user.avatar.with_format('jpg').url}) | [`WEBP`]({user.avatar.with_format('webp').url})"
|
||||
if user.avatar.is_animated():
|
||||
description += f" | [`GIF`]({user.avatar.with_format('gif').url})"
|
||||
if banner_url:
|
||||
description += f" | [`Banner`]({banner_url})"
|
||||
|
||||
embed = discord.Embed(
|
||||
color=self.color,
|
||||
description=description
|
||||
)
|
||||
embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else member.default_avatar.url)
|
||||
embed.set_image(url=user.avatar.url)
|
||||
embed.set_footer(text=f"Requested By {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
|
||||
view = AvatarView(user, member, ctx.author.id, banner_url)
|
||||
await ctx.send(embed=embed, view=view)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
@commands.hybrid_command(
|
||||
name="servericon",
|
||||
help="Get the server icon",
|
||||
usage="Servericon"
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def servericon(self, ctx: commands.Context):
|
||||
server = ctx.guild
|
||||
if server.icon is None:
|
||||
await ctx.reply(view=CV2("❌ Error", "This server does not have an icon."))
|
||||
return
|
||||
|
||||
webp = server.icon.replace(format='webp')
|
||||
jpg = server.icon.replace(format='jpg')
|
||||
png = server.icon.replace(format='png')
|
||||
|
||||
links = f"[`PNG`]({png}) | [`JPG`]({jpg}) | [`WEBP`]({webp})"
|
||||
if server.icon.is_animated():
|
||||
gif = server.icon.replace(format='gif')
|
||||
links += f" | [`GIF`]({gif})"
|
||||
|
||||
view = LayoutView(timeout=None)
|
||||
gallery = MediaGallery()
|
||||
gallery.add_item(media=str(server.icon.url))
|
||||
dl_btn = Button(label="Download Icon", url=str(server.icon.url), style=ButtonStyle.link)
|
||||
view.add_item(build_container(
|
||||
TextDisplay(f"**{server}'s Icon**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(links),
|
||||
gallery,
|
||||
ActionRow(dl_btn)
|
||||
))
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
@commands.hybrid_command(name="membercount",
|
||||
help="Get total member count of the server",
|
||||
usage="membercount",
|
||||
aliases=["mc"])
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 2, commands.BucketType.user)
|
||||
async def membercount(self, ctx: commands.Context):
|
||||
total_members = len(ctx.guild.members)
|
||||
total_humans = len([m for m in ctx.guild.members if not m.bot])
|
||||
total_bots = len([m for m in ctx.guild.members if m.bot])
|
||||
|
||||
online = len([m for m in ctx.guild.members if m.status == discord.Status.online])
|
||||
offline = len([m for m in ctx.guild.members if m.status == discord.Status.offline])
|
||||
idle = len([m for m in ctx.guild.members if m.status == discord.Status.idle])
|
||||
dnd = len([m for m in ctx.guild.members if m.status == discord.Status.do_not_disturb])
|
||||
|
||||
stats_text = (
|
||||
f"**__Count Stats:__**\n"
|
||||
f"Total Members: {total_members}\nTotal Humans: {total_humans}\nTotal Bots: {total_bots}\n\n"
|
||||
f"**__Presence Stats:__**\n"
|
||||
f"Online: {online} | DND: {dnd} | Idle: {idle} | Offline: {offline}"
|
||||
)
|
||||
await ctx.send(view=CV2("Member Statistics", stats_text))
|
||||
|
||||
@commands.hybrid_command(name="poll", usage="Poll <message>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def poll(self, ctx: commands.Context, *, message):
|
||||
author = ctx.author
|
||||
msg = await ctx.send(view=CV2(f"**Poll raised by {author}!**", message))
|
||||
await msg.add_reaction(TICK)
|
||||
await msg.add_reaction(CROSS)
|
||||
|
||||
|
||||
@commands.command(name="hack",
|
||||
help="hack someone's discord account",
|
||||
usage="Hack <member>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def hack(self, ctx: commands.Context, member: discord.Member):
|
||||
stringi = member.name
|
||||
min_length = 2
|
||||
max_length = 12
|
||||
length = random.randint(min_length, max_length)
|
||||
stringg = member.name
|
||||
remaining_length = length - len(stringg)
|
||||
all_chars = string.ascii_letters + string.digits + string.punctuation
|
||||
random_chars = random.choices(all_chars, k=remaining_length)
|
||||
|
||||
password = stringg + ''.join(random_chars)
|
||||
|
||||
lund = await ctx.send(f"Processing to Hack {member.mention}...")
|
||||
await asyncio.sleep(2)
|
||||
random_pass = random.choice(lawda)
|
||||
|
||||
random_pass2 = ''.join(random.choices(string.ascii_letters + string.digits, k=3))
|
||||
hack_text = (
|
||||
f"**User:** {member.mention}\n"
|
||||
f"**E-Mail:** {''.join(c for c in stringi if c.isalnum())}{random_pass}@gmail.com\n"
|
||||
f"**Account Password:** {member.name}@{random_pass2}"
|
||||
)
|
||||
await ctx.send(view=CV2(f"Hacked {member.display_name}!", hack_text))
|
||||
await lund.delete()
|
||||
|
||||
|
||||
@commands.command(name="token", usage="Token <member>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 2, commands.BucketType.user)
|
||||
async def token(self, ctx: commands.Context, user: discord.Member = None):
|
||||
list = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "_"
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'ñ', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9'
|
||||
]
|
||||
token = random.choices(list, k=59)
|
||||
if user is None:
|
||||
user = ctx.author
|
||||
await ctx.send(view=CV2("🔐 Token", f"{user.mention}'s token: `{''.join(token)}`"))
|
||||
|
||||
@commands.command(name="users", help=f"checks total users of {BotName}.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def users(self, ctx: commands.Context):
|
||||
users = sum(g.member_count for g in self.bot.guilds
|
||||
if g.member_count != None)
|
||||
guilds = len(self.bot.guilds)
|
||||
await ctx.send(view=CV2(f"{BotName} Users", f"❯ Total of __**{users}**__ Users in **{guilds}** Guilds"))
|
||||
|
||||
|
||||
@commands.command(name="wizz", usage="Wizz")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def wizz(self, ctx: commands.Context):
|
||||
message6 = await ctx.send(
|
||||
f"`Wizzing {ctx.guild.name}, will take 22 seconds to complete`")
|
||||
message7 = await ctx.send(f"Changing all guild settings...")
|
||||
message5 = await ctx.send(f"Deleting **{len(ctx.guild.roles)}** Roles...")
|
||||
await asyncio.sleep(1)
|
||||
message4 = await ctx.send(
|
||||
f"Deleting **{len(ctx.guild.channels)}** Channels...")
|
||||
await asyncio.sleep(1)
|
||||
message3 = await ctx.send(f"Deleting Webhooks...")
|
||||
message2 = await ctx.send(f"Deleting emojis")
|
||||
await asyncio.sleep(1)
|
||||
message1 = await ctx.send(f"Installing Ban Wave..")
|
||||
await asyncio.sleep(1)
|
||||
await message6.delete()
|
||||
await message7.delete()
|
||||
await message5.delete()
|
||||
await message4.delete()
|
||||
await message3.delete()
|
||||
await message2.delete()
|
||||
await message1.delete()
|
||||
await ctx.send(view=CV2(f"{self.bot.user.name}", f"**{ZWARNING} Successfully Wizzed {ctx.guild.name}**"))
|
||||
|
||||
|
||||
@commands.hybrid_command(
|
||||
name="urban",
|
||||
description="Searches for specified phrase on urbandictionary",
|
||||
help="Get meaning of specified phrase",
|
||||
usage="Urban <phrase>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def urban(self, ctx: commands.Context, *, phrase):
|
||||
async with self.aiohttp.get(
|
||||
"http://api.urbandictionary.com/v0/define?term={}".format(
|
||||
phrase)) as urb:
|
||||
urban = await urb.json()
|
||||
try:
|
||||
definition = urban['list'][0]['definition'].replace('[', '').replace(']', '')
|
||||
example = urban['list'][0]['example'].replace('[', '').replace(']', '')
|
||||
author = urban['list'][0]['author'].replace('[', '').replace(']', '')
|
||||
written_on = urban['list'][0]['written_on'].replace('[', '').replace(']', '')
|
||||
|
||||
urban_text = (
|
||||
f"**__Definition:__**\n{definition}\n\n"
|
||||
f"**__Example:__**\n{example}\n\n"
|
||||
f"**__Author:__** {author}\n"
|
||||
f"**__Written On:__** {written_on}"
|
||||
)
|
||||
temp = await ctx.reply(view=CV2(f"Meaning of \"{phrase}\"", urban_text), mention_author=True)
|
||||
await asyncio.sleep(45)
|
||||
await temp.delete()
|
||||
await ctx.message.delete()
|
||||
except:
|
||||
pass
|
||||
|
||||
@commands.command(name="rickroll",
|
||||
help="Detects if provided url is a rick-roll",
|
||||
usage="Rickroll <url>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def rickroll(self, ctx: commands.Context, *, url: str):
|
||||
if not re.match(self._URL_REGEX, url):
|
||||
raise BadArgument("Invalid URL")
|
||||
|
||||
phrases = [
|
||||
"rickroll", "rick roll", "rick astley", "never gonna give you up"
|
||||
]
|
||||
source = str(await (await self.aiohttp.get(
|
||||
url, allow_redirects=True)).content.read()).lower()
|
||||
rickRoll = bool((re.findall('|'.join(phrases), source,
|
||||
re.MULTILINE | re.IGNORECASE)))
|
||||
title = "Rick Roll {} in webpage".format("was found" if rickRoll else "was not found")
|
||||
await ctx.reply(view=CV2(title, "🎵 Never gonna give you up..." if rickRoll else "✅ Safe to click!"), mention_author=True)
|
||||
|
||||
@commands.command(name="hash",
|
||||
help="Hashes provided text with provided algorithm")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def hash(self, ctx: commands.Context, algorithm: str, *, message):
|
||||
algos: dict[str, str] = {
|
||||
"md5": hashlib.md5(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha1": hashlib.sha1(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha224": hashlib.sha224(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha3_224": hashlib.sha3_224(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha256": hashlib.sha256(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha3_256": hashlib.sha3_256(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha384": hashlib.sha384(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha3_384": hashlib.sha3_384(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha512": hashlib.sha512(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"sha3_512": hashlib.sha3_512(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"blake2b": hashlib.blake2b(bytes(message.encode("utf-8"))).hexdigest(),
|
||||
"blake2s": hashlib.blake2s(bytes(message.encode("utf-8"))).hexdigest()
|
||||
}
|
||||
if algorithm.lower() not in list(algos.keys()):
|
||||
hash_lines = "\n".join(f"**{algo}:** `{algos[algo]}`" for algo in algos)
|
||||
else:
|
||||
hash_lines = f"**{algorithm}:** `{algos[algorithm.lower()]}`"
|
||||
await ctx.reply(view=CV2(f"Hashed \"{message}\"", hash_lines), mention_author=True)
|
||||
|
||||
@commands.command(
|
||||
name="invite",
|
||||
aliases=['invite-bot'],
|
||||
description="Get Support & Bot invite link!"
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def invite(self, ctx: commands.Context):
|
||||
invite_text = (
|
||||
"```Empower your server with blazing-fast features and 24/7 support!```\n"
|
||||
f"{ZYROXLINKS} **Quick Actions**\n"
|
||||
f">>> **[Invite {BotName}](https://discord.com/oauth2/authorize?client_id=1396114795102470196&permissions=8&integration_type=0&scope=bot+applications.commands)**\n"
|
||||
"**[Support Server](https://discord.gg/codexdev)**"
|
||||
)
|
||||
await ctx.send(view=CV2(f"{ZYROXCONNECTION} {BotName} Integration Hub!", invite_text))
|
||||
399
bot/cogs/commands/giveaway.py
Normal file
399
bot/cogs/commands/giveaway.py
Normal file
@@ -0,0 +1,399 @@
|
||||
from discord.ext import commands, tasks
|
||||
import datetime, pytz, time as t
|
||||
from discord.ui import Button, Select, View
|
||||
import aiosqlite, random, typing
|
||||
import sqlite3
|
||||
import asyncio
|
||||
import discord, logging
|
||||
from utils.emoji import ARROWRED, TADAA, TICK
|
||||
from discord.utils import get
|
||||
from utils.Tools import *
|
||||
import os
|
||||
import aiohttp
|
||||
from utils.cv2 import CV2
|
||||
|
||||
db_folder = 'db'
|
||||
db_file = 'giveaways.db'
|
||||
db_path = os.path.join(db_folder, db_file)
|
||||
connection = sqlite3.connect(db_path)
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
cursor.execute('''CREATE TABLE IF NOT EXISTS Giveaway (
|
||||
guild_id INTEGER,
|
||||
host_id INTEGER,
|
||||
start_time TIMESTAMP,
|
||||
ends_at TIMESTAMP,
|
||||
prize TEXT,
|
||||
winners INTEGER,
|
||||
message_id INTEGER,
|
||||
channel_id INTEGER,
|
||||
PRIMARY KEY (guild_id, message_id)
|
||||
)''')
|
||||
|
||||
connection.commit()
|
||||
connection.close()
|
||||
|
||||
def convert(time):
|
||||
pos = ["s","m","h","d"]
|
||||
time_dict = {"s" : 1, "m" : 60, "h" : 3600 , "d" : 86400 , "f" : 259200}
|
||||
unit = time[-1]
|
||||
if unit not in pos:
|
||||
return
|
||||
try:
|
||||
val = int(time[:-1])
|
||||
except ValueError:
|
||||
return
|
||||
return val * time_dict[unit]
|
||||
|
||||
def WinnerConverter(winner):
|
||||
try:
|
||||
int(winner)
|
||||
except ValueError:
|
||||
try:
|
||||
return int(winner[:-1])
|
||||
except:
|
||||
return -4
|
||||
return winner
|
||||
|
||||
class Giveaway(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
async def cog_load(self) -> None:
|
||||
self.connection = await aiosqlite.connect(db_path)
|
||||
self.cursor = await self.connection.cursor()
|
||||
await self.check_for_ended_giveaways()
|
||||
self.GiveawayEnd.start()
|
||||
|
||||
async def cog_unload(self) -> None:
|
||||
await self.connection.close()
|
||||
|
||||
async def check_for_ended_giveaways(self):
|
||||
await self.cursor.execute("SELECT ends_at, guild_id, message_id, host_id, winners, prize, channel_id FROM Giveaway WHERE ends_at <= ?", (datetime.datetime.now().timestamp(),))
|
||||
ended_giveaways = await self.cursor.fetchall()
|
||||
for giveaway in ended_giveaways:
|
||||
await self.end_giveaway(giveaway)
|
||||
|
||||
async def end_giveaway(self, giveaway):
|
||||
try:
|
||||
current_time = datetime.datetime.now().timestamp()
|
||||
guild = self.bot.get_guild(int(giveaway[1]))
|
||||
if guild is None:
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (giveaway[2], giveaway[1]))
|
||||
await self.connection.commit()
|
||||
return
|
||||
|
||||
channel = self.bot.get_channel(int(giveaway[6]))
|
||||
if channel is not None:
|
||||
try:
|
||||
retries = 3
|
||||
for attempt in range(retries):
|
||||
try:
|
||||
message = await channel.fetch_message(int(giveaway[2]))
|
||||
break
|
||||
except discord.NotFound:
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (giveaway[2], giveaway[1]))
|
||||
await self.connection.commit()
|
||||
return
|
||||
except aiohttp.ClientResponseError as e:
|
||||
if e.status == 503:
|
||||
if attempt < retries - 1:
|
||||
await asyncio.sleep(2 ** attempt)
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise
|
||||
|
||||
users = [i.id async for i in message.reactions[0].users()]
|
||||
if self.bot.user.id in users:
|
||||
users.remove(self.bot.user.id)
|
||||
|
||||
if len(users) < 1:
|
||||
await message.reply(f"No one won the **{giveaway[5]}** giveaway, due to Not enough participants.")
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (message.id, message.guild.id))
|
||||
await self.connection.commit()
|
||||
return
|
||||
|
||||
winners_count = min(len(users), int(giveaway[4]))
|
||||
winner = ', '.join(f'<@!{i}>' for i in random.sample(users, k=winners_count))
|
||||
|
||||
desc = f"Ended at <t:{int(current_time)}:R>\nHosted by <@{int(giveaway[3])}>\nWinner(s): {winner}"
|
||||
view = CV2(f"{giveaway[5]}", desc)
|
||||
|
||||
await message.edit(content=f"{TADAA} **GIVEAWAY ENDED** {TADAA}", view=view)
|
||||
await message.reply(f"{TADAA} Congrats {winner}, you won **{giveaway[5]}!**, Hosted by <@{int(giveaway[3])}>")
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (message.id, message.guild.id))
|
||||
await self.connection.commit()
|
||||
|
||||
except (discord.HTTPException, aiohttp.ClientResponseError) as e:
|
||||
logging.error(f"Error ending giveaway: {e}")
|
||||
|
||||
except IndexError:
|
||||
logging.error(f"Giveaway data is corrupted or missing: {giveaway}")
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (giveaway[2], giveaway[1]))
|
||||
await self.connection.commit()
|
||||
|
||||
@tasks.loop(seconds=5)
|
||||
async def GiveawayEnd(self):
|
||||
await self.cursor.execute("SELECT ends_at, guild_id, message_id, host_id, winners, prize, channel_id FROM Giveaway WHERE ends_at <= ?", (datetime.datetime.now().timestamp(),))
|
||||
ends_raw = await self.cursor.fetchall()
|
||||
for giveaway in ends_raw:
|
||||
await self.end_giveaway(giveaway)
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.hybrid_command(description="Starts a new giveaway.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_guild_permissions(manage_guild=True)
|
||||
async def gstart(self, ctx,
|
||||
time,
|
||||
winners: int,
|
||||
*,
|
||||
prize: str):
|
||||
|
||||
await self.cursor.execute("SELECT message_id, channel_id FROM Giveaway WHERE guild_id = ?", (ctx.guild.id,))
|
||||
re = await self.cursor.fetchall()
|
||||
|
||||
if winners >= 15:
|
||||
message = await ctx.send(view=CV2("⚠️ Access Denied", "Cannot exceed more than 15 winners."))
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
g_list = [i[0] for i in re]
|
||||
if len(g_list) >= 5:
|
||||
message = await ctx.send(view=CV2("⚠️ Access Denied", "You can only host upto 5 giveaways in this Guild."))
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
converted = self.convert(time)
|
||||
if converted / 60 >= 50400:
|
||||
message = await ctx.send(view=CV2("⚠️ Access Denied", "Time cannot exceed 31 days!"))
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
if converted == -1:
|
||||
message = await ctx.send(view=CV2("❌ Error", "Invalid time format"))
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
return
|
||||
if converted == -2:
|
||||
message = await ctx.send(view=CV2("❌ Error", "Invalid time format. Please provide the time in numbers."))
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
ends = (datetime.datetime.now().timestamp() + converted)
|
||||
|
||||
desc = (
|
||||
f"{ARROWRED} Winner(s): **{winners}**\n"
|
||||
f"{ARROWRED} Hosted by {ctx.author.mention}\n"
|
||||
f"{ARROWRED} Ends <t:{round(ends)}:R> (<t:{round(ends)}:f>)\n\n"
|
||||
f"{ARROWRED} React with {TADAA} to participate!"
|
||||
)
|
||||
|
||||
view = CV2(f"{TADAA} {prize}", desc)
|
||||
|
||||
message = await ctx.send(f"{TADAA} **GIVEAWAY** {TADAA}", view=view)
|
||||
try:
|
||||
await ctx.message.delete()
|
||||
except:
|
||||
pass
|
||||
|
||||
await self.cursor.execute("INSERT INTO Giveaway(guild_id, host_id, start_time, ends_at, prize, winners, message_id, channel_id) VALUES(?, ?, ?, ?, ?, ?, ?, ?)", (ctx.guild.id, ctx.author.id, datetime.datetime.now(), ends, prize, winners, message.id, ctx.channel.id))
|
||||
|
||||
await message.add_reaction(TADAA)
|
||||
await self.connection.commit()
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.Cog.listener("on_message_delete")
|
||||
async def GiveawayMessageDelete(self, message):
|
||||
await self.cursor.execute("SELECT message_id FROM Giveaway WHERE guild_id = ?", (message.guild.id,))
|
||||
re = await self.cursor.fetchone()
|
||||
|
||||
if message.author != self.bot.user:
|
||||
return
|
||||
|
||||
if re is not None:
|
||||
if message.id == int(re[0]):
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE channel_id = ? AND message_id = ? AND guild_id = ?", (message.channel.id, message.id, message.guild.id))
|
||||
|
||||
print(f"Giveaway message deleted in {message.guild.name} - {message.guild.id}")
|
||||
await self.connection.commit()
|
||||
|
||||
@commands.hybrid_command(name="gend", description="Ends a giveaway before its ending time.", help="Ends a giveaway before its ending time.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_guild_permissions(manage_guild=True)
|
||||
async def gend(self, ctx, message_id = None):
|
||||
if message_id:
|
||||
try:
|
||||
int(message_id)
|
||||
except ValueError:
|
||||
message = await ctx.send(view=CV2("⚠️ Access Denied", "Invalid message ID provided."))
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
if message_id is not None:
|
||||
current_time = datetime.datetime.now().timestamp()
|
||||
await self.cursor.execute('SELECT ends_at, guild_id, message_id, host_id, winners, prize, channel_id FROM Giveaway WHERE message_id = ?', (int(message_id),))
|
||||
re = await self.cursor.fetchone()
|
||||
|
||||
if re is None:
|
||||
message = await ctx.send(view=CV2("❌ Error", "The giveaway was not found."))
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
ch = self.bot.get_channel(int(re[6]))
|
||||
message = await ch.fetch_message(int(message_id))
|
||||
|
||||
users = [i.id async for i in message.reactions[0].users()]
|
||||
users.remove(self.bot.user.id)
|
||||
|
||||
if len(users) < 1:
|
||||
await ctx.send(f"{TICK} Successfully Ended the giveaway in <#{int(re[6])}>")
|
||||
await message.reply(f"No one won the **{re[5]}** giveaway, due to Not enough participants.")
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (message.id, message.guild.id))
|
||||
return
|
||||
|
||||
winner = ', '.join(f'<@!{i}>' for i in random.sample(users, k=int(re[4])))
|
||||
|
||||
desc = f"Ended at <t:{int(current_time)}:R>\nHosted by <@{int(re[3])}>\nWinner(s): {winner}"
|
||||
view = CV2(f"🎁 {re[5]}", desc)
|
||||
|
||||
await message.edit(content="🎁 **GIVEAWAY ENDED** 🎁", view=view)
|
||||
|
||||
if int(ctx.channel.id) != int(re[6]):
|
||||
await ctx.send(f"{TADAA} Successfully ended the giveaway in <#{int(re[6])}>")
|
||||
|
||||
await message.reply(f" Congrats {winner}, you won **{re[5]}!**, Hosted by <@{int(re[3])}>")
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (message.id, message.guild.id))
|
||||
|
||||
elif ctx.message.reference:
|
||||
await self.cursor.execute('SELECT ends_at, guild_id, message_id, host_id, winners, prize, channel_id FROM Giveaway WHERE message_id = ?', (ctx.message.reference.resolved.id,))
|
||||
re = await self.cursor.fetchone()
|
||||
|
||||
if re is None:
|
||||
return await ctx.send(f"The giveaway was not found.")
|
||||
|
||||
current_time = datetime.datetime.now().timestamp()
|
||||
|
||||
message = await ctx.fetch_message(ctx.message.reference.message_id)
|
||||
|
||||
users = [i.id async for i in message.reactions[0].users()]
|
||||
try: users.remove(self.bot.user.id)
|
||||
except: pass
|
||||
|
||||
if len(users) < 1:
|
||||
await message.reply(f"No one won the **{re[5]}** giveaway, due to not enough participants.")
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (message.id, message.guild.id))
|
||||
return
|
||||
|
||||
winner = ', '.join(f'<@!{i}>' for i in random.sample(users, k=int(re[4])))
|
||||
|
||||
desc = f"Ended <t:{int(current_time)}:R>\nHosted by <@{int(re[3])}>\nWinner(s): {winner}"
|
||||
view = CV2(f"🎁 {re[5]}", desc)
|
||||
|
||||
await message.edit(content="🎁 **GIVEAWAY ENDED** 🎁", view=view)
|
||||
|
||||
await message.reply(f"💐 Congrats {winner}, you won **{re[5]}!**, Hosted by <@{int(re[3])}>")
|
||||
await self.cursor.execute("DELETE FROM Giveaway WHERE message_id = ? AND guild_id = ?", (message.id, message.guild.id))
|
||||
|
||||
else:
|
||||
await ctx.send("Please reply to the giveaway message or provide the giveaway ID.")
|
||||
await self.connection.commit()
|
||||
|
||||
@commands.hybrid_command(description="Rerolls a giveaway on replying the giveaway message.", help="Rerolls a giveaway on replying the giveaway message.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_guild_permissions(manage_guild=True)
|
||||
async def greroll(self, ctx, message_id: typing.Optional[int] = None):
|
||||
if not ctx.message.reference:
|
||||
message = await ctx.reply("Reply this command with the Giveaway Ended message to reroll.")
|
||||
await asyncio.sleep(5)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
if ctx.message.reference:
|
||||
message_id = ctx.message.reference.resolved.id
|
||||
|
||||
message = await ctx.fetch_message(message_id)
|
||||
|
||||
if ctx.message.reference.resolved.author.id != self.bot.user.id :
|
||||
msg = await ctx.send(view=CV2("⚠️ Access Denied", "The giveaway was not found."))
|
||||
await asyncio.sleep(5)
|
||||
await msg.delete()
|
||||
return
|
||||
|
||||
|
||||
await self.cursor.execute(f"SELECT message_id FROM Giveaway WHERE message_id = ?", (message.id,))
|
||||
re = await self.cursor.fetchone()
|
||||
|
||||
if re is not None:
|
||||
msg = await ctx.send(view=CV2("⚠️ Access Denied", "The giveaway is currently running. Please use the `gend` command instead to end the giveaway."))
|
||||
await asyncio.sleep(5)
|
||||
await msg.delete()
|
||||
return
|
||||
|
||||
users = [i.id async for i in message.reactions[0].users()]
|
||||
users.remove(self.bot.user.id)
|
||||
|
||||
if len(users) < 1:
|
||||
await message.reply(f"No one won the **{re[5]}** giveaway, due to not enough participants.")
|
||||
return
|
||||
|
||||
winners = random.sample(users, k=1)
|
||||
await message.reply(f" The new winner is "+", ".join(f"<@{i}>" for i in winners)+". Congratulations!")
|
||||
await self.connection.commit()
|
||||
|
||||
def convert(self, time):
|
||||
pos = ["s", "m", "h", "d"]
|
||||
time_dict = {"s": 1, "m": 60, "h": 3600, "d": 86400, "f": 259200}
|
||||
|
||||
unit = time[-1]
|
||||
if unit not in pos:
|
||||
return -1
|
||||
|
||||
try:
|
||||
val = int(time[:-1])
|
||||
except ValueError:
|
||||
return -2
|
||||
|
||||
return val * time_dict[unit]
|
||||
|
||||
|
||||
@commands.hybrid_command(name="glist", description="Lists all ongoing giveaways.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.has_guild_permissions(manage_guild=True)
|
||||
async def glist(self, ctx):
|
||||
await self.cursor.execute("SELECT prize, ends_at, winners, message_id FROM Giveaway WHERE guild_id = ?", (ctx.guild.id,))
|
||||
giveaways = await self.cursor.fetchall()
|
||||
|
||||
if not giveaways:
|
||||
await ctx.send(view=CV2("Ongoing Giveaways", "No ongoing giveaways."))
|
||||
return
|
||||
|
||||
desc = ""
|
||||
for giveaway in giveaways:
|
||||
prize, ends_at, winners, message_id = giveaway
|
||||
desc += f"**{prize}**\nEnds: <t:{int(ends_at)}:R> (<t:{int(ends_at)}:f>)\nWinners: {winners}\n[Jump to Message](https://discord.com/channels/{ctx.guild.id}/{ctx.channel.id}/{message_id})\n\n"
|
||||
|
||||
await ctx.send(view=CV2("Ongoing Giveaways", desc))
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Giveaway(bot))
|
||||
271
bot/cogs/commands/help.py
Normal file
271
bot/cogs/commands/help.py
Normal file
@@ -0,0 +1,271 @@
|
||||
import discord
|
||||
from utils.emoji import ARROWRED, BOOST, CAST, GAMES, LEVEL_UP, LOADINGRED, LOCK, MESSAGE, MINECRAFT, MUSIC, NEW, PIN, SEED, STAR, SWORD, SYSTEM, THUNDER, TICKET, WIFI, ZAI, ZARROW, ZBAN, ZBOT, ZCIRCLE, ZCIRCLE_ALT1, ZCLOUD, ZCOUNTING, ZMODULE, ZPEOPLE, ZROCKET, ZSAFE, ZTADA, ZUNMUTE, ZWRENCH
|
||||
from discord.ext import commands
|
||||
from discord import app_commands, Interaction
|
||||
from difflib import get_close_matches
|
||||
from contextlib import suppress
|
||||
from core import Context
|
||||
from core.zyrox import zyrox
|
||||
from core.Cog import Cog
|
||||
from utils.Tools import getConfig
|
||||
from itertools import chain
|
||||
import json
|
||||
from utils import help as vhelp
|
||||
from utils import Paginator, DescriptionEmbedPaginator, FieldPagePaginator, TextPaginator
|
||||
import asyncio
|
||||
from utils.config import serverLink
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, CV2Embed
|
||||
from utils.config import *
|
||||
|
||||
color = 0xFF0000
|
||||
client = zyrox()
|
||||
|
||||
from utils.config import BotName
|
||||
|
||||
class HelpCommand(commands.HelpCommand):
|
||||
|
||||
async def send_ignore_message(self, ctx, ignore_type: str):
|
||||
if ignore_type == "channel":
|
||||
await ctx.reply(f"This channel is ignored.", mention_author=False)
|
||||
elif ignore_type == "command":
|
||||
await ctx.reply(f"{ctx.author.mention} This Command, Channel, or You have been ignored here.", delete_after=6)
|
||||
elif ignore_type == "user":
|
||||
await ctx.reply(f"You are ignored.", mention_author=False)
|
||||
|
||||
async def on_help_command_error(self, ctx, error):
|
||||
errors = [
|
||||
commands.CommandOnCooldown, commands.CommandNotFound,
|
||||
discord.HTTPException, commands.CommandInvokeError
|
||||
]
|
||||
if not type(error) in errors:
|
||||
await self.context.reply(f"Unknown Error Occurred\n{error.original}",
|
||||
mention_author=False)
|
||||
else:
|
||||
if type(error) == commands.CommandOnCooldown:
|
||||
return
|
||||
return await super().on_help_command_error(ctx, error)
|
||||
|
||||
async def command_not_found(self, string: str) -> None:
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
cmds = (str(cmd) for cmd in self.context.bot.walk_commands())
|
||||
matches = get_close_matches(string, cmds)
|
||||
|
||||
embed = CV2Embed(
|
||||
title=f"{BotName} Helper",
|
||||
description=f">>> **Ops! Command not found with the name** `{string}`.",
|
||||
color=0xFF0000
|
||||
)
|
||||
|
||||
await ctx.reply(view=embed, mention_author=True)
|
||||
|
||||
async def send_bot_help(self, mapping):
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
# Show loading message
|
||||
loading_embed = CV2(f"{LOADINGRED} Loading help Menu...")
|
||||
loading_msg = await ctx.reply(view=loading_embed)
|
||||
|
||||
# Wait 2 seconds
|
||||
await asyncio.sleep(2)
|
||||
|
||||
# Delete loading message
|
||||
with suppress(discord.NotFound):
|
||||
await loading_msg.delete()
|
||||
|
||||
data = await getConfig(self.context.guild.id)
|
||||
prefix = data["prefix"]
|
||||
filtered = await self.filter_commands(self.context.bot.walk_commands(), sort=True)
|
||||
|
||||
embed = CV2Embed(
|
||||
description=(
|
||||
f"**{ARROWRED} __Start {BotName} Today__**\n"
|
||||
f"**{ZARROW} Type {prefix}antinuke enable**\n"
|
||||
f"**{ZARROW} Server Prefix:** `{prefix}`\n"
|
||||
f"**{ZARROW} Total Commands:** `{len(set(self.context.bot.walk_commands()))}`\n"),
|
||||
color=0xFF0000)
|
||||
|
||||
embed.add_field(
|
||||
name=f"{ZCLOUD} Main Features",
|
||||
value=f">>> \n {ZSAFE} `»` Security\n"
|
||||
f" {ZBOT} `»` Automoderation\n"
|
||||
f" {ZWRENCH} `»` Utility\n"
|
||||
f" {MUSIC} `»` Music\n"
|
||||
f" {WIFI} `»` Autoreact & responder\n"
|
||||
f" {SWORD} `»` Moderation\n"
|
||||
f" {ZPEOPLE} `»` Autorole & Invc\n"
|
||||
f" {ZROCKET} `»` Fun\n"
|
||||
f" {GAMES} `»` Games\n"
|
||||
f" {ZBAN} `»` Ignore Channels\n"
|
||||
f" {WIFI} `»` Server\n"
|
||||
f" {ZUNMUTE} `»` Voice\n"
|
||||
f" {SEED} `»` Welcomer\n"
|
||||
f" {ZTADA} `»` Giveaway\n"
|
||||
f" {TICKET} `»` Ticket {NEW}\n"
|
||||
f" {ZPEOPLE} `»` Invite Tracker {NEW}\n"
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name=f" {ZMODULE} Extra Features",
|
||||
value=f">>> \n {CAST} `»` Advance Logging\n"
|
||||
f" {STAR} `»` Vanityroles\n"
|
||||
f" {ZCOUNTING} `»` Counting {NEW}\n"
|
||||
f" {SYSTEM} `»` J2C {NEW}\n"
|
||||
f" {ZAI} `»` AI {NEW}\n"
|
||||
f" {BOOST} `»` Boost {NEW}\n"
|
||||
f" {LEVEL_UP} `»` Leveling {NEW}\n"
|
||||
f" {PIN} `»` Sticky {NEW}\n"
|
||||
f" {THUNDER} `»` Verification {NEW}\n"
|
||||
f" {LOCK} `»` Encryption {NEW}\n"
|
||||
f" {MINECRAFT} `»` Minecraft {NEW}\n"
|
||||
f" {MESSAGE} `»` Joindm {NEW}\n"
|
||||
f" {ZCIRCLE} `»` Birthday {NEW}\n"
|
||||
f" {ZCIRCLE_ALT1} `»` Customrole\n"
|
||||
)
|
||||
|
||||
embed.set_footer(
|
||||
text=f"Requested By {self.context.author} | [Support](https://discord.gg/codexdev)",
|
||||
)
|
||||
|
||||
view = vhelp.View(mapping=mapping, ctx=self.context, homeembed=embed, ui=2)
|
||||
await ctx.reply(view=view)
|
||||
|
||||
async def send_command_help(self, command):
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
zyrox = f">>> {command.help}" if command.help else '>>> No Help Provided...'
|
||||
embed = CV2Embed(
|
||||
description=f"""{zyrox}""",
|
||||
color=color)
|
||||
alias = ' & '.join(command.aliases)
|
||||
|
||||
embed.add_field(name="**Alt cmd**",
|
||||
value=f"```{alias}```" if command.aliases else "No Alt cmd",
|
||||
inline=False)
|
||||
embed.add_field(name="**Usage**",
|
||||
value=f"```{self.context.prefix}{command.signature}```\n")
|
||||
embed.set_author(name=f"{command.qualified_name.title()} Command")
|
||||
embed.set_footer(text="<[] = optional | < > = required • Use Prefix Before Commands.")
|
||||
await self.context.reply(view=embed, mention_author=False)
|
||||
|
||||
def get_command_signature(self, command: commands.Command) -> str:
|
||||
parent = command.full_parent_name
|
||||
if len(command.aliases) > 0:
|
||||
aliases = ' | '.join(command.aliases)
|
||||
fmt = f'[{command.name} | {aliases}]'
|
||||
if parent:
|
||||
fmt = f'{parent}'
|
||||
alias = f'[{command.name} | {aliases}]'
|
||||
else:
|
||||
alias = command.name if not parent else f'{parent} {command.name}'
|
||||
return f'{alias} {command.signature}'
|
||||
|
||||
def common_command_formatting(self, embed_like, command):
|
||||
embed_like.title = self.get_command_signature(command)
|
||||
if command.description:
|
||||
embed_like.description = f'{command.description}\n\n{command.help}'
|
||||
else:
|
||||
embed_like.description = command.help or 'No help found...'
|
||||
|
||||
async def send_group_help(self, group):
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
entries = [
|
||||
(
|
||||
f"`{self.context.prefix}{cmd.qualified_name}`\n",
|
||||
f"{cmd.short_doc if cmd.short_doc else ''}\n\u200b"
|
||||
)
|
||||
for cmd in group.commands
|
||||
]
|
||||
|
||||
count = len(group.commands)
|
||||
|
||||
embeds = FieldPagePaginator(
|
||||
entries=entries,
|
||||
title=f"{group.qualified_name.title()} [{count}]",
|
||||
description="< > Duty | [ ] Optional\n",
|
||||
per_page=4
|
||||
).get_pages()
|
||||
|
||||
paginator = Paginator(ctx, embeds)
|
||||
await paginator.paginate()
|
||||
|
||||
async def send_cog_help(self, cog):
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
entries = [(
|
||||
f"> `{self.context.prefix}{cmd.qualified_name}`",
|
||||
f"-# Description : {cmd.short_doc if cmd.short_doc else ''}"
|
||||
f"\n\u200b",
|
||||
) for cmd in cog.get_commands()]
|
||||
paginator = Paginator(source=FieldPagePaginator(
|
||||
entries=entries,
|
||||
title=f"{BRAND_NAME}'s {cog.qualified_name.title()} ({len(cog.get_commands())})",
|
||||
description="`<..> Required | [..] Optional`\n\n",
|
||||
color=0xFF0000,
|
||||
per_page=4),
|
||||
ctx=self.context)
|
||||
await paginator.paginate()
|
||||
|
||||
|
||||
class Help(Cog, name="help"):
|
||||
|
||||
def __init__(self, client: zyrox):
|
||||
self._original_help_command = client.help_command
|
||||
attributes = {
|
||||
'name': "help",
|
||||
'aliases': ['h'],
|
||||
'cooldown': commands.CooldownMapping.from_cooldown(1, 5, commands.BucketType.user),
|
||||
'help': 'Shows help about bot, a command, or a category'
|
||||
}
|
||||
client.help_command = HelpCommand(command_attrs=attributes)
|
||||
client.help_command.cog = self
|
||||
|
||||
async def cog_unload(self):
|
||||
self.help_command = self._original_help_command
|
||||
278
bot/cogs/commands/help_backup.txt
Normal file
278
bot/cogs/commands/help_backup.txt
Normal file
@@ -0,0 +1,278 @@
|
||||
import discord
|
||||
from utils.emoji import ARROWRED, BOOST, CAST, GAMES, LEVEL_UP, LOADINGRED, LOCK, MESSAGE, MINECRAFT, MUSIC, NEW, PIN, SEED, STAR, SWORD, SYSTEM, THUNDER, TICKET, WIFI, ZAI, ZARROW, ZBAN, ZBOT, ZCIRCLE, ZCIRCLE_ALT1, ZCLOUD, ZCOUNTING, ZMODULE, ZPEOPLE, ZROCKET, ZSAFE, ZTADA, ZUNMUTE, ZWRENCH
|
||||
from discord.ext import commands
|
||||
from discord import app_commands, Interaction
|
||||
from difflib import get_close_matches
|
||||
from contextlib import suppress
|
||||
from core import Context
|
||||
from core.zyrox import zyrox
|
||||
from core.Cog import Cog
|
||||
from utils.Tools import getConfig
|
||||
from itertools import chain
|
||||
import json
|
||||
from utils import help as vhelp
|
||||
from utils import Paginator, DescriptionEmbedPaginator, FieldPagePaginator, TextPaginator
|
||||
import asyncio
|
||||
from utils.config import serverLink
|
||||
from utils.Tools import *
|
||||
|
||||
color = 0xFF0000
|
||||
client = zyrox()
|
||||
|
||||
class HelpCommand(commands.HelpCommand):
|
||||
|
||||
async def send_ignore_message(self, ctx, ignore_type: str):
|
||||
if ignore_type == "channel":
|
||||
await ctx.reply(f"This channel is ignored.", mention_author=False)
|
||||
elif ignore_type == "command":
|
||||
await ctx.reply(f"{ctx.author.mention} This Command, Channel, or You have been ignored here.", delete_after=6)
|
||||
elif ignore_type == "user":
|
||||
await ctx.reply(f"You are ignored.", mention_author=False)
|
||||
|
||||
async def on_help_command_error(self, ctx, error):
|
||||
errors = [
|
||||
commands.CommandOnCooldown, commands.CommandNotFound,
|
||||
discord.HTTPException, commands.CommandInvokeError
|
||||
]
|
||||
if not type(error) in errors:
|
||||
await self.context.reply(f"Unknown Error Occurred\n{error.original}",
|
||||
mention_author=False)
|
||||
else:
|
||||
if type(error) == commands.CommandOnCooldown:
|
||||
return
|
||||
return await super().on_help_command_error(ctx, error)
|
||||
|
||||
async def command_not_found(self, string: str) -> None:
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
cmds = (str(cmd) for cmd in self.context.bot.walk_commands())
|
||||
matches = get_close_matches(string, cmds)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Zyrox Helper",
|
||||
description=f">>> **Ops! Command not found with the name** `{string}`.",
|
||||
color=0xFF0000
|
||||
)
|
||||
|
||||
#if matches:
|
||||
#match_list = "\n".join([f"{index}. `{match}`" for index, match in enumerate(matches, start=1)])
|
||||
#embed.add_field(name="Did you mean:", value=match_list, inline=True)
|
||||
|
||||
await ctx.reply(embed=embed, mention_author=True)
|
||||
|
||||
async def send_bot_help(self, mapping):
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
# Show loading embed
|
||||
loading_embed = discord.Embed(
|
||||
description=f"{LOADINGRED} Loading help Menu...",
|
||||
color=0xFF0000
|
||||
)
|
||||
loading_msg = await ctx.reply(embed=loading_embed)
|
||||
|
||||
# Wait 2 seconds
|
||||
await asyncio.sleep(2)
|
||||
|
||||
# Delete loading message
|
||||
with suppress(discord.NotFound):
|
||||
await loading_msg.delete()
|
||||
|
||||
data = await getConfig(self.context.guild.id)
|
||||
prefix = data["prefix"]
|
||||
filtered = await self.filter_commands(self.context.bot.walk_commands(), sort=True)
|
||||
|
||||
embed = discord.Embed(
|
||||
description=(
|
||||
f"**{ARROWRED} __Start Zyrox X Today__**\n"
|
||||
f"**{ZARROW} Type {prefix}antinuke enable**\n"
|
||||
f"**{ZARROW} Server Prefix:** `{prefix}`\n"
|
||||
f"**{ZARROW} Total Commands:** `{len(set(self.context.bot.walk_commands()))}`\n"),
|
||||
color=0xFF0000)
|
||||
embed.set_author(name=f"{ctx.author}",
|
||||
icon_url=ctx.author.display_avatar.url)
|
||||
embed.set_thumbnail(url=ctx.author.display_avatar.url)
|
||||
|
||||
embed.add_field(
|
||||
name=f"{ZCLOUD} __**Main Features**__",
|
||||
value=f">>> \n {ZSAFE} `»` Security\n"
|
||||
f" {ZBOT} `»` Automoderation\n"
|
||||
f" {ZWRENCH} `»` Utility\n"
|
||||
f" {MUSIC} `»` Music\n"
|
||||
f" {WIFI} `»` Autoreact & responder\n"
|
||||
f" {SWORD} `»` Moderation\n"
|
||||
f" {ZPEOPLE} `»` Autorole & Invc\n"
|
||||
f" {ZROCKET} `»` Fun\n"
|
||||
f" {GAMES} `»` Games\n"
|
||||
f" {ZBAN} `»` Ignore Channels\n"
|
||||
f" {WIFI} `»` Server\n"
|
||||
f" {ZUNMUTE} `»` Voice\n"
|
||||
f" {SEED} `»` Welcomer\n"
|
||||
f" {ZTADA} `»` Giveaway\n"
|
||||
f" {TICKET} `»` Ticket {NEW}\n"
|
||||
f" {ZPEOPLE} `»` Invite Tracker {NEW}\n"
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name=f" {ZMODULE} __**Extra Features**__",
|
||||
value=f">>> \n {CAST} `»` Advance Logging\n"
|
||||
f" {STAR} `»` Vanityroles\n"
|
||||
|
||||
f" {ZCOUNTING} `»` Counting {NEW}\n"
|
||||
f" {SYSTEM} `»` J2C {NEW}\n"
|
||||
f" {ZAI} `»` AI {NEW}\n"
|
||||
f" {BOOST} `»` Boost {NEW}\n"
|
||||
f" {LEVEL_UP} `»` Leveling {NEW}\n"
|
||||
f" {PIN} `»` Sticky {NEW}\n"
|
||||
f" {THUNDER} `»` Verification {NEW}\n"
|
||||
f" {LOCK} `»` Encryption {NEW}\n"
|
||||
f" {MINECRAFT} `»` Minecraft {NEW}\n"
|
||||
f" {MESSAGE} `»` Joindm {NEW}\n"
|
||||
f" {ZCIRCLE} `»` Birthday {NEW}\n"
|
||||
f" {ZCIRCLE_ALT1} `»` Customrole\n"
|
||||
)
|
||||
|
||||
embed.set_footer(
|
||||
text=f"Requested By {self.context.author} | [Support](https://discord.gg/codexdev)",
|
||||
)
|
||||
|
||||
view = vhelp.View(mapping=mapping, ctx=self.context, homeembed=embed, ui=2)
|
||||
await ctx.reply(embed=embed, view=view)
|
||||
|
||||
async def send_command_help(self, command):
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
zyrox = f">>> {command.help}" if command.help else '>>> No Help Provided...'
|
||||
embed = discord.Embed(
|
||||
description=f"""{zyrox}""",
|
||||
color=color)
|
||||
alias = ' & '.join(command.aliases)
|
||||
|
||||
embed.add_field(name="**Alt cmd**",
|
||||
value=f"```{alias}```" if command.aliases else "No Alt cmd",
|
||||
inline=False)
|
||||
embed.add_field(name="**Usage**",
|
||||
value=f"```{self.context.prefix}{command.signature}```\n")
|
||||
embed.set_author(name=f"{command.qualified_name.title()} Command")
|
||||
embed.set_footer(text="<[] = optional | < > = required • Use Prefix Before Commands.")
|
||||
await self.context.reply(embed=embed, mention_author=False)
|
||||
|
||||
def get_command_signature(self, command: commands.Command) -> str:
|
||||
parent = command.full_parent_name
|
||||
if len(command.aliases) > 0:
|
||||
aliases = ' | '.join(command.aliases)
|
||||
fmt = f'[{command.name} | {aliases}]'
|
||||
if parent:
|
||||
fmt = f'{parent}'
|
||||
alias = f'[{command.name} | {aliases}]'
|
||||
else:
|
||||
alias = command.name if not parent else f'{parent} {command.name}'
|
||||
return f'{alias} {command.signature}'
|
||||
|
||||
def common_command_formatting(self, embed_like, command):
|
||||
embed_like.title = self.get_command_signature(command)
|
||||
if command.description:
|
||||
embed_like.description = f'{command.description}\n\n{command.help}'
|
||||
else:
|
||||
embed_like.description = command.help or 'No help found...'
|
||||
|
||||
async def send_group_help(self, group):
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
entries = [
|
||||
(
|
||||
f"`{self.context.prefix}{cmd.qualified_name}`\n",
|
||||
f"{cmd.short_doc if cmd.short_doc else ''}\n\u200b"
|
||||
)
|
||||
for cmd in group.commands
|
||||
]
|
||||
|
||||
count = len(group.commands)
|
||||
|
||||
embeds = FieldPagePaginator(
|
||||
entries=entries,
|
||||
title=f"{group.qualified_name.title()} [{count}]",
|
||||
description="< > Duty | [ ] Optional\n",
|
||||
per_page=4
|
||||
).get_pages()
|
||||
|
||||
paginator = Paginator(ctx, embeds)
|
||||
await paginator.paginate()
|
||||
|
||||
async def send_cog_help(self, cog):
|
||||
ctx = self.context
|
||||
check_ignore = await ignore_check().predicate(ctx)
|
||||
check_blacklist = await blacklist_check().predicate(ctx)
|
||||
|
||||
if not check_blacklist:
|
||||
return
|
||||
|
||||
if not check_ignore:
|
||||
await self.send_ignore_message(ctx, "command")
|
||||
return
|
||||
|
||||
entries = [(
|
||||
f"> `{self.context.prefix}{cmd.qualified_name}`",
|
||||
f"-# Description : {cmd.short_doc if cmd.short_doc else ''}"
|
||||
f"\n\u200b",
|
||||
) for cmd in cog.get_commands()]
|
||||
paginator = Paginator(source=FieldPagePaginator(
|
||||
entries=entries,
|
||||
title=f"Zyrox's {cog.qualified_name.title()} ({len(cog.get_commands())})",
|
||||
description="`<..> Required | [..] Optional`\n\n",
|
||||
color=0xFF0000,
|
||||
per_page=4),
|
||||
ctx=self.context)
|
||||
await paginator.paginate()
|
||||
|
||||
|
||||
class Help(Cog, name="help"):
|
||||
|
||||
def __init__(self, client: zyrox):
|
||||
self._original_help_command = client.help_command
|
||||
attributes = {
|
||||
'name': "help",
|
||||
'aliases': ['h'],
|
||||
'cooldown': commands.CooldownMapping.from_cooldown(1, 5, commands.BucketType.user),
|
||||
'help': 'Shows help about bot, a command, or a category'
|
||||
}
|
||||
client.help_command = HelpCommand(command_attrs=attributes)
|
||||
client.help_command.cog = self
|
||||
|
||||
async def cog_unload(self):
|
||||
self.help_command = self._original_help_command
|
||||
595
bot/cogs/commands/ignore.py
Normal file
595
bot/cogs/commands/ignore.py
Normal file
@@ -0,0 +1,595 @@
|
||||
from __future__ import annotations
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK, ZWARNING
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from discord.ext import commands
|
||||
from core import *
|
||||
from utils.Tools import *
|
||||
from typing import Optional
|
||||
import aiosqlite
|
||||
|
||||
color = 0xFF0000
|
||||
|
||||
|
||||
class SuccessView(LayoutView):
|
||||
def __init__(self, title, description):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
Container(
|
||||
TextDisplay(f"**{TICK} {title}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(description),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ErrorView(LayoutView):
|
||||
def __init__(self, title, description):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
Container(
|
||||
TextDisplay(f"**{CROSS} {title}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(description),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class WarningView(LayoutView):
|
||||
def __init__(self, title, description):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
Container(
|
||||
TextDisplay(f"**{ZWARNING} {title}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(description),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ListView(LayoutView):
|
||||
def __init__(self, title, items, empty_message, guild=None):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
if not items:
|
||||
self.add_item(
|
||||
Container(
|
||||
TextDisplay(f"**{title}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(empty_message),
|
||||
)
|
||||
)
|
||||
else:
|
||||
if guild:
|
||||
mentions = []
|
||||
for item in items:
|
||||
if isinstance(item, int):
|
||||
entity = guild.get_channel(item) or guild.get_member(item)
|
||||
mentions.append(entity.mention if entity else f"ID {item}")
|
||||
else:
|
||||
mentions.append(f"`{item}`")
|
||||
description = "\n".join(mentions)
|
||||
else:
|
||||
description = "\n".join([f"`{item}`" for item in items])
|
||||
|
||||
self.add_item(
|
||||
Container(
|
||||
TextDisplay(f"**{title}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(description),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Ignore(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.db_path = "db/ignore.db"
|
||||
self.color = 0xFF0000
|
||||
bot.loop.create_task(self.initialize_db())
|
||||
|
||||
async def initialize_db(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS ignored_commands (guild_id INTEGER, command_name TEXT)"
|
||||
)
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS ignored_channels (guild_id INTEGER, channel_id INTEGER)"
|
||||
)
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS ignored_users (guild_id INTEGER, user_id INTEGER)"
|
||||
)
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS bypassed_users (guild_id INTEGER, user_id INTEGER)"
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
@commands.group(
|
||||
name="ignore",
|
||||
help="Manage ignored commands, channels, users, and bypassed users.",
|
||||
invoke_without_command=True,
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _ignore(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_ignore.group(
|
||||
name="command",
|
||||
help="Manage ignored commands in this guild.",
|
||||
invoke_without_command=True,
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _command(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_command.command(name="add", help="Adds a command to the ignore list.")
|
||||
@commands.has_permissions(administrator=True)
|
||||
@blacklist_check()
|
||||
async def command_add(self, ctx: commands.Context, command_name: str):
|
||||
command_name_normalized = command_name.strip().lower()
|
||||
command = self.bot.get_command(command_name_normalized)
|
||||
if not command:
|
||||
await ctx.reply(
|
||||
view=ErrorView("Error", f"`{command_name}` is not a valid command.")
|
||||
)
|
||||
return
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT COUNT(*) FROM ignored_commands WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
count = await cursor.fetchone()
|
||||
if count[0] >= 25:
|
||||
await ctx.reply(
|
||||
view=WarningView(
|
||||
"Access Denied",
|
||||
"You can only add up to 25 commands to the ignore list.",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
cursor = await db.execute(
|
||||
"SELECT command_name FROM ignored_commands WHERE guild_id = ? AND command_name = ?",
|
||||
(ctx.guild.id, command_name_normalized),
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
if result:
|
||||
await ctx.reply(
|
||||
view=ErrorView(
|
||||
"Error",
|
||||
f"`{command_name}` is already in the ignore commands list.",
|
||||
)
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"INSERT INTO ignored_commands (guild_id, command_name) VALUES (?, ?)",
|
||||
(ctx.guild.id, command_name_normalized),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(
|
||||
view=SuccessView(
|
||||
"Success",
|
||||
f"Successfully added `{command_name}` to the ignore commands list.",
|
||||
)
|
||||
)
|
||||
|
||||
@_command.command(name="remove", help="Removes a command from the ignore list.")
|
||||
@commands.has_permissions(administrator=True)
|
||||
@blacklist_check()
|
||||
async def command_remove(self, ctx: commands.Context, command_name: str):
|
||||
command_name_normalized = command_name.strip().lower()
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT command_name FROM ignored_commands WHERE guild_id = ? AND command_name = ?",
|
||||
(ctx.guild.id, command_name_normalized),
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
if not result:
|
||||
await ctx.reply(
|
||||
view=ErrorView(
|
||||
"Error", f"`{command_name}` is not in the ignore commands list."
|
||||
)
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"DELETE FROM ignored_commands WHERE guild_id = ? AND command_name = ?",
|
||||
(ctx.guild.id, command_name_normalized),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(
|
||||
view=SuccessView(
|
||||
"Success",
|
||||
f"Successfully removed `{command_name}` from the ignore commands list.",
|
||||
)
|
||||
)
|
||||
|
||||
@_command.command(name="show", help="Displays the list of ignored commands.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def command_show(self, ctx: commands.Context):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT command_name FROM ignored_commands WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
commands = await cursor.fetchall()
|
||||
if not commands:
|
||||
await ctx.reply(
|
||||
view=ListView(
|
||||
"Ignored Commands",
|
||||
[],
|
||||
"No commands are currently ignored in this server.",
|
||||
)
|
||||
)
|
||||
else:
|
||||
await ctx.reply(
|
||||
view=ListView("Ignored Commands", [c[0] for c in commands], "")
|
||||
)
|
||||
|
||||
@_ignore.group(
|
||||
name="channel",
|
||||
help="Manage ignored channels in this guild.",
|
||||
invoke_without_command=True,
|
||||
)
|
||||
@blacklist_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _channel(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_channel.command(name="add", help="Adds a channel to the ignore list.")
|
||||
@blacklist_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def channel_add(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT COUNT(*) FROM ignored_channels WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
count = await cursor.fetchone()
|
||||
|
||||
if count[0] >= 30:
|
||||
await ctx.reply(
|
||||
view=WarningView(
|
||||
"Access Denied",
|
||||
"You can only add up to 30 channels to the ignore list.",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
cursor = await db.execute(
|
||||
"SELECT channel_id FROM ignored_channels WHERE guild_id = ? AND channel_id = ?",
|
||||
(ctx.guild.id, channel.id),
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
|
||||
if result:
|
||||
await ctx.reply(
|
||||
view=ErrorView(
|
||||
"Error",
|
||||
f"{channel.mention} is already in the ignore channels list.",
|
||||
)
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"INSERT INTO ignored_channels (guild_id, channel_id) VALUES (?, ?)",
|
||||
(ctx.guild.id, channel.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(
|
||||
view=SuccessView(
|
||||
"Success",
|
||||
f"Successfully added {channel.mention} to the ignore channels list.",
|
||||
)
|
||||
)
|
||||
|
||||
@_channel.command(name="remove", help="Removes a channel from the ignore list.")
|
||||
@blacklist_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def channel_remove(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT channel_id FROM ignored_channels WHERE guild_id = ? AND channel_id = ?",
|
||||
(ctx.guild.id, channel.id),
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
|
||||
if not result:
|
||||
await ctx.reply(
|
||||
view=ErrorView(
|
||||
"Error",
|
||||
f"{channel.mention} is not in the ignore channels list.",
|
||||
)
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"DELETE FROM ignored_channels WHERE guild_id = ? AND channel_id = ?",
|
||||
(ctx.guild.id, channel.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(
|
||||
view=SuccessView(
|
||||
"Success",
|
||||
f"Successfully removed {channel.mention} from the ignore channels list.",
|
||||
)
|
||||
)
|
||||
|
||||
@_channel.command(name="show", help="Displays the list of ignored channels.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def channel_show(self, ctx: commands.Context):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT channel_id FROM ignored_channels WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
channels = await cursor.fetchall()
|
||||
|
||||
if not channels:
|
||||
await ctx.reply(
|
||||
view=ListView(
|
||||
"Ignored Channels",
|
||||
[],
|
||||
"No channels are currently ignored in this server.",
|
||||
)
|
||||
)
|
||||
else:
|
||||
await ctx.reply(
|
||||
view=ListView(
|
||||
"Ignored Channels", [c[0] for c in channels], "", ctx.guild
|
||||
)
|
||||
)
|
||||
|
||||
@_ignore.group(
|
||||
name="user",
|
||||
help="Manage ignored users in this guild.",
|
||||
invoke_without_command=True,
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _user(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_user.command(name="add", help="Adds a user to the ignore list.")
|
||||
@commands.has_permissions(administrator=True)
|
||||
@blacklist_check()
|
||||
async def user_add(self, ctx: commands.Context, user: discord.User):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT COUNT(*) FROM ignored_users WHERE guild_id = ?", (ctx.guild.id,)
|
||||
)
|
||||
count = await cursor.fetchone()
|
||||
|
||||
if count[0] >= 30:
|
||||
await ctx.reply(
|
||||
view=WarningView(
|
||||
"Access Denied",
|
||||
"You can only add up to 30 users to the ignore list.",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
cursor = await db.execute(
|
||||
"SELECT user_id FROM ignored_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, user.id),
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
|
||||
if result:
|
||||
await ctx.reply(
|
||||
view=ErrorView(
|
||||
"Error", f"{user.mention} is already in the ignore users list."
|
||||
)
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"INSERT INTO ignored_users (guild_id, user_id) VALUES (?, ?)",
|
||||
(ctx.guild.id, user.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(
|
||||
view=SuccessView(
|
||||
"Success",
|
||||
f"Successfully added {user.mention} to the ignore users list.",
|
||||
)
|
||||
)
|
||||
|
||||
@_user.command(name="remove", help="Removes a user from the ignore list.")
|
||||
@blacklist_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def user_remove(self, ctx: commands.Context, user: discord.User):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT user_id FROM ignored_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, user.id),
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
|
||||
if not result:
|
||||
await ctx.reply(
|
||||
view=ErrorView(
|
||||
"Error", f"{user.mention} is not in the ignore users list."
|
||||
)
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"DELETE FROM ignored_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, user.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.send(
|
||||
view=SuccessView(
|
||||
"Success",
|
||||
f"Successfully removed {user.mention} from the ignore users list.",
|
||||
)
|
||||
)
|
||||
|
||||
@_user.command(name="show", help="Displays the list of ignored users.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def user_show(self, ctx: commands.Context):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT user_id FROM ignored_users WHERE guild_id = ?", (ctx.guild.id,)
|
||||
)
|
||||
users = await cursor.fetchall()
|
||||
|
||||
if not users:
|
||||
await ctx.reply(
|
||||
view=ListView(
|
||||
"Ignored Users",
|
||||
[],
|
||||
"No users are currently ignored in this server.",
|
||||
)
|
||||
)
|
||||
else:
|
||||
await ctx.reply(
|
||||
view=ListView("Ignored Users", [u[0] for u in users], "", ctx.guild)
|
||||
)
|
||||
|
||||
@_ignore.group(
|
||||
name="bypass",
|
||||
help="Manage bypassed users in this guild.",
|
||||
invoke_without_command=True,
|
||||
)
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def _bypass(self, ctx):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@_bypass.command(name="add", help="Adds a user to the bypass list.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_add(self, ctx: commands.Context, user: discord.User):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT COUNT(*) FROM bypassed_users WHERE guild_id = ?",
|
||||
(ctx.guild.id,),
|
||||
)
|
||||
count = await cursor.fetchone()
|
||||
|
||||
if count[0] >= 30:
|
||||
await ctx.reply(
|
||||
view=WarningView(
|
||||
"Access Denied",
|
||||
"You can only add up to 30 users to the bypass list.",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
cursor = await db.execute(
|
||||
"SELECT user_id FROM bypassed_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, user.id),
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
|
||||
if result:
|
||||
await ctx.reply(
|
||||
view=ErrorView(
|
||||
"Error", f"{user.mention} is already in the bypass users list."
|
||||
)
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"INSERT INTO bypassed_users (guild_id, user_id) VALUES (?, ?)",
|
||||
(ctx.guild.id, user.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(
|
||||
view=SuccessView(
|
||||
"Success",
|
||||
f"Successfully added {user.mention} to the bypass users list.",
|
||||
)
|
||||
)
|
||||
|
||||
@_bypass.command(name="remove", help="Removes a user from the bypass list.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_remove(self, ctx: commands.Context, user: discord.User):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT user_id FROM bypassed_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, user.id),
|
||||
)
|
||||
result = await cursor.fetchone()
|
||||
|
||||
if not result:
|
||||
await ctx.reply(
|
||||
view=ErrorView(
|
||||
"Error", f"{user.mention} is not in the bypass users list."
|
||||
)
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
"DELETE FROM bypassed_users WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, user.id),
|
||||
)
|
||||
await db.commit()
|
||||
await ctx.reply(
|
||||
view=SuccessView(
|
||||
"Success",
|
||||
f"Successfully removed {user.mention} from the bypass users list.",
|
||||
)
|
||||
)
|
||||
|
||||
@_bypass.command(
|
||||
name="show", aliases=["list"], help="Displays the list of bypassed users."
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def bypass_show(self, ctx: commands.Context):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"SELECT user_id FROM bypassed_users WHERE guild_id = ?", (ctx.guild.id,)
|
||||
)
|
||||
users = await cursor.fetchall()
|
||||
|
||||
if not users:
|
||||
await ctx.reply(
|
||||
view=ListView(
|
||||
"Bypassed Users",
|
||||
[],
|
||||
"No users are currently bypassed in this server.",
|
||||
)
|
||||
)
|
||||
else:
|
||||
await ctx.reply(
|
||||
view=ListView(
|
||||
"Bypassed Users", [u[0] for u in users], "", ctx.guild
|
||||
)
|
||||
)
|
||||
64
bot/cogs/commands/image.py
Normal file
64
bot/cogs/commands/image.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ui import LayoutView, MediaGallery, TextDisplay
|
||||
import aiohttp
|
||||
import os
|
||||
import random
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
PEXELS_API_KEY = "js24mfV1bCCvgV6KfnEFvo5UnCHnATFarFnAdDrpDbczl7f0yXpjDF8x"
|
||||
|
||||
class ImageCommands(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
async def fetch_pexels_image(self, query):
|
||||
headers = {
|
||||
"Authorization": PEXELS_API_KEY
|
||||
}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api.pexels.com/v1/search?query={query}&per_page=50", headers=headers) as resp:
|
||||
data = await resp.json()
|
||||
if data.get("photos"):
|
||||
image = random.choice(data["photos"])
|
||||
return image["src"]["original"]
|
||||
return None
|
||||
|
||||
async def fetch_waifu_image(self, category="waifu"):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api.waifu.pics/sfw/{category}") as resp:
|
||||
data = await resp.json()
|
||||
return data["url"]
|
||||
|
||||
async def send_image_view(self, ctx, title, url):
|
||||
if url:
|
||||
view = LayoutView(timeout=None)
|
||||
gallery = MediaGallery()
|
||||
gallery.add_item(media=url)
|
||||
view.add_item(build_container(TextDisplay(f"**{title}**"), gallery))
|
||||
await ctx.send(view=view)
|
||||
else:
|
||||
await ctx.send(view=CV2("❌ Error", f"No image found for {title.lower()}."))
|
||||
|
||||
@commands.command(name="boy")
|
||||
async def boy_image(self, ctx):
|
||||
url = await self.fetch_pexels_image("handsome boy")
|
||||
await self.send_image_view(ctx, "👦 Boy Pic", url)
|
||||
|
||||
# @commands.command(name="girl")
|
||||
# async def girl_image(self, ctx):
|
||||
# url = await self.fetch_pexels_image("beautiful girl")
|
||||
# await self.send_image_view(ctx, "👧 Girl Pic", url)
|
||||
|
||||
@commands.command(name="couple")
|
||||
async def couple_image(self, ctx):
|
||||
url = await self.fetch_pexels_image("romantic couple")
|
||||
await self.send_image_view(ctx, "💑 Couple Pic", url)
|
||||
|
||||
@commands.command(name="anime")
|
||||
async def anime_image(self, ctx):
|
||||
url = await self.fetch_waifu_image("waifu")
|
||||
await self.send_image_view(ctx, "🧚 Anime Waifu", url)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(ImageCommands(bot))
|
||||
154
bot/cogs/commands/imagine.py
Normal file
154
bot/cogs/commands/imagine.py
Normal file
@@ -0,0 +1,154 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord import app_commands
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import random
|
||||
import time
|
||||
from utils.ai_utils import poly_image_gen, generate_image_prodia
|
||||
from prodia.constants import Model
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2
|
||||
|
||||
blacklisted_words = [
|
||||
"naked", "nude", "nudes", "teen", "gay", "lesbian", "porn", "xnxx",
|
||||
"bitch", "loli", "hentai", "explicit", "pornography", "adult", "XXX",
|
||||
"sex", "erotic", "dick", "vagina", "pussy", "gay", "lick", "creampie", "nsfw",
|
||||
"hardcore", "ass", "anal", "anus", "boobs", "tits", "cum", "cunnilingus", "squirt", "penis", "lick", "masturbate", "masturbation ", "orgasm", "orgy", "fap", "fapping", "fuck", "fucking", "handjob", "cowgirl", "doggystyle", "blowjob", "boobjob", "boobies", "horny", "nudity"
|
||||
]
|
||||
|
||||
blocked=["minor", "minors", "kid", "kids", "child", "children", "baby", "babies", "toddler", "childporn", "todd", "underage"]
|
||||
|
||||
class CooldownManager:
|
||||
def __init__(self, rate: int, per: float):
|
||||
self.rate = rate
|
||||
self.per = per
|
||||
self.cooldowns = {}
|
||||
|
||||
def check_cooldown(self, user_id: int):
|
||||
now = time.time()
|
||||
if user_id not in self.cooldowns:
|
||||
self.cooldowns[user_id] = [now]
|
||||
return None
|
||||
|
||||
self.cooldowns[user_id] = [timestamp for timestamp in self.cooldowns[user_id] if now - timestamp < self.per]
|
||||
if len(self.cooldowns[user_id]) >= self.rate:
|
||||
retry_after = self.per - (now - self.cooldowns[user_id][0])
|
||||
return retry_after
|
||||
self.cooldowns[user_id].append(now)
|
||||
return None
|
||||
|
||||
cooldown_manager = CooldownManager(rate=1, per=60.0)
|
||||
|
||||
async def cooldown_check(interaction: discord.Interaction):
|
||||
retry_after = cooldown_manager.check_cooldown(interaction.user.id)
|
||||
if retry_after:
|
||||
await interaction.response.send_message(f"You are on cooldown. Try again in {retry_after:.2f} seconds.", ephemeral=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class AiStuffCog(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.guild_only()
|
||||
@app_commands.command(name="imagine", description="Generate an image using AI")
|
||||
@discord.app_commands.choices(
|
||||
model=[
|
||||
discord.app_commands.Choice(name='✨ Elldreth vivid mix (Landscapes, Stylized characters, nsfw)', value='ELLDRETHVIVIDMIX'),
|
||||
discord.app_commands.Choice(name='💪 Deliberate v2 (Anything you want, nsfw)', value='DELIBERATE'),
|
||||
discord.app_commands.Choice(name='🔮 Dreamshaper (HOLYSHIT this so good)', value='DREAMSHAPER_6'),
|
||||
discord.app_commands.Choice(name='🎼 Lyriel', value='LYRIEL_V16'),
|
||||
discord.app_commands.Choice(name='💥 Anything diffusion (Good for anime)', value='ANYTHING_V4'),
|
||||
discord.app_commands.Choice(name='🌅 Openjourney (Midjourney alternative)', value='OPENJOURNEY'),
|
||||
discord.app_commands.Choice(name='🏞️ Realistic (Lifelike pictures)', value='REALISTICVS_V20'),
|
||||
discord.app_commands.Choice(name='👨🎨 Portrait (For headshots I guess)', value='PORTRAIT'),
|
||||
discord.app_commands.Choice(name='🌟 Rev animated (Illustration, Anime)', value='REV_ANIMATED'),
|
||||
discord.app_commands.Choice(name='🤖 Analog', value='ANALOG'),
|
||||
discord.app_commands.Choice(name='🌌 AbyssOrangeMix', value='ABYSSORANGEMIX'),
|
||||
discord.app_commands.Choice(name='🌌 Dreamlike v1', value='DREAMLIKE_V1'),
|
||||
discord.app_commands.Choice(name='🌌 Dreamlike v2', value='DREAMLIKE_V2'),
|
||||
discord.app_commands.Choice(name='🌌 Dreamshaper 5', value='DREAMSHAPER_5'),
|
||||
discord.app_commands.Choice(name='🌌 MechaMix', value='MECHAMIX'),
|
||||
discord.app_commands.Choice(name='🌌 MeinaMix', value='MEINAMIX'),
|
||||
discord.app_commands.Choice(name='🌌 Stable Diffusion v14', value='SD_V14'),
|
||||
discord.app_commands.Choice(name='🌌 Stable Diffusion v15', value='SD_V15'),
|
||||
discord.app_commands.Choice(name="🌌 Shonin's Beautiful People", value='SBP'),
|
||||
discord.app_commands.Choice(name="🌌 TheAlly's Mix II", value='THEALLYSMIX'),
|
||||
discord.app_commands.Choice(name='🌌 Timeless', value='TIMELESS')
|
||||
],
|
||||
sampler=[
|
||||
discord.app_commands.Choice(name='📏 Euler (Recommended)', value='Euler'),
|
||||
discord.app_commands.Choice(name='📏 Euler a', value='Euler a'),
|
||||
discord.app_commands.Choice(name='📐 Heun', value='Heun'),
|
||||
discord.app_commands.Choice(name='💥 DPM++ 2M Karras', value='DPM++ 2M Karras'),
|
||||
discord.app_commands.Choice(name='💥 DPM++ SDE Karras', value='DPM++ SDE Karras'),
|
||||
discord.app_commands.Choice(name='🔍 DDIM', value='DDIM')
|
||||
]
|
||||
)
|
||||
@discord.app_commands.describe(
|
||||
prompt="Write an amazing prompt for an image",
|
||||
model="Model to generate image",
|
||||
sampler="Sampler for denoising",
|
||||
negative="Prompt that specifies what you do not want the model to generate",
|
||||
)
|
||||
async def imagine(self, interaction: discord.Interaction, prompt: str, model: discord.app_commands.Choice[str], sampler: discord.app_commands.Choice[str], negative: str = None, seed: int = None):
|
||||
retry_after = cooldown_manager.check_cooldown(interaction.user.id)
|
||||
if retry_after:
|
||||
await interaction.response.send_message(f"You are on cooldown. Try again in {retry_after:.2f} seconds.", ephemeral=True)
|
||||
return
|
||||
|
||||
await interaction.response.defer()
|
||||
|
||||
is_nsfw = any(word in prompt.lower() for word in blacklisted_words)
|
||||
|
||||
is_child = any(word in prompt.lower() for word in blocked)
|
||||
|
||||
if is_child:
|
||||
await interaction.followup.send("Child porn is not allowed as it violates Discord ToS. Please try again with a different peompt.")
|
||||
return
|
||||
|
||||
if is_nsfw and not interaction.channel.nsfw:
|
||||
await interaction.followup.send("You can create NSFW images in NSFW channels only. Please try in an appropriate channel.", ephemeral=True)
|
||||
return
|
||||
|
||||
model_uid = Model[model.value].value[0]
|
||||
|
||||
try:
|
||||
imagefileobj = await generate_image_prodia(prompt, model_uid, sampler.value, seed, negative)
|
||||
except aiohttp.ClientPayloadError:
|
||||
await interaction.followup.send("An error occurred while generating the image. Please try again later.", ephemeral=True)
|
||||
return
|
||||
except Exception as e:
|
||||
await interaction.followup.send(f"An unexpected error occurred: {e}", ephemeral=True)
|
||||
return
|
||||
|
||||
if is_nsfw:
|
||||
img_file = discord.File(imagefileobj, filename="image.png", spoiler=True, description=prompt)
|
||||
prompt = f"||{prompt}||"
|
||||
else:
|
||||
img_file = discord.File(imagefileobj, filename="image.png", description=prompt)
|
||||
|
||||
desc = (
|
||||
f"**Prompt:** {prompt}\n"
|
||||
f"**Model:** {model.value}\n"
|
||||
f"**Sampler:** {sampler.value}\n"
|
||||
f"**Seed:** {seed}"
|
||||
)
|
||||
if negative:
|
||||
desc += f"\n**Negative Prompt:** {negative}"
|
||||
if is_nsfw:
|
||||
desc += f"\n**NSFW:** {str(is_nsfw)}"
|
||||
|
||||
view = CV2(f"Generated Image by {interaction.user.display_name}", desc)
|
||||
|
||||
await interaction.followup.send(view=view, file=img_file, ephemeral=True)
|
||||
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(AiStuffCog(bot))
|
||||
712
bot/cogs/commands/j2c.py
Normal file
712
bot/cogs/commands/j2c.py
Normal file
@@ -0,0 +1,712 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord import ui, SelectOption
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, ActionRow
|
||||
import aiosqlite
|
||||
import asyncio
|
||||
from typing import Dict, List, Optional
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
class JoinToCreate(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.private_channels: Dict[int, Dict] = {}
|
||||
self.category_name = "J2C"
|
||||
self.setup_data: Dict[int, Dict] = {}
|
||||
self.db_path = "j2c_data.db"
|
||||
self.blocked_users: Dict[int, List[int]] = {} # {vc_id: [user_ids]}
|
||||
self.creating_vc = set()
|
||||
|
||||
async def init_db(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS guild_setup (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
join_channel_id INTEGER,
|
||||
control_channel_id INTEGER,
|
||||
control_message_id INTEGER,
|
||||
category_id INTEGER
|
||||
)
|
||||
""")
|
||||
try:
|
||||
await db.execute("ALTER TABLE guild_setup ADD COLUMN category_id INTEGER")
|
||||
except aiosqlite.OperationalError:
|
||||
pass
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS private_channels (
|
||||
vc_id INTEGER PRIMARY KEY,
|
||||
guild_id INTEGER,
|
||||
owner_id INTEGER,
|
||||
member_limit INTEGER DEFAULT 2,
|
||||
region TEXT DEFAULT '',
|
||||
is_locked BOOLEAN DEFAULT FALSE,
|
||||
has_waiting_room BOOLEAN DEFAULT FALSE,
|
||||
has_thread BOOLEAN DEFAULT FALSE
|
||||
)
|
||||
""")
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS blocked_users (
|
||||
vc_id INTEGER,
|
||||
user_id INTEGER,
|
||||
PRIMARY KEY (vc_id, user_id)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
async def load_data(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
# Load guild setups
|
||||
async with db.execute("SELECT guild_id, join_channel_id, control_channel_id, control_message_id, category_id FROM guild_setup") as cursor:
|
||||
async for row in cursor:
|
||||
guild_id, join_channel_id, control_channel_id, control_message_id, category_id = row
|
||||
self.setup_data[guild_id] = {
|
||||
"join_channel_id": join_channel_id,
|
||||
"control_channel_id": control_channel_id,
|
||||
"control_message_id": control_message_id,
|
||||
"category_id": category_id
|
||||
}
|
||||
|
||||
# Load private channels
|
||||
async with db.execute("SELECT * FROM private_channels") as cursor:
|
||||
async for row in cursor:
|
||||
vc_id, guild_id, owner_id, member_limit, region, is_locked, has_waiting_room, has_thread = row
|
||||
self.private_channels[vc_id] = {
|
||||
"owner": owner_id,
|
||||
"limit": member_limit,
|
||||
"region": region,
|
||||
"is_locked": bool(is_locked),
|
||||
"has_waiting_room": bool(has_waiting_room),
|
||||
"has_thread": bool(has_thread),
|
||||
"guild_id": guild_id
|
||||
}
|
||||
|
||||
# Load blocked users
|
||||
async with db.execute("SELECT * FROM blocked_users") as cursor:
|
||||
async for row in cursor:
|
||||
vc_id, user_id = row
|
||||
if vc_id not in self.blocked_users:
|
||||
self.blocked_users[vc_id] = []
|
||||
self.blocked_users[vc_id].append(user_id)
|
||||
|
||||
async def save_guild_setup(self, guild_id: int, data: Dict):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("""
|
||||
INSERT OR REPLACE INTO guild_setup (guild_id, join_channel_id, control_channel_id, control_message_id, category_id)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", (guild_id, data["join_channel_id"], data["control_channel_id"], data["control_message_id"], data.get("category_id")))
|
||||
await db.commit()
|
||||
|
||||
async def save_private_channel(self, vc_id: int, guild_id: int, data: Dict):
|
||||
try:
|
||||
if vc_id not in self.private_channels:
|
||||
self.private_channels[vc_id] = data
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("""
|
||||
INSERT OR REPLACE INTO private_channels
|
||||
(vc_id, guild_id, owner_id, member_limit, region, is_locked, has_waiting_room, has_thread)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
vc_id, guild_id,
|
||||
data["owner"],
|
||||
data.get("limit", 2),
|
||||
data.get("region", ""),
|
||||
data.get("is_locked", False),
|
||||
data.get("has_waiting_room", False),
|
||||
data.get("has_thread", False)
|
||||
))
|
||||
await db.commit()
|
||||
except Exception as e:
|
||||
print(f"Error saving private channel: {e}")
|
||||
|
||||
async def delete_private_channel(self, vc_id: int):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("DELETE FROM private_channels WHERE vc_id = ?", (vc_id,))
|
||||
await db.execute("DELETE FROM blocked_users WHERE vc_id = ?", (vc_id,))
|
||||
await db.commit()
|
||||
|
||||
async def delete_guild_setup(self, guild_id: int):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("DELETE FROM guild_setup WHERE guild_id = ?", (guild_id,))
|
||||
await db.execute("DELETE FROM private_channels WHERE guild_id = ?", (guild_id,))
|
||||
await db.execute("DELETE FROM blocked_users WHERE vc_id IN (SELECT vc_id FROM private_channels WHERE guild_id = ?)", (guild_id,))
|
||||
await db.commit()
|
||||
|
||||
async def block_user(self, vc_id: int, user_id: int):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("INSERT OR IGNORE INTO blocked_users (vc_id, user_id) VALUES (?, ?)", (vc_id, user_id))
|
||||
await db.commit()
|
||||
if vc_id not in self.blocked_users:
|
||||
self.blocked_users[vc_id] = []
|
||||
if user_id not in self.blocked_users[vc_id]:
|
||||
self.blocked_users[vc_id].append(user_id)
|
||||
|
||||
async def unblock_user(self, vc_id: int, user_id: int):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("DELETE FROM blocked_users WHERE vc_id = ? AND user_id = ?", (vc_id, user_id))
|
||||
await db.commit()
|
||||
if vc_id in self.blocked_users and user_id in self.blocked_users[vc_id]:
|
||||
self.blocked_users[vc_id].remove(user_id)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
await self.init_db()
|
||||
await self.load_data()
|
||||
for guild_id, data in self.setup_data.items():
|
||||
guild = self.bot.get_guild(guild_id)
|
||||
if guild:
|
||||
try:
|
||||
control_channel = guild.get_channel(data["control_channel_id"])
|
||||
if control_channel:
|
||||
msg = None
|
||||
if data["control_message_id"]:
|
||||
try:
|
||||
msg = await control_channel.fetch_message(data["control_message_id"])
|
||||
except:
|
||||
pass
|
||||
|
||||
if msg:
|
||||
view = ControlPanelView(self, guild)
|
||||
await msg.edit(view=view, embed=None, content=None)
|
||||
else:
|
||||
view = ControlPanelView(self, guild)
|
||||
msg = await control_channel.send(view=view)
|
||||
self.setup_data[guild_id]["control_message_id"] = msg.id
|
||||
await self.save_guild_setup(guild_id, self.setup_data[guild_id])
|
||||
except Exception as e:
|
||||
print(f"Error in J2C on_ready: {e}")
|
||||
continue
|
||||
|
||||
@commands.command(name='j2csetup')
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def setup_private_channels(self, ctx):
|
||||
if ctx.guild.id in self.setup_data:
|
||||
await ctx.send(view=CV2("❌ Error", "J2C system is already setup in this server!"))
|
||||
return
|
||||
|
||||
category = discord.utils.get(ctx.guild.categories, name=self.category_name)
|
||||
if not category:
|
||||
category = await ctx.guild.create_category(self.category_name)
|
||||
|
||||
join_channel = await ctx.guild.create_voice_channel(
|
||||
"➕ Join to Create",
|
||||
category=category,
|
||||
reason="J2C System Setup"
|
||||
)
|
||||
|
||||
control_channel = await ctx.guild.create_text_channel(
|
||||
"ctrl-panel",
|
||||
category=category,
|
||||
reason="J2C System Setup"
|
||||
)
|
||||
|
||||
view = ControlPanelView(self, ctx.guild)
|
||||
control_message = await control_channel.send(view=view)
|
||||
|
||||
self.setup_data[ctx.guild.id] = {
|
||||
"join_channel_id": join_channel.id,
|
||||
"control_channel_id": control_channel.id,
|
||||
"control_message_id": control_message.id
|
||||
}
|
||||
await self.save_guild_setup(ctx.guild.id, self.setup_data[ctx.guild.id])
|
||||
|
||||
await ctx.send(view=CV2("✅ Success", f"J2C system setup complete! Join {join_channel.mention} to create a private VC."))
|
||||
|
||||
@commands.command(name='j2creset')
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def reset_private_channels(self, ctx):
|
||||
if ctx.guild.id not in self.setup_data:
|
||||
await ctx.send(view=CV2("❌ Error", "J2C system is not setup in this server!"))
|
||||
return
|
||||
|
||||
category = discord.utils.get(ctx.guild.categories, name=self.category_name)
|
||||
if category:
|
||||
for channel in category.channels:
|
||||
try:
|
||||
await channel.delete(reason="J2C System Reset")
|
||||
except:
|
||||
continue
|
||||
|
||||
if category:
|
||||
try:
|
||||
await category.delete(reason="J2C System Reset")
|
||||
except:
|
||||
pass
|
||||
|
||||
vc_ids = [vc_id for vc_id, data in self.private_channels.items()
|
||||
if data.get("guild_id") == ctx.guild.id]
|
||||
for vc_id in vc_ids:
|
||||
del self.private_channels[vc_id]
|
||||
|
||||
del self.setup_data[ctx.guild.id]
|
||||
await self.delete_guild_setup(ctx.guild.id)
|
||||
|
||||
await ctx.send(view=CV2("✅ Success", "J2C system has been completely reset in this server!"))
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_voice_state_update(self, member, before, after):
|
||||
if member.guild.id not in self.setup_data:
|
||||
return
|
||||
|
||||
guild_data = self.setup_data[member.guild.id]
|
||||
|
||||
# User joined the join channel
|
||||
if after.channel and after.channel.id == guild_data["join_channel_id"]:
|
||||
if member.id in self.creating_vc:
|
||||
return
|
||||
self.creating_vc.add(member.id)
|
||||
try:
|
||||
category_id = guild_data.get("category_id")
|
||||
category = None
|
||||
if category_id:
|
||||
try:
|
||||
category = member.guild.get_channel(int(category_id))
|
||||
except:
|
||||
pass
|
||||
|
||||
# Fallback 1: Use the parent category of the join channel
|
||||
if not category and after.channel.category:
|
||||
category = after.channel.category
|
||||
|
||||
# Fallback 2: J2C category by name
|
||||
if not category:
|
||||
category = discord.utils.get(member.guild.categories, name=self.category_name)
|
||||
|
||||
vc = await member.guild.create_voice_channel(
|
||||
f"{member.name}'s VC",
|
||||
category=category,
|
||||
reason="Private VC Creation",
|
||||
user_limit=2
|
||||
)
|
||||
|
||||
await member.move_to(vc)
|
||||
|
||||
self.private_channels[vc.id] = {
|
||||
"owner": member.id,
|
||||
"limit": 2,
|
||||
"region": "",
|
||||
"is_locked": False,
|
||||
"has_waiting_room": False,
|
||||
"has_thread": False,
|
||||
"guild_id": member.guild.id
|
||||
}
|
||||
await self.save_private_channel(vc.id, member.guild.id, self.private_channels[vc.id])
|
||||
await self.update_control_panel(member.guild)
|
||||
finally:
|
||||
self.creating_vc.discard(member.id)
|
||||
|
||||
# User left a private channel
|
||||
if before.channel and before.channel.id in self.private_channels:
|
||||
# Check if user was blocked
|
||||
if (before.channel.id in self.blocked_users and
|
||||
member.id in self.blocked_users[before.channel.id]):
|
||||
await member.move_to(None)
|
||||
return
|
||||
|
||||
# Check if channel is empty
|
||||
if len(before.channel.members) == 0:
|
||||
try:
|
||||
await before.channel.delete()
|
||||
except:
|
||||
pass
|
||||
|
||||
if before.channel.id in self.private_channels:
|
||||
del self.private_channels[before.channel.id]
|
||||
await self.delete_private_channel(before.channel.id)
|
||||
|
||||
await self.update_control_panel(member.guild)
|
||||
|
||||
async def update_control_panel(self, guild: discord.Guild):
|
||||
if guild.id not in self.setup_data:
|
||||
return
|
||||
|
||||
guild_data = self.setup_data[guild.id]
|
||||
control_channel = guild.get_channel(guild_data["control_channel_id"])
|
||||
if not control_channel or not guild_data["control_message_id"]:
|
||||
return
|
||||
|
||||
try:
|
||||
control_message = await control_channel.fetch_message(guild_data["control_message_id"])
|
||||
view = ControlPanelView(self, guild)
|
||||
await control_message.edit(view=view, embed=None, content=None)
|
||||
except:
|
||||
pass
|
||||
|
||||
class ControlPanelView(LayoutView):
|
||||
def __init__(self, cog, guild):
|
||||
super().__init__(timeout=None)
|
||||
self.cog = cog
|
||||
|
||||
desc = ("Join the 'Join to Create VC' voice channel to create your own private voice channel.\n\n"
|
||||
"Use the buttons below to manage your private VC.")
|
||||
active_vcs = []
|
||||
for vc_id, data in self.cog.private_channels.items():
|
||||
if guild.get_channel(vc_id):
|
||||
owner = guild.get_member(data["owner"])
|
||||
if owner:
|
||||
status = []
|
||||
if data["is_locked"]:
|
||||
status.append("🔒 Locked")
|
||||
if data["has_thread"]:
|
||||
status.append("💬 Thread")
|
||||
|
||||
vc_info = f"<#{vc_id}> (👑 {owner.mention})"
|
||||
if status:
|
||||
vc_info += f" [{' '.join(status)}]"
|
||||
active_vcs.append(vc_info)
|
||||
|
||||
if active_vcs:
|
||||
desc += "\n\n**Active Private VCs**\n" + "\n".join(active_vcs)
|
||||
|
||||
container = build_container(
|
||||
TextDisplay("**J2C System**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(desc)
|
||||
)
|
||||
self.add_item(container)
|
||||
|
||||
b_limit = ui.Button(label="LIMIT", style=discord.ButtonStyle.blurple, custom_id="j2c:limit", emoji="⏳")
|
||||
b_limit.callback = self.set_limit
|
||||
b_privacy = ui.Button(label="PRIVACY", style=discord.ButtonStyle.blurple, custom_id="j2c:privacy", emoji="🔒")
|
||||
b_privacy.callback = self.toggle_privacy
|
||||
b_thread = ui.Button(label="THREAD", style=discord.ButtonStyle.blurple, custom_id="j2c:thread", emoji="💬")
|
||||
b_thread.callback = self.create_thread
|
||||
b_untrust = ui.Button(label="UNTRUST", style=discord.ButtonStyle.green, custom_id="j2c:untrust", emoji="❌")
|
||||
b_untrust.callback = self.untrust
|
||||
b_invite = ui.Button(label="INVITE", style=discord.ButtonStyle.green, custom_id="j2c:invite", emoji="✉️")
|
||||
b_invite.callback = self.invite_user
|
||||
b_kick = ui.Button(label="KICK", style=discord.ButtonStyle.green, custom_id="j2c:kick", emoji="👢")
|
||||
b_kick.callback = self.kick_user
|
||||
b_region = ui.Button(label="REGION", style=discord.ButtonStyle.green, custom_id="j2c:region", emoji="🌍")
|
||||
b_region.callback = self.set_region
|
||||
b_unblock = ui.Button(label="UNBLOCK", style=discord.ButtonStyle.red, custom_id="j2c:unblock", emoji="🔓")
|
||||
b_unblock.callback = self.unblock
|
||||
b_claim = ui.Button(label="CLAIM", style=discord.ButtonStyle.red, custom_id="j2c:claim", emoji="⭐")
|
||||
b_claim.callback = self.claim
|
||||
b_transfer = ui.Button(label="TRANSFER", style=discord.ButtonStyle.red, custom_id="j2c:transfer", emoji="🔄")
|
||||
b_transfer.callback = self.transfer
|
||||
b_delete = ui.Button(label="DELETE", style=discord.ButtonStyle.red, custom_id="j2c:delete", emoji="🗑️")
|
||||
b_delete.callback = self.delete_vc
|
||||
b_block = ui.Button(label="BLOCK", style=discord.ButtonStyle.danger, custom_id="j2c:block", emoji="🚫")
|
||||
b_block.callback = self.block
|
||||
|
||||
self.add_item(ActionRow(b_limit, b_privacy, b_thread))
|
||||
self.add_item(ActionRow(b_untrust, b_invite, b_kick, b_region))
|
||||
self.add_item(ActionRow(b_unblock, b_claim, b_transfer, b_delete))
|
||||
self.add_item(ActionRow(b_block))
|
||||
|
||||
async def get_owned_vc(self, interaction: discord.Interaction) -> Optional[discord.VoiceChannel]:
|
||||
for vc_id, data in self.cog.private_channels.items():
|
||||
if data["owner"] == interaction.user.id:
|
||||
vc = interaction.guild.get_channel(vc_id)
|
||||
if vc:
|
||||
return vc
|
||||
return None
|
||||
|
||||
async def set_limit(self, interaction: discord.Interaction):
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.response.send_message("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
modal = SetLimitModal(vc)
|
||||
await interaction.response.send_modal(modal)
|
||||
|
||||
async def toggle_privacy(self, interaction: discord.Interaction):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.followup.send("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
is_locked = not self.cog.private_channels[vc.id]["is_locked"]
|
||||
await vc.set_permissions(interaction.guild.default_role, connect=not is_locked)
|
||||
self.cog.private_channels[vc.id]["is_locked"] = is_locked
|
||||
await self.cog.save_private_channel(vc.id, interaction.guild.id, self.cog.private_channels[vc.id])
|
||||
await interaction.followup.send(f"VC is now {'🔒 locked' if is_locked else '🔓 unlocked'}!", ephemeral=True)
|
||||
await self.cog.update_control_panel(interaction.guild)
|
||||
|
||||
|
||||
async def create_thread(self, interaction: discord.Interaction):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.followup.send("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
has_thread = not self.cog.private_channels[vc.id]["has_thread"]
|
||||
self.cog.private_channels[vc.id]["has_thread"] = has_thread
|
||||
await self.cog.save_private_channel(vc.id, interaction.guild.id, self.cog.private_channels[vc.id])
|
||||
if has_thread:
|
||||
thread = await interaction.channel.create_thread(name=f"{vc.name} Discussion", auto_archive_duration=60)
|
||||
await interaction.followup.send(f"Created thread: {thread.mention}", ephemeral=True)
|
||||
else:
|
||||
await interaction.followup.send("Thread feature disabled for your VC", ephemeral=True)
|
||||
await self.cog.update_control_panel(interaction.guild)
|
||||
|
||||
async def untrust(self, interaction: discord.Interaction):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.followup.send("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
await interaction.followup.send("Untrust feature would be implemented here", ephemeral=True)
|
||||
|
||||
async def invite_user(self, interaction: discord.Interaction):
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.response.send_message("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
options = []
|
||||
for member in interaction.guild.members:
|
||||
if (member not in vc.members and not member.bot and member != interaction.user):
|
||||
options.append(SelectOption(label=member.name, value=str(member.id)))
|
||||
if not options:
|
||||
await interaction.response.send_message("No members available to invite!", ephemeral=True)
|
||||
return
|
||||
dropdown = UserSelectDropdown(options, "Select members to invite", self.invite_selected)
|
||||
view = ui.View()
|
||||
view.add_item(dropdown)
|
||||
await interaction.response.send_message("Select members to invite:", view=view, ephemeral=True)
|
||||
|
||||
async def invite_selected(self, interaction: discord.Interaction, selected: List[str]):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc: return
|
||||
for user_id in selected:
|
||||
member = interaction.guild.get_member(int(user_id))
|
||||
if member:
|
||||
try: await member.send(f"You've been invited to join {vc.mention} by {interaction.user.mention}!")
|
||||
except: pass
|
||||
await interaction.followup.send("Invites sent!", ephemeral=True)
|
||||
|
||||
async def kick_user(self, interaction: discord.Interaction):
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.response.send_message("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
options = []
|
||||
for member in vc.members:
|
||||
if member.id != interaction.user.id:
|
||||
options.append(SelectOption(label=member.name, value=str(member.id)))
|
||||
if not options:
|
||||
await interaction.response.send_message("No users to kick in your VC!", ephemeral=True)
|
||||
return
|
||||
dropdown = UserSelectDropdown(options, "Select members to kick", self.kick_selected)
|
||||
view = ui.View()
|
||||
view.add_item(dropdown)
|
||||
await interaction.response.send_message("Select members to kick:", view=view, ephemeral=True)
|
||||
|
||||
async def kick_selected(self, interaction: discord.Interaction, selected: List[str]):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc: return
|
||||
for user_id in selected:
|
||||
member = interaction.guild.get_member(int(user_id))
|
||||
if member and member in vc.members:
|
||||
try: await member.move_to(None)
|
||||
except: pass
|
||||
await interaction.followup.send("Selected members kicked!", ephemeral=True)
|
||||
|
||||
async def set_region(self, interaction: discord.Interaction):
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.response.send_message("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
regions = [
|
||||
SelectOption(label="Automatic", value="auto"), SelectOption(label="US West", value="us-west"),
|
||||
SelectOption(label="US East", value="us-east"), SelectOption(label="Europe", value="europe"),
|
||||
SelectOption(label="Singapore", value="singapore"), SelectOption(label="Japan", value="japan"),
|
||||
SelectOption(label="Brazil", value="brazil"), SelectOption(label="Australia", value="australia")
|
||||
]
|
||||
dropdown = RegionSelectDropdown(regions, vc)
|
||||
view = ui.View()
|
||||
view.add_item(dropdown)
|
||||
await interaction.response.send_message("Select a region:", view=view, ephemeral=True)
|
||||
|
||||
async def unblock(self, interaction: discord.Interaction):
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.response.send_message("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
if vc.id not in self.cog.blocked_users or not self.cog.blocked_users[vc.id]:
|
||||
await interaction.response.send_message("No blocked users!", ephemeral=True)
|
||||
return
|
||||
options = []
|
||||
for user_id in self.cog.blocked_users[vc.id]:
|
||||
member = interaction.guild.get_member(user_id)
|
||||
if member:
|
||||
options.append(SelectOption(label=member.name, value=str(user_id)))
|
||||
dropdown = UserSelectDropdown(options, "Select users to unblock", self.unblock_selected)
|
||||
view = ui.View()
|
||||
view.add_item(dropdown)
|
||||
await interaction.response.send_message("Select users to unblock:", view=view, ephemeral=True)
|
||||
|
||||
async def unblock_selected(self, interaction: discord.Interaction, selected: List[str]):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc: return
|
||||
for user_id in selected:
|
||||
await self.cog.unblock_user(vc.id, int(user_id))
|
||||
await interaction.followup.send("Selected users unblocked!", ephemeral=True)
|
||||
await self.cog.update_control_panel(interaction.guild)
|
||||
|
||||
async def claim(self, interaction: discord.Interaction):
|
||||
available_vcs = []
|
||||
for vc_id, data in self.cog.private_channels.items():
|
||||
if data["guild_id"] == interaction.guild.id:
|
||||
vc = interaction.guild.get_channel(vc_id)
|
||||
if vc:
|
||||
owner = interaction.guild.get_member(data["owner"])
|
||||
if not owner or owner not in vc.members:
|
||||
available_vcs.append((vc, data))
|
||||
if not available_vcs:
|
||||
await interaction.response.send_message("No VCs available to claim!", ephemeral=True)
|
||||
return
|
||||
options = []
|
||||
for vc, data in available_vcs:
|
||||
owner_mention = f"<{data['owner']}>" if data['owner'] else "Unknown"
|
||||
options.append(SelectOption(label=f"{vc.name} (prev: {owner_mention})", value=str(vc.id)))
|
||||
dropdown = VCSelectDropdown(options, "Select VC to claim", self.claim_selected)
|
||||
view = ui.View()
|
||||
view.add_item(dropdown)
|
||||
await interaction.response.send_message("Select VC to claim:", view=view, ephemeral=True)
|
||||
|
||||
async def claim_selected(self, interaction: discord.Interaction, selected: List[str]):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc_id = int(selected[0])
|
||||
vc = interaction.guild.get_channel(vc_id)
|
||||
if not vc:
|
||||
await interaction.followup.send("VC no longer exists!", ephemeral=True)
|
||||
return
|
||||
self.cog.private_channels[vc.id]["owner"] = interaction.user.id
|
||||
await self.cog.save_private_channel(vc.id, interaction.guild.id, self.cog.private_channels[vc.id])
|
||||
await interaction.followup.send(f"You've claimed {vc.mention}!", ephemeral=True)
|
||||
await self.cog.update_control_panel(interaction.guild)
|
||||
|
||||
async def transfer(self, interaction: discord.Interaction):
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.response.send_message("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
options = []
|
||||
for member in vc.members:
|
||||
if member.id != interaction.user.id:
|
||||
options.append(SelectOption(label=member.name, value=str(member.id)))
|
||||
if not options:
|
||||
await interaction.response.send_message("No users to transfer to in your VC!", ephemeral=True)
|
||||
return
|
||||
dropdown = UserSelectDropdown(options, "Select new owner", self.transfer_selected)
|
||||
view = ui.View()
|
||||
view.add_item(dropdown)
|
||||
await interaction.response.send_message("Select new owner:", view=view, ephemeral=True)
|
||||
|
||||
async def transfer_selected(self, interaction: discord.Interaction, selected: List[str]):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc: return
|
||||
new_owner_id = int(selected[0])
|
||||
new_owner = interaction.guild.get_member(new_owner_id)
|
||||
if not new_owner:
|
||||
await interaction.followup.send("User not found!", ephemeral=True)
|
||||
return
|
||||
self.cog.private_channels[vc.id]["owner"] = new_owner_id
|
||||
await self.cog.save_private_channel(vc.id, interaction.guild.id, self.cog.private_channels[vc.id])
|
||||
await interaction.followup.send(f"Transferred ownership of {vc.mention} to {new_owner.mention}!", ephemeral=True)
|
||||
await self.cog.update_control_panel(interaction.guild)
|
||||
|
||||
async def delete_vc(self, interaction: discord.Interaction):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.followup.send("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
for member in vc.members:
|
||||
try: await member.move_to(None)
|
||||
except: pass
|
||||
await vc.delete()
|
||||
if vc.id in self.cog.private_channels:
|
||||
del self.cog.private_channels[vc.id]
|
||||
await self.cog.delete_private_channel(vc.id)
|
||||
await interaction.followup.send("Your private VC has been deleted!", ephemeral=True)
|
||||
await self.cog.update_control_panel(interaction.guild)
|
||||
|
||||
async def block(self, interaction: discord.Interaction):
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc:
|
||||
await interaction.response.send_message("You don't own any private VC!", ephemeral=True)
|
||||
return
|
||||
options = []
|
||||
for member in interaction.guild.members:
|
||||
if (member not in vc.members and not member.bot and member != interaction.user):
|
||||
options.append(SelectOption(label=member.name, value=str(member.id)))
|
||||
if not options:
|
||||
await interaction.response.send_message("No members available to block!", ephemeral=True)
|
||||
return
|
||||
dropdown = UserSelectDropdown(options, "Select members to block", self.block_selected)
|
||||
view = ui.View()
|
||||
view.add_item(dropdown)
|
||||
await interaction.response.send_message("Select members to block:", view=view, ephemeral=True)
|
||||
|
||||
async def block_selected(self, interaction: discord.Interaction, selected: List[str]):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
vc = await self.get_owned_vc(interaction)
|
||||
if not vc: return
|
||||
for user_id in selected:
|
||||
await self.cog.block_user(vc.id, int(user_id))
|
||||
await interaction.followup.send("Selected members blocked from joining!", ephemeral=True)
|
||||
await self.cog.update_control_panel(interaction.guild)
|
||||
|
||||
|
||||
class UserSelectDropdown(ui.Select):
|
||||
def __init__(self, options: List[SelectOption], placeholder: str, callback):
|
||||
super().__init__(placeholder=placeholder, options=options, min_values=1, max_values=len(options))
|
||||
self.callback_func = callback
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
await self.callback_func(interaction, self.values)
|
||||
|
||||
class RegionSelectDropdown(ui.Select):
|
||||
def __init__(self, options: List[SelectOption], vc: discord.VoiceChannel):
|
||||
super().__init__(placeholder="Select a region", options=options)
|
||||
self.vc = vc
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
region = self.values[0]
|
||||
try:
|
||||
await self.vc.edit(rtc_region=region if region != "auto" else None)
|
||||
await interaction.response.send_message(f"Region set to {self.values[0]}!", ephemeral=True)
|
||||
except Exception as e:
|
||||
await interaction.response.send_message(f"Failed to set region: {e}", ephemeral=True)
|
||||
|
||||
class VCSelectDropdown(ui.Select):
|
||||
def __init__(self, options: List[SelectOption], placeholder: str, callback):
|
||||
super().__init__(placeholder=placeholder, options=options, min_values=1, max_values=1)
|
||||
self.callback_func = callback
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
await self.callback_func(interaction, self.values)
|
||||
|
||||
class SetLimitModal(ui.Modal, title="Set VC User Limit"):
|
||||
def __init__(self, vc: discord.VoiceChannel):
|
||||
super().__init__()
|
||||
self.vc = vc
|
||||
self.limit = ui.TextInput(
|
||||
label="User Limit (0 for no limit)",
|
||||
placeholder="Enter a number between 0 and 99",
|
||||
default=str(vc.user_limit) if vc.user_limit else "0",
|
||||
max_length=2
|
||||
)
|
||||
self.add_item(self.limit)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
try:
|
||||
limit = int(self.limit.value)
|
||||
if limit < 0 or limit > 99:
|
||||
raise ValueError
|
||||
await self.vc.edit(user_limit=limit if limit != 0 else None)
|
||||
cog = interaction.client.get_cog("JoinToCreate")
|
||||
if cog and self.vc.id in cog.private_channels:
|
||||
cog.private_channels[self.vc.id]["limit"] = limit
|
||||
await cog.save_private_channel(self.vc.id, interaction.guild.id, cog.private_channels[self.vc.id])
|
||||
await interaction.response.send_message(f"User limit set to {limit if limit != 0 else 'no limit'}!", ephemeral=True)
|
||||
await cog.update_control_panel(interaction.guild)
|
||||
except:
|
||||
await interaction.response.send_message("Invalid limit! Please enter a number between 0 and 99.", ephemeral=True)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(JoinToCreate(bot))
|
||||
201
bot/cogs/commands/jail.py
Normal file
201
bot/cogs/commands/jail.py
Normal file
@@ -0,0 +1,201 @@
|
||||
import discord
|
||||
from discord.ext import commands, tasks
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
from utils.cv2 import CV2
|
||||
|
||||
DB_FILE = "db/jail.db"
|
||||
|
||||
class Jail(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.conn = sqlite3.connect(DB_FILE)
|
||||
self.conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS jailed (
|
||||
guild_id TEXT,
|
||||
user_id TEXT,
|
||||
mod_id TEXT,
|
||||
reason TEXT,
|
||||
jailed_at TEXT,
|
||||
duration INTEGER,
|
||||
roles TEXT,
|
||||
PRIMARY KEY (guild_id, user_id)
|
||||
);
|
||||
""")
|
||||
self.conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS jail_settings (
|
||||
guild_id TEXT PRIMARY KEY,
|
||||
jail_role TEXT,
|
||||
jail_channel TEXT,
|
||||
mod_role TEXT,
|
||||
log_channel TEXT
|
||||
);
|
||||
""")
|
||||
# Ensure roles column exists (safe migration)
|
||||
try:
|
||||
self.conn.execute("SELECT roles FROM jailed LIMIT 1;")
|
||||
except sqlite3.OperationalError:
|
||||
self.conn.execute("ALTER TABLE jailed ADD COLUMN roles TEXT;")
|
||||
self.conn.commit()
|
||||
|
||||
self.jail_check_loop.start()
|
||||
|
||||
def cog_unload(self):
|
||||
self.jail_check_loop.cancel()
|
||||
self.conn.close()
|
||||
|
||||
def get_setting(self, guild_id, field):
|
||||
cursor = self.conn.execute(f"SELECT {field} FROM jail_settings WHERE guild_id = ?", (str(guild_id),))
|
||||
row = cursor.fetchone()
|
||||
return row[0] if row else None
|
||||
|
||||
def set_setting(self, guild_id, field, value):
|
||||
self.conn.execute(f"""
|
||||
INSERT INTO jail_settings (guild_id, {field})
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT(guild_id) DO UPDATE SET {field} = excluded.{field}
|
||||
""", (str(guild_id), str(value)))
|
||||
self.conn.commit()
|
||||
|
||||
def parse_duration(self, duration_str: str):
|
||||
pattern = re.compile(r'((?P<hours>\d+)h)?((?P<minutes>\d+)m)?')
|
||||
match = pattern.fullmatch(duration_str.lower())
|
||||
if not match:
|
||||
return None
|
||||
hours = int(match.group('hours') or 0)
|
||||
minutes = int(match.group('minutes') or 0)
|
||||
return (hours * 60 + minutes) * 60 if (hours or minutes) else None
|
||||
|
||||
@tasks.loop(minutes=1)
|
||||
async def jail_check_loop(self):
|
||||
now = datetime.utcnow()
|
||||
cursor = self.conn.execute("SELECT guild_id, user_id, duration, jailed_at, roles FROM jailed")
|
||||
for guild_id, user_id, duration, jailed_at, roles in cursor.fetchall():
|
||||
if not duration:
|
||||
continue
|
||||
jailed_time = datetime.fromisoformat(jailed_at)
|
||||
if (now - jailed_time).total_seconds() >= duration:
|
||||
guild = self.bot.get_guild(int(guild_id))
|
||||
if guild:
|
||||
member = guild.get_member(int(user_id))
|
||||
if member:
|
||||
await self.unjail_member(guild, member)
|
||||
|
||||
async def unjail_member(self, guild, member):
|
||||
jail_role_id = self.get_setting(guild.id, "jail_role")
|
||||
if jail_role_id:
|
||||
jail_role = guild.get_role(int(jail_role_id))
|
||||
if jail_role in member.roles:
|
||||
await member.remove_roles(jail_role, reason="Unjailed")
|
||||
|
||||
# Restore old roles
|
||||
cursor = self.conn.execute("SELECT roles FROM jailed WHERE guild_id = ? AND user_id = ?", (str(guild.id), str(member.id)))
|
||||
row = cursor.fetchone()
|
||||
if row and row[0]:
|
||||
role_ids = map(int, row[0].split(","))
|
||||
roles = [guild.get_role(rid) for rid in role_ids if guild.get_role(rid)]
|
||||
await member.add_roles(*roles, reason="Restored previous roles after jail")
|
||||
|
||||
self.conn.execute("DELETE FROM jailed WHERE guild_id = ? AND user_id = ?", (str(guild.id), str(member.id)))
|
||||
self.conn.commit()
|
||||
|
||||
try:
|
||||
await member.send(f"🔓 You have been unjailed in **{guild.name}**.")
|
||||
except:
|
||||
pass
|
||||
|
||||
log_channel_id = self.get_setting(guild.id, "log_channel")
|
||||
if log_channel_id:
|
||||
log_channel = guild.get_channel(int(log_channel_id))
|
||||
if log_channel:
|
||||
desc = (
|
||||
f"**User:** {member.mention}\n"
|
||||
f"**Time:** <t:{int(datetime.utcnow().timestamp())}:f>"
|
||||
)
|
||||
await log_channel.send(view=CV2("🔓 Member Unjailed", desc))
|
||||
|
||||
@commands.command(name="jail")
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def jail(self, ctx, member: discord.Member, duration: str = None, *, reason="No reason provided"):
|
||||
jail_role_id = self.get_setting(ctx.guild.id, "jail_role")
|
||||
jail_channel_id = self.get_setting(ctx.guild.id, "jail_channel")
|
||||
log_channel_id = self.get_setting(ctx.guild.id, "log_channel")
|
||||
|
||||
if not jail_role_id or not jail_channel_id:
|
||||
return await ctx.send(view=CV2("⚠️ Configuration Error", "Jail system not fully configured."))
|
||||
|
||||
jail_role = ctx.guild.get_role(int(jail_role_id))
|
||||
if not jail_role:
|
||||
return await ctx.send(view=CV2("⚠️ Configuration Error", "Jail role does not exist."))
|
||||
|
||||
duration_secs = self.parse_duration(duration) if duration else None
|
||||
jailed_at = datetime.utcnow().isoformat()
|
||||
roles_str = ",".join(str(r.id) for r in member.roles if r != ctx.guild.default_role)
|
||||
|
||||
self.conn.execute("DELETE FROM jailed WHERE guild_id = ? AND user_id = ?", (str(ctx.guild.id), str(member.id)))
|
||||
self.conn.execute("""
|
||||
INSERT INTO jailed (guild_id, user_id, mod_id, reason, jailed_at, duration, roles)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""", (str(ctx.guild.id), str(member.id), str(ctx.author.id), reason, jailed_at, duration_secs, roles_str))
|
||||
self.conn.commit()
|
||||
|
||||
try:
|
||||
await member.edit(roles=[jail_role], reason="Jailed")
|
||||
except discord.Forbidden:
|
||||
return await ctx.send(view=CV2("❌ Permission Error", "I don't have permission to change roles."))
|
||||
|
||||
jail_channel = ctx.guild.get_channel(int(jail_channel_id))
|
||||
if jail_channel:
|
||||
await jail_channel.set_permissions(member, view_channel=True, send_messages=True, read_message_history=True)
|
||||
|
||||
try:
|
||||
await member.send(f"🔒 You were jailed in **{ctx.guild.name}**.\n📝 Reason: {reason}\n⏰ Duration: {duration or 'Permanent'}")
|
||||
except:
|
||||
pass
|
||||
|
||||
await ctx.send(view=CV2("✅ Success", f"🔒 {member.mention} has been jailed {'for ' + duration if duration else 'permanently'}."))
|
||||
|
||||
if log_channel_id:
|
||||
log_channel = ctx.guild.get_channel(int(log_channel_id))
|
||||
if log_channel:
|
||||
desc = (
|
||||
f"**User:** {member.mention}\n"
|
||||
f"**Moderator:** {ctx.author.mention}\n"
|
||||
f"**Reason:** {reason}\n"
|
||||
f"**Duration:** {duration or 'Permanent'}\n"
|
||||
f"**Time:** <t:{int(datetime.utcnow().timestamp())}:f>"
|
||||
)
|
||||
await log_channel.send(view=CV2("🔒 Member Jailed", desc))
|
||||
|
||||
@commands.command(name="unjail")
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def unjail(self, ctx, member: discord.Member):
|
||||
await self.unjail_member(ctx.guild, member)
|
||||
await ctx.send(view=CV2("✅ Success", f"{member.mention} has been unjailed."))
|
||||
|
||||
@commands.command(name="jailhistory")
|
||||
async def jailhistory(self, ctx, member: discord.Member):
|
||||
cursor = self.conn.execute("""
|
||||
SELECT reason, jailed_at, duration, mod_id FROM jailed
|
||||
WHERE guild_id = ? AND user_id = ?
|
||||
""", (str(ctx.guild.id), str(member.id)))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
reason, jailed_at, duration, mod_id = row
|
||||
mod = ctx.guild.get_member(int(mod_id))
|
||||
jailed_time = datetime.fromisoformat(jailed_at)
|
||||
|
||||
desc = (
|
||||
f"**User:** {member.mention}\n"
|
||||
f"**Reason:** {reason}\n"
|
||||
f"**Jailed At:** <t:{int(jailed_time.timestamp())}:f>\n"
|
||||
f"**Duration:** {f'{duration // 60} minutes' if duration else 'Permanent'}\n"
|
||||
f"**Jailed By:** {mod.mention if mod else 'Unknown'}"
|
||||
)
|
||||
await ctx.send(view=CV2("📄 Jail Record", desc))
|
||||
else:
|
||||
await ctx.send(view=CV2("❌ Error", f"No jail record found for {member.mention}"))
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Jail(bot))
|
||||
86
bot/cogs/commands/joindm.py
Normal file
86
bot/cogs/commands/joindm.py
Normal file
@@ -0,0 +1,86 @@
|
||||
import discord
|
||||
from utils.emoji import BLOBPART
|
||||
from discord.ext import commands
|
||||
import json
|
||||
from utils.cv2 import CV2
|
||||
|
||||
class joindm(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.joindm_messages = {}
|
||||
self.load_joindm_messages()
|
||||
|
||||
def load_joindm_messages(self):
|
||||
# Load the join DM messages from file
|
||||
try:
|
||||
with open('jsondb/joindm_messages.json', 'r') as f:
|
||||
self.joindm_messages = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self.joindm_messages = {}
|
||||
|
||||
def save_joindm_messages(self):
|
||||
# Save the join DM messages to file
|
||||
with open('jsondb/joindm_messages.json', 'w') as f:
|
||||
json.dump(self.joindm_messages, f)
|
||||
|
||||
@commands.group(invoke_without_command=True)
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def joindm(self, ctx):
|
||||
# Display the current join DM message
|
||||
guild_id = str(ctx.guild.id)
|
||||
if guild_id in self.joindm_messages:
|
||||
await ctx.send(view=CV2("✅ Join DM Status", f"The current join DM message is:\n`{self.joindm_messages[guild_id]}`"))
|
||||
else:
|
||||
await ctx.send(view=CV2("❌ Error", "No custom join DM message has been set for this server."))
|
||||
|
||||
@joindm.command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def message(self, ctx, *, message=None):
|
||||
# Set the custom join DM message
|
||||
if message is None:
|
||||
await ctx.send(view=CV2("❌ Error", "Please provide a custom join DM message."))
|
||||
else:
|
||||
self.joindm_messages[str(ctx.guild.id)] = message
|
||||
self.save_joindm_messages()
|
||||
await ctx.send(view=CV2("✅ Success", "Custom join DM message set successfully."))
|
||||
|
||||
@joindm.command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def enable(self, ctx):
|
||||
# Enable the join DM module
|
||||
self.bot.add_listener(self.on_member_join, 'on_member_join')
|
||||
await ctx.send(view=CV2("✅ Success", "Join DM module enabled. Custom DM will be sent to new members."))
|
||||
|
||||
@joindm.command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def disable(self, ctx):
|
||||
# Disable the join DM module
|
||||
self.bot.remove_listener(self.on_member_join)
|
||||
await ctx.send(view=CV2("✅ Success", "Join DM module disabled. Custom DM will not be sent to new members."))
|
||||
|
||||
@joindm.command()
|
||||
async def test(self, ctx):
|
||||
# Send a test join DM to the author of the command
|
||||
guild_id = str(ctx.guild.id)
|
||||
if guild_id in self.joindm_messages:
|
||||
message = self.joindm_messages[guild_id]
|
||||
server_name = ctx.guild.name
|
||||
join_dm_message = f"{message}\n\n ``Sent from {server_name} `` "
|
||||
await ctx.send(view=CV2("✅ Test Sent", "Test Join DM Sent To Your Dm"))
|
||||
await ctx.message.add_reaction(BLOBPART)
|
||||
await ctx.author.send(view=CV2("👋 Welcome", join_dm_message))
|
||||
else:
|
||||
await ctx.send(view=CV2("❌ Error", "No custom join DM message has been set for this server."))
|
||||
|
||||
async def on_member_join(self, member):
|
||||
|
||||
guild_id = str(member.guild.id)
|
||||
if guild_id in self.joindm_messages:
|
||||
message = self.joindm_messages[guild_id]
|
||||
dm_channel = await member.create_dm()
|
||||
server_name = member.guild.name
|
||||
join_dm_message = f"{message}\n\n``Sent from {server_name} ``"
|
||||
await dm_channel.send(view=CV2("👋 Welcome", join_dm_message))
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(joindm(bot))
|
||||
3151
bot/cogs/commands/leveling.py
Normal file
3151
bot/cogs/commands/leveling.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
bot/cogs/commands/leveling_original.py
Normal file
BIN
bot/cogs/commands/leveling_original.py
Normal file
Binary file not shown.
2985
bot/cogs/commands/logging.py
Normal file
2985
bot/cogs/commands/logging.py
Normal file
File diff suppressed because it is too large
Load Diff
260
bot/cogs/commands/map.py
Normal file
260
bot/cogs/commands/map.py
Normal file
@@ -0,0 +1,260 @@
|
||||
import discord
|
||||
from utils.emoji import DELETE
|
||||
from discord.ext import commands
|
||||
from discord import ui, ButtonStyle, SelectOption
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, ActionRow, MediaGallery
|
||||
import requests
|
||||
import asyncio
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container
|
||||
from utils.config import *
|
||||
class MapView(LayoutView):
|
||||
def __init__(self, bot, location, ctx):
|
||||
super().__init__(timeout=None)
|
||||
self.bot = bot
|
||||
self.location = location
|
||||
self.ctx = ctx
|
||||
self.zoom_level = 14
|
||||
self.map_style = 'map'
|
||||
self.map_size = '1200,900'
|
||||
self.coordinates = self.get_coordinates(location)
|
||||
self.latitude, self.longitude = None, None
|
||||
if self.coordinates != (None, None):
|
||||
self.latitude, self.longitude = float(self.coordinates[0]), float(self.coordinates[1])
|
||||
|
||||
self.update_map()
|
||||
|
||||
b_left = ui.Button(label="", emoji="⬅️", style=ButtonStyle.secondary)
|
||||
b_left.callback = self.move_left
|
||||
b_up = ui.Button(label="", emoji="⬆️", style=ButtonStyle.secondary)
|
||||
b_up.callback = self.move_up
|
||||
b_delete = ui.Button(label="", emoji=DELETE, style=ButtonStyle.danger)
|
||||
b_delete.callback = self.delete_embed
|
||||
b_down = ui.Button(label="", emoji="⬇️", style=ButtonStyle.secondary)
|
||||
b_down.callback = self.move_down
|
||||
b_right = ui.Button(label="", emoji="➡️", style=ButtonStyle.secondary)
|
||||
b_right.callback = self.move_right
|
||||
|
||||
self.add_item(ActionRow(b_left, b_up, b_delete, b_down, b_right))
|
||||
|
||||
b_zin = ui.Button(label="Zoom In", style=ButtonStyle.primary)
|
||||
b_zin.callback = self.zoom_in
|
||||
b_zout = ui.Button(label="Zoom Out", style=ButtonStyle.primary)
|
||||
b_zout.callback = self.zoom_out
|
||||
b_coords = ui.Button(label="Enter Coordinates", style=ButtonStyle.primary)
|
||||
b_coords.callback = self.enter_coordinates
|
||||
b_addr = ui.Button(label="Enter Address", style=ButtonStyle.success)
|
||||
b_addr.callback = self.enter_address
|
||||
|
||||
self.add_item(ActionRow(b_zin, b_zout, b_coords, b_addr))
|
||||
|
||||
self.add_item(ActionRow(MapStyleSelect(self)))
|
||||
self.add_item(ActionRow(MapSizeSelect(self)))
|
||||
|
||||
self.build_ui()
|
||||
|
||||
def build_ui(self):
|
||||
|
||||
container = build_container(
|
||||
TextDisplay(f"**Map of {self.location}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
f"🌐 **[Open in Webpage](https://www.openstreetmap.org/?mlat={self.latitude}&mlon={self.longitude}&zoom={self.zoom_level})**\n"
|
||||
f"🔍 **Current Zoom Level:** {str(self.zoom_level)}\n"
|
||||
f"🗺️ **Map Style:** {self.map_style}\n"
|
||||
f"📏 **Map Size:** {self.map_size}\n"
|
||||
f"📍 **Current Coordinates:** {self.latitude}, {self.longitude}"
|
||||
)
|
||||
)
|
||||
|
||||
gallery = MediaGallery()
|
||||
gallery.add_item(media=self.map_url)
|
||||
container.add_item(gallery)
|
||||
|
||||
|
||||
self.children = [c for c in self.children if not isinstance(c, type(container))]
|
||||
self.add_item(container)
|
||||
|
||||
|
||||
def get_coordinates(self, location):
|
||||
try:
|
||||
headers = {'User-Agent': f'{BRAND_NAME} Bot (https://codexdevs.in)'}
|
||||
response = requests.get(f'https://nominatim.openstreetmap.org/search?q={location}&format=json', headers=headers)
|
||||
response.raise_for_status()
|
||||
data = response.json()[0]
|
||||
return data['lat'], data['lon']
|
||||
except (requests.RequestException, IndexError) as e:
|
||||
print(f"Failed to get coordinates: {e}")
|
||||
return None, None
|
||||
|
||||
def update_map(self):
|
||||
if self.latitude is None or self.longitude is None:
|
||||
return
|
||||
self.map_url = f'https://www.mapquestapi.com/staticmap/v5/map?key=E2SaL3qiTpXQ43nxZFBp0wzEnBI6pqbG¢er={self.latitude},{self.longitude}&zoom={self.zoom_level}&size={self.map_size}&type={self.map_style}'
|
||||
self.build_ui()
|
||||
|
||||
async def update_embed(self, interaction: discord.Interaction):
|
||||
if self.latitude is None or self.longitude is None:
|
||||
await interaction.response.send_message("Failed to retrieve map data. Please try again.", ephemeral=True)
|
||||
return
|
||||
|
||||
try:
|
||||
await interaction.message.edit(view=self)
|
||||
except Exception as e:
|
||||
await interaction.response.send_message(f"Error updating message: {e}", ephemeral=True)
|
||||
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||
if interaction.user != self.ctx.author:
|
||||
await interaction.response.send_message(
|
||||
view=CV2("⚠️ Access Denied", "Sorry only the requested author can control this"),
|
||||
ephemeral=True
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
async def move_left(self, interaction: discord.Interaction):
|
||||
if self.longitude is not None:
|
||||
self.longitude -= 0.01
|
||||
self.update_map()
|
||||
await self.update_embed(interaction)
|
||||
await interaction.response.send_message(view=CV2("✅ Map Updated", "Moved left."), ephemeral=True)
|
||||
|
||||
async def move_up(self, interaction: discord.Interaction):
|
||||
if self.latitude is not None:
|
||||
self.latitude += 0.01
|
||||
self.update_map()
|
||||
await self.update_embed(interaction)
|
||||
await interaction.response.send_message(view=CV2("✅ Map Updated", "Moved up."), ephemeral=True)
|
||||
|
||||
async def delete_embed(self, interaction: discord.Interaction):
|
||||
try:
|
||||
await interaction.message.delete()
|
||||
except Exception as e:
|
||||
await interaction.response.send_message(f"Error deleting message: {e}", ephemeral=True)
|
||||
|
||||
async def move_down(self, interaction: discord.Interaction):
|
||||
if self.latitude is not None:
|
||||
self.latitude -= 0.01
|
||||
self.update_map()
|
||||
await self.update_embed(interaction)
|
||||
await interaction.response.send_message(view=CV2("✅ Map Updated", "Moved down."), ephemeral=True)
|
||||
|
||||
async def move_right(self, interaction: discord.Interaction):
|
||||
if self.longitude is not None:
|
||||
self.longitude += 0.01
|
||||
self.update_map()
|
||||
await self.update_embed(interaction)
|
||||
await interaction.response.send_message(view=CV2("✅ Map Updated", "Moved right."), ephemeral=True)
|
||||
|
||||
async def zoom_in(self, interaction: discord.Interaction):
|
||||
print("Zooming in")
|
||||
self.zoom_level = min(self.zoom_level + 1, 18)
|
||||
self.update_map()
|
||||
await self.update_embed(interaction)
|
||||
await interaction.response.send_message(view=CV2("✅ Map Updated", "Zoomed in."), ephemeral=True)
|
||||
|
||||
async def zoom_out(self, interaction: discord.Interaction):
|
||||
print("Zooming out")
|
||||
self.zoom_level = max(self.zoom_level - 1, 0)
|
||||
self.update_map()
|
||||
await self.update_embed(interaction)
|
||||
await interaction.response.send_message(view=CV2("✅ Map Updated", "Zoomed Out."), ephemeral=True)
|
||||
|
||||
async def enter_coordinates(self, interaction: discord.Interaction):
|
||||
await interaction.response.send_message("Please enter the coordinates (latitude, longitude):", ephemeral=True)
|
||||
|
||||
def check(message):
|
||||
return message.author == interaction.user and message.channel == interaction.channel
|
||||
|
||||
try:
|
||||
coords_msg = await self.bot.wait_for('message', check=check, timeout=60)
|
||||
coords = coords_msg.content.split(',')
|
||||
if len(coords) == 2:
|
||||
self.latitude, self.longitude = float(coords[0].strip()), float(coords[1].strip())
|
||||
self.update_map()
|
||||
await self.update_embed(interaction)
|
||||
await interaction.followup.send(view=CV2("✅ Map Updated", "Coordinates updated."), ephemeral=True)
|
||||
else:
|
||||
await interaction.followup.send("Invalid coordinates format. Please enter in the format 'latitude, longitude'.", ephemeral=True)
|
||||
except asyncio.TimeoutError:
|
||||
await interaction.followup.send("You took too long to respond. Please try again.", ephemeral=True)
|
||||
|
||||
async def enter_address(self, interaction: discord.Interaction):
|
||||
await interaction.response.send_message("Please enter the address:", ephemeral=True)
|
||||
|
||||
def check(message):
|
||||
return message.author == interaction.user and message.channel == interaction.channel
|
||||
|
||||
try:
|
||||
address_msg = await self.bot.wait_for('message', check=check, timeout=60)
|
||||
address = address_msg.content
|
||||
self.coordinates = self.get_coordinates(address)
|
||||
if self.coordinates == (None, None):
|
||||
await interaction.followup.send("Failed to retrieve coordinates for the address. Please try again.", ephemeral=True)
|
||||
else:
|
||||
self.latitude, self.longitude = float(self.coordinates[0]), float(self.coordinates[1])
|
||||
self.location = address
|
||||
self.update_map()
|
||||
await self.update_embed(interaction)
|
||||
await interaction.followup.send(view=CV2("✅ Map Updated", "Address updated."), ephemeral=True)
|
||||
except asyncio.TimeoutError:
|
||||
await interaction.followup.send("You took too long to respond. Please try again.", ephemeral=True)
|
||||
|
||||
|
||||
class MapStyleSelect(ui.Select):
|
||||
def __init__(self, map_view):
|
||||
super().__init__(placeholder='Select Map Style')
|
||||
self.map_view = map_view
|
||||
options = [
|
||||
SelectOption(label='Map', value='map'),
|
||||
SelectOption(label='Satellite', value='sat'),
|
||||
SelectOption(label='Hybrid', value='hyb'),
|
||||
SelectOption(label='Light', value='light'),
|
||||
SelectOption(label='Dark', value='dark'),
|
||||
]
|
||||
self.options = options
|
||||
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
print(f"Changing map style to {self.values[0]}")
|
||||
self.map_view.map_style = self.values[0]
|
||||
self.map_view.update_map()
|
||||
await self.map_view.update_embed(interaction)
|
||||
await interaction.response.send_message(view=CV2("✅ Map Updated", "Map style updated successfully."), ephemeral=True)
|
||||
|
||||
class MapSizeSelect(ui.Select):
|
||||
def __init__(self, map_view):
|
||||
super().__init__(placeholder='Select Map Size')
|
||||
self.map_view = map_view
|
||||
options = [
|
||||
SelectOption(label='400x300', value='400,300'),
|
||||
SelectOption(label='800x600', value='800,600'),
|
||||
SelectOption(label='1200x900', value='1200,900')
|
||||
]
|
||||
self.options = options
|
||||
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
print(f"Changing map size to {self.values[0]}")
|
||||
self.map_view.map_size = self.values[0]
|
||||
self.map_view.update_map()
|
||||
await self.map_view.update_embed(interaction)
|
||||
await interaction.response.send_message(view=CV2("✅ Map Updated", "Map size updated successfully."), ephemeral=True)
|
||||
|
||||
|
||||
class Map(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.hybrid_command(name="map", help="Shows a map of a location", usage="<location>", description="Shows a map of a location")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
async def map(self, ctx, *, location: str):
|
||||
view = MapView(self.bot, location, ctx)
|
||||
if view.coordinates == (None, None):
|
||||
await ctx.send(view=CV2("❌ Error", "Failed to retrieve coordinates for the location. Please try again."))
|
||||
return
|
||||
await ctx.send(view=view)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Map(bot))
|
||||
94
bot/cogs/commands/messages.py
Normal file
94
bot/cogs/commands/messages.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import discord
|
||||
from utils.emoji import ARROWRED
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from discord.ext import commands
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
from utils.config import *
|
||||
|
||||
|
||||
class MessagesView(LayoutView):
|
||||
def __init__(self, member, total, today_count, daily_average, author):
|
||||
super().__init__(timeout=None)
|
||||
|
||||
self.add_item(
|
||||
Container(
|
||||
TextDisplay(f"**Messages Stats for {member.display_name}**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(
|
||||
f"**User**: {member.mention}\n\n"
|
||||
f"**Daily Messages**: {daily_average}\n"
|
||||
f"**Today Messages**: {today_count}\n"
|
||||
f"**Total Messages**: {total}\n\n"
|
||||
f"**{ARROWRED}Upgrade Your Experience With [{BRAND_NAME} Noprefix](https://discord.gg/codexdev)**"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Messages(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.bot or not message.guild:
|
||||
return
|
||||
|
||||
conn = sqlite3.connect("db/messages.db")
|
||||
c = conn.cursor()
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
guild_id INTEGER,
|
||||
user_id INTEGER,
|
||||
date TEXT,
|
||||
count INTEGER
|
||||
)
|
||||
""")
|
||||
|
||||
today = datetime.utcnow().strftime("%Y-%m-%d")
|
||||
c.execute(
|
||||
"SELECT count FROM messages WHERE guild_id = ? AND user_id = ? AND date = ?",
|
||||
(message.guild.id, message.author.id, today),
|
||||
)
|
||||
result = c.fetchone()
|
||||
|
||||
if result:
|
||||
c.execute(
|
||||
"UPDATE messages SET count = count + 1 WHERE guild_id = ? AND user_id = ? AND date = ?",
|
||||
(message.guild.id, message.author.id, today),
|
||||
)
|
||||
else:
|
||||
c.execute(
|
||||
"INSERT INTO messages (guild_id, user_id, date, count) VALUES (?, ?, ?, 1)",
|
||||
(message.guild.id, message.author.id, today),
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
@commands.command(name="messages", aliases=["msg"])
|
||||
async def messages(self, ctx, member: discord.Member = None):
|
||||
member = member or ctx.author
|
||||
today = datetime.utcnow().strftime("%Y-%m-%d")
|
||||
|
||||
conn = sqlite3.connect("db/messages.db")
|
||||
c = conn.cursor()
|
||||
c.execute(
|
||||
"SELECT date, count FROM messages WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, member.id),
|
||||
)
|
||||
data = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
total = sum(row[1] for row in data)
|
||||
today_count = sum(row[1] for row in data if row[0] == today)
|
||||
unique_days = set(row[0] for row in data)
|
||||
daily_average = round(total / len(unique_days), 2) if unique_days else 0
|
||||
|
||||
view = MessagesView(member, total, today_count, daily_average, ctx.author)
|
||||
await ctx.send(view=view)
|
||||
|
||||
|
||||
async def setup(client):
|
||||
await client.add_cog(Messages(client))
|
||||
269
bot/cogs/commands/minecraft.py
Normal file
269
bot/cogs/commands/minecraft.py
Normal file
@@ -0,0 +1,269 @@
|
||||
import discord
|
||||
from discord.ext import commands, tasks
|
||||
from discord import app_commands, ui, File
|
||||
from mcstatus import JavaServer, BedrockServer
|
||||
import aiosqlite
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import io
|
||||
import asyncio
|
||||
import base64
|
||||
from utils.emoji import SUCCESS, ERROR, WARNING_UNICODE, CLOCK, REFRESH, JAVA_COFFEE
|
||||
|
||||
# --- Updated Asset Paths ---
|
||||
ASSETS_DIR = "assets"
|
||||
FONT_PATH = os.path.join(ASSETS_DIR, "fonts", "minecraft.ttf")
|
||||
BACKGROUND_PATH = os.path.join(ASSETS_DIR, "background", "background.png")
|
||||
DB_PATH = "db/minecraft.db"
|
||||
|
||||
# --- Other Constants ---
|
||||
REFRESH_COOLDOWN_SECONDS = 30
|
||||
CANVAS_WIDTH = 800
|
||||
CANVAS_HEIGHT = 125
|
||||
|
||||
# --- Emojis (imported from utils.emoji) ---
|
||||
EMOJI_STATUS_ONLINE = "🟢"
|
||||
EMOJI_STATUS_OFFLINE = "🔴"
|
||||
EMOJI_PLAYERS = "👥"
|
||||
EMOJI_INFO = "💻"
|
||||
EMOJI_SUCCESS = SUCCESS
|
||||
EMOJI_ERROR = ERROR
|
||||
EMOJI_WARNING = WARNING_UNICODE
|
||||
EMOJI_CLOCK = CLOCK
|
||||
EMOJI_REFRESH = REFRESH
|
||||
EMOJI_JAVA = JAVA_COFFEE
|
||||
EMOJI_BEDROCK = "📱"
|
||||
EMOJI_PROXY = "🔌"
|
||||
|
||||
class SetupModal(ui.Modal, title="Minecraft Server Setup"):
|
||||
server_ip = ui.TextInput(label="Enter Server IP Address", placeholder="e.g., play.hypixel.net", required=True)
|
||||
server_port = ui.TextInput(label="Enter Server Port (Optional)", placeholder="Leave blank for default ports", required=False)
|
||||
|
||||
def __init__(self, cog):
|
||||
super().__init__(timeout=None)
|
||||
self.cog = cog
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
ip, port_str = self.server_ip.value.strip(), self.server_port.value.strip()
|
||||
port = int(port_str) if port_str.isdigit() else None
|
||||
detected_type, status, _ = await self.cog.auto_detect_server(ip, port)
|
||||
if not detected_type or not status or not status['online']:
|
||||
await interaction.followup.send(f"{EMOJI_ERROR} Could not reach or identify the server. Check IP/Port and try again.", ephemeral=True)
|
||||
return
|
||||
embed, file, view = await self.cog.generate_response(interaction.guild, detected_type, ip, port, interaction.user.id)
|
||||
sent_message = await interaction.channel.send(embed=embed, file=file, view=view)
|
||||
await self.cog.save_status_message(interaction.guild.id, interaction.user.id, interaction.channel.id, sent_message.id, detected_type, ip, port)
|
||||
await interaction.followup.send(f"{EMOJI_SUCCESS} Auto-updating status for `{ip}` set up!", ephemeral=True)
|
||||
|
||||
class MinecraftView(ui.View):
|
||||
def __init__(self, bot, server_type, ip, port, user_id):
|
||||
super().__init__(timeout=None)
|
||||
self.bot, self.server_type, self.ip, self.port, self.user_id = bot, server_type, ip, port, user_id
|
||||
self.last_refresh = None
|
||||
|
||||
@ui.button(label="Refresh", style=discord.ButtonStyle.secondary, custom_id="refresh_button", emoji=EMOJI_REFRESH)
|
||||
async def refresh_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if interaction.user.id != self.user_id:
|
||||
return await interaction.response.send_message(f"{EMOJI_ERROR} You can't refresh this panel.", ephemeral=True)
|
||||
now = datetime.utcnow()
|
||||
if self.last_refresh and (now - self.last_refresh).total_seconds() < REFRESH_COOLDOWN_SECONDS:
|
||||
return await interaction.response.send_message(f"{EMOJI_CLOCK} On cooldown. Wait `{REFRESH_COOLDOWN_SECONDS}` seconds.", ephemeral=True)
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
cog = self.bot.get_cog("Minecraft")
|
||||
embed, file, view = await cog.generate_response(interaction.guild, self.server_type, self.ip, self.port, self.user_id)
|
||||
await interaction.message.edit(embed=embed, attachments=[file] if file else [], view=view)
|
||||
await interaction.followup.send(f"{EMOJI_SUCCESS} Status refreshed.", ephemeral=True)
|
||||
self.last_refresh = now
|
||||
|
||||
class Minecraft(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(FONT_PATH), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(BACKGROUND_PATH), exist_ok=True)
|
||||
self.bot.loop.create_task(self.init_db())
|
||||
self.refresh_all_statuses.start()
|
||||
|
||||
async def init_db(self):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS mc_status_messages (
|
||||
guild_id INTEGER PRIMARY KEY, user_id INTEGER, channel_id INTEGER,
|
||||
message_id INTEGER, server_type TEXT, server_ip TEXT, server_port INTEGER
|
||||
)""")
|
||||
await db.commit()
|
||||
|
||||
def clean_motd(self, motd: str) -> str:
|
||||
return re.sub(r'§.', '', motd).strip()
|
||||
|
||||
async def auto_detect_server(self, ip: str, port: int = None):
|
||||
try:
|
||||
java_port = port or 25565
|
||||
server = JavaServer(ip, java_port)
|
||||
status = await asyncio.wait_for(server.async_status(tries=1), timeout=5)
|
||||
version_name = status.version.name.lower()
|
||||
detected_type = 'velocity' if 'velocity' in version_name else 'bungeecord' if 'bungee' in version_name else 'java'
|
||||
favicon = getattr(status, 'favicon', None)
|
||||
motd_raw = status.description
|
||||
full_text = "".join(part.get('text', '') for part in motd_raw.get('extra', [])) or motd_raw.get('text', '') if isinstance(motd_raw, dict) else str(motd_raw)
|
||||
status_data = {
|
||||
"motd": self.clean_motd(full_text) or "A Minecraft Server",
|
||||
"players_online": status.players.online, "players_max": status.players.max,
|
||||
"players_sample": status.players.sample, "version": status.version.name, "online": True}
|
||||
return detected_type, status_data, favicon
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
bedrock_port = port or 19132
|
||||
server = BedrockServer(ip, bedrock_port)
|
||||
status = await asyncio.wait_for(server.async_status(tries=1), timeout=5)
|
||||
status_data = {
|
||||
"motd": self.clean_motd(status.motd) or "A Minecraft Server",
|
||||
"players_online": status.players.online, "players_max": status.players.max,
|
||||
"players_sample": None, "version": status.version.name, "online": True}
|
||||
return 'bedrock', status_data, None
|
||||
except Exception:
|
||||
pass
|
||||
return None, {"online": False}, None
|
||||
|
||||
async def create_status_image(self, status_data, ip, port, server_type, server_icon_b64):
|
||||
try:
|
||||
bg = Image.open(BACKGROUND_PATH).convert("RGBA")
|
||||
img = Image.new("RGBA", (CANVAS_WIDTH, CANVAS_HEIGHT))
|
||||
for i in range(0, img.width, bg.width):
|
||||
for j in range(0, img.height, bg.height): img.paste(bg, (i, j))
|
||||
draw = ImageDraw.Draw(img)
|
||||
font_large, font_medium, font_small = ImageFont.truetype(FONT_PATH, 24), ImageFont.truetype(FONT_PATH, 20), ImageFont.truetype(FONT_PATH, 16)
|
||||
if server_icon_b64:
|
||||
icon_data = base64.b64decode(server_icon_b64.split(',')[-1])
|
||||
icon_img = Image.open(io.BytesIO(icon_data)).convert("RGBA").resize((80, 80), Image.Resampling.LANCZOS)
|
||||
img.paste(icon_img, (20, (CANVAS_HEIGHT - 80) // 2), icon_img)
|
||||
default_port = 25565 if server_type != 'bedrock' else 19132
|
||||
full_ip = f"{ip}:{port}" if port and port != default_port else ip
|
||||
draw.text((120, 15), f"IP: {full_ip}", font=font_medium, fill=(255, 255, 255))
|
||||
player_text = f"{status_data.get('players_online', 0)}/{status_data.get('players_max', 0)}"
|
||||
bbox = draw.textbbox((0, 0), player_text, font=font_medium)
|
||||
draw.text((CANVAS_WIDTH - (bbox[2] - bbox[0]) - 20, 15), player_text, font=font_medium, fill=(255, 255, 255))
|
||||
motd = (status_data.get('motd', 'Offline') or "A Minecraft Server").split('')[0]
|
||||
bbox = draw.textbbox((0,0), motd, font=font_large)
|
||||
draw.text(((CANVAS_WIDTH - (bbox[2] - bbox[0])) // 2, 60), motd, font=font_large, fill=(0, 255, 255))
|
||||
power_text = "Powered by STACY"
|
||||
bbox = draw.textbbox((0,0), power_text, font=font_small)
|
||||
draw.text((CANVAS_WIDTH - (bbox[2] - bbox[0]) - 15, CANVAS_HEIGHT - 25), power_text, font=font_small, fill=(170, 170, 170))
|
||||
buffer = io.BytesIO()
|
||||
img.save(buffer, "PNG")
|
||||
buffer.seek(0)
|
||||
return File(buffer, filename="status.png")
|
||||
except FileNotFoundError: return None
|
||||
except Exception: return None
|
||||
|
||||
async def generate_response(self, guild, server_type, ip, port, user_id):
|
||||
_, status, favicon = await self.auto_detect_server(ip, port)
|
||||
if not status: status = {'online': False}
|
||||
file = await self.create_status_image(status, ip, port, server_type, favicon)
|
||||
is_online = status.get("online", False)
|
||||
embed = discord.Embed(title=f"{EMOJI_STATUS_ONLINE if is_online else EMOJI_STATUS_OFFLINE} Minecraft Server Status", color=discord.Color.dark_green() if is_online else discord.Color.red())
|
||||
|
||||
default_port = 25565 if server_type != 'bedrock' else 19132
|
||||
full_ip = f"{ip}:{port}" if port and port != default_port else ip
|
||||
type_emoji_map = {'java': EMOJI_JAVA, 'bedrock': EMOJI_BEDROCK, 'velocity': EMOJI_PROXY, 'bungeecord': EMOJI_PROXY}
|
||||
type_emoji = type_emoji_map.get(server_type, EMOJI_INFO)
|
||||
|
||||
info_value = (f"**IP:** `{full_ip}`\n"
|
||||
f"**Type:** {type_emoji} {server_type.capitalize()}\n"
|
||||
f"**Version:** `{status.get('version', 'Unknown')}`\n")
|
||||
embed.add_field(name=f"{EMOJI_INFO} Server Information", value=info_value, inline=False)
|
||||
|
||||
players_online = status.get('players_online', 0)
|
||||
players_max = status.get('players_max', 0)
|
||||
player_list_str = f"**Online:** `{players_online}/{players_max}`\n"
|
||||
player_sample = status.get('players_sample')
|
||||
if player_sample:
|
||||
player_names = [player.name for player in player_sample]
|
||||
player_list_str += "\n".join(f"`{i+1}.` {name}" for i, name in enumerate(player_names[:10]))
|
||||
if len(player_names) > 10:
|
||||
player_list_str += f"*...and {len(player_names) - 10} more*"
|
||||
elif is_online:
|
||||
player_list_str += "*Player list is hidden or unavailable.*"
|
||||
|
||||
embed.add_field(name=f"{EMOJI_PLAYERS} Players", value=player_list_str, inline=False)
|
||||
|
||||
if file:
|
||||
embed.set_image(url="attachment://status.png")
|
||||
|
||||
embed.set_footer(text="Last updated")
|
||||
embed.timestamp = datetime.utcnow()
|
||||
view = MinecraftView(self.bot, server_type, ip, port, user_id)
|
||||
return embed, file, view
|
||||
|
||||
async def save_status_message(self, guild_id, user_id, channel_id, message_id, server_type, ip, port):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
INSERT INTO mc_status_messages (guild_id, user_id, channel_id, message_id, server_type, server_ip, server_port)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(guild_id) DO UPDATE SET
|
||||
user_id=excluded.user_id, channel_id=excluded.channel_id, message_id=excluded.message_id,
|
||||
server_type=excluded.server_type, server_ip=excluded.server_ip, server_port=excluded.server_port
|
||||
""", (guild_id, user_id, channel_id, message_id, server_type, ip, port))
|
||||
await db.commit()
|
||||
|
||||
async def delete_status_message(self, guild_id):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
cursor = await db.execute("SELECT channel_id, message_id FROM mc_status_messages WHERE guild_id = ?", (guild_id,))
|
||||
if row := await cursor.fetchone():
|
||||
try:
|
||||
channel = await self.bot.fetch_channel(row[0]); msg = await channel.fetch_message(row[1]); await msg.delete()
|
||||
except (discord.NotFound, discord.Forbidden): pass
|
||||
await db.execute("DELETE FROM mc_status_messages WHERE guild_id = ?", (guild_id,)); await db.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
@tasks.loop(minutes=2)
|
||||
async def refresh_all_statuses(self):
|
||||
await self.bot.wait_until_ready()
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT guild_id, user_id, channel_id, message_id, server_type, server_ip, server_port FROM mc_status_messages") as cursor:
|
||||
async for row in cursor:
|
||||
try:
|
||||
guild = self.bot.get_guild(row[0])
|
||||
if not guild: continue
|
||||
channel = guild.get_channel(row[2])
|
||||
if not channel: continue
|
||||
message = await channel.fetch_message(row[3])
|
||||
embed, file, view = await self.generate_response(guild, row[4], row[5], row[6], row[1])
|
||||
await message.edit(embed=embed, attachments=[file] if file else [], view=view)
|
||||
await asyncio.sleep(2)
|
||||
except discord.NotFound: await self.delete_status_message(row[0])
|
||||
except Exception as e: print(f"Auto-refresh error for guild {row[0]}: {e}")
|
||||
|
||||
minecraft = app_commands.Group(name="minecraft", description="Commands for Minecraft server status.")
|
||||
|
||||
@minecraft.command(name="setup", description="Set up the auto-updating Minecraft server status.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
async def setup_slash(self, interaction: discord.Interaction):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
if await (await db.execute("SELECT 1 FROM mc_status_messages WHERE guild_id = ?", (interaction.guild.id,))).fetchone():
|
||||
return await interaction.response.send_message(f"{EMOJI_WARNING} Setup already exists. Use `/minecraft reset`.", ephemeral=True)
|
||||
await interaction.response.send_modal(SetupModal(self))
|
||||
|
||||
@minecraft.command(name="reset", description="Remove the Minecraft server status setup.")
|
||||
@app_commands.checks.has_permissions(manage_guild=True)
|
||||
async def reset_slash(self, interaction: discord.Interaction):
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
removed = await self.delete_status_message(interaction.guild.id)
|
||||
await interaction.followup.send(f"{EMOJI_SUCCESS} Minecraft status panel removed." if removed else f"{EMOJI_WARNING} No setup found.", ephemeral=True)
|
||||
|
||||
@minecraft.command(name="status", description="Get a one-time status of the configured server.")
|
||||
async def status_slash(self, interaction: discord.Interaction):
|
||||
await interaction.response.defer(ephemeral=False)
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
row = await (await db.execute("SELECT server_type, server_ip, server_port, user_id FROM mc_status_messages WHERE guild_id = ?", (interaction.guild.id,))).fetchone()
|
||||
if not row:
|
||||
return await interaction.followup.send(f"{EMOJI_WARNING} No server configured. Use `/minecraft setup`.", ephemeral=True)
|
||||
embed, file, _ = await self.generate_response(interaction.guild, row[0], row[1], row[2], row[3])
|
||||
await interaction.followup.send(embed=embed, file=file, ephemeral=False)
|
||||
|
||||
async def setup(bot: commands.Bot):
|
||||
await bot.add_cog(Minecraft(bot))
|
||||
68
bot/cogs/commands/msgpack.py
Normal file
68
bot/cogs/commands/msgpack.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
|
||||
class Messagespack(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(name="addmessages", aliases=["addmsg"])
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def addmessages(self, ctx, member: discord.Member, amount: int):
|
||||
if amount <= 0:
|
||||
return await ctx.send("Amount must be greater than 0.")
|
||||
|
||||
today = datetime.utcnow().strftime("%Y-%m-%d")
|
||||
conn = sqlite3.connect("db/messages.db")
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute("SELECT count FROM messages WHERE guild_id = ? AND user_id = ? AND date = ?",
|
||||
(ctx.guild.id, member.id, today))
|
||||
result = c.fetchone()
|
||||
|
||||
if result:
|
||||
c.execute("UPDATE messages SET count = count + ? WHERE guild_id = ? AND user_id = ? AND date = ?",
|
||||
(amount, ctx.guild.id, member.id, today))
|
||||
else:
|
||||
c.execute("INSERT INTO messages (guild_id, user_id, date, count) VALUES (?, ?, ?, ?)",
|
||||
(ctx.guild.id, member.id, today, amount))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
await ctx.send(f"Added {amount} messages to {member.mention} for today.")
|
||||
|
||||
@commands.command(name="removemessages", aliases=["removemsg"])
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def removemessages(self, ctx, member: discord.Member, amount: int):
|
||||
if amount <= 0:
|
||||
return await ctx.send("Amount must be greater than 0.")
|
||||
|
||||
today = datetime.utcnow().strftime("%Y-%m-%d")
|
||||
conn = sqlite3.connect("db/messages.db")
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute("SELECT count FROM messages WHERE guild_id = ? AND user_id = ? AND date = ?",
|
||||
(ctx.guild.id, member.id, today))
|
||||
result = c.fetchone()
|
||||
|
||||
if result:
|
||||
new_count = max(0, result[0] - amount)
|
||||
c.execute("UPDATE messages SET count = ? WHERE guild_id = ? AND user_id = ? AND date = ?",
|
||||
(new_count, ctx.guild.id, member.id, today))
|
||||
conn.commit()
|
||||
await ctx.send(f"Removed {amount} messages from {member.mention} for today.")
|
||||
else:
|
||||
await ctx.send(f"{member.mention} has no messages recorded for today.")
|
||||
conn.close()
|
||||
|
||||
@commands.command(name="clearmessage", aliases=["clearmsg"])
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def clearmessage(self, ctx, member: discord.Member):
|
||||
conn = sqlite3.connect("db/messages.db")
|
||||
c = conn.cursor()
|
||||
c.execute("DELETE FROM messages WHERE guild_id = ? AND user_id = ?",
|
||||
(ctx.guild.id, member.id))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
await ctx.send(f"All messages cleared for {member.mention}.")
|
||||
963
bot/cogs/commands/music.py
Normal file
963
bot/cogs/commands/music.py
Normal file
@@ -0,0 +1,963 @@
|
||||
import random
|
||||
import discord
|
||||
from utils.emoji import FORWARD, ICONLOAD, ICONS_MUSIC, ICONS_PAUSE, ICONS_WARNING_ALT1, MUSICSTOP_ICONS, MUSIC_ALT1, MUTE, REWIND, REWIND_ALT1, SHUFFLE, SKIP, TICK, WARNING, ZMUSICPAUSE, ZPLUS, ZUNMUTE
|
||||
from discord.ext import commands, tasks
|
||||
import datetime
|
||||
from discord.ui import Button, View, LayoutView, TextDisplay, Separator, Container, ActionRow
|
||||
import wavelink
|
||||
from wavelink.enums import TrackSource
|
||||
from utils import Paginator, DescriptionEmbedPaginator
|
||||
from core import Cog, zyrox, Context
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageOps
|
||||
import io
|
||||
import aiohttp
|
||||
from typing import cast
|
||||
import asyncio
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2, build_container, CV2Embed
|
||||
track_histories = {}
|
||||
import base64
|
||||
import re
|
||||
from utils.config import *
|
||||
|
||||
SPOTIFY_TRACK_REGEX = r"https?://open\.spotify\.com/track/([a-zA-Z0-9]+)"
|
||||
SPOTIFY_PLAYLIST_REGEX = r"https?://open\.spotify\.com/playlist/([a-zA-Z0-9]+)"
|
||||
SPOTIFY_ALBUM_REGEX = r"https?://open\.spotify\.com/album/([a-zA-Z0-9]+)"
|
||||
|
||||
class SpotifyAPI:
|
||||
BASE_URL = "https://api.spotify.com/v1"
|
||||
|
||||
def __init__(self, client_id, client_secret):
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self.token = None
|
||||
|
||||
async def get_token(self):
|
||||
auth_url = "https://accounts.spotify.com/api/token"
|
||||
auth_value = base64.b64encode(f"{self.client_id}:{self.client_secret}".encode('utf-8')).decode('utf-8')
|
||||
headers = {"Authorization": f"Basic {auth_value}"}
|
||||
data = {"grant_type": "client_credentials"}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(auth_url, headers=headers, data=data) as response:
|
||||
text = await response.text()
|
||||
if response.status != 200:
|
||||
raise Exception(f"Failed to fetch token: {response.status}, response: {text}")
|
||||
self.token = (await response.json()).get("access_token")
|
||||
|
||||
async def get(self, endpoint, params=None):
|
||||
retries = 2
|
||||
for attempt in range(retries):
|
||||
if not self.token or attempt > 0:
|
||||
await self.get_token()
|
||||
|
||||
url = f"{self.BASE_URL}/{endpoint}"
|
||||
headers = {"Authorization": f"Bearer {self.token}"}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url, headers=headers, params=params) as response:
|
||||
if response.status == 401 and attempt < retries - 1:
|
||||
continue
|
||||
elif response.status != 200:
|
||||
raise Exception(f"Failed to fetch data from Spotify: {response.status}")
|
||||
return await response.json()
|
||||
raise Exception("Exceeded max retries to fetch Spotify data")
|
||||
|
||||
|
||||
async def get_track(self, track_id):
|
||||
return await self.get(f"tracks/{track_id}")
|
||||
|
||||
async def get_playlist(self, playlist_id):
|
||||
return await self.get(f"playlists/{playlist_id}")
|
||||
|
||||
spotify_api = SpotifyAPI(client_id="ac2b614ca5ce46a18dfd1d3475fd6fd9", client_secret="df7bec95ae88438e8286db597bac8621")
|
||||
|
||||
class PlatformSelectView(LayoutView):
|
||||
def __init__(self, ctx, query):
|
||||
super().__init__(timeout=60)
|
||||
self.ctx = ctx
|
||||
self.query = query
|
||||
|
||||
platforms = [
|
||||
("YouTube", "ytsearch", discord.ButtonStyle.red),
|
||||
("JioSaavn", "jssearch", discord.ButtonStyle.green),
|
||||
("SoundCloud", "scsearch", discord.ButtonStyle.grey),
|
||||
]
|
||||
buttons = []
|
||||
for name, source, style in platforms:
|
||||
btn = Button(label=name, style=style)
|
||||
btn.callback = self.create_callback(source)
|
||||
buttons.append(btn)
|
||||
|
||||
container = build_container(
|
||||
TextDisplay("**Select a platform to search from:**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("Click a button below to choose."),
|
||||
Separator(visible=True),
|
||||
ActionRow(*buttons),
|
||||
)
|
||||
self.add_item(container)
|
||||
|
||||
def create_callback(self, source):
|
||||
async def callback(interaction: discord.Interaction):
|
||||
if interaction.user != self.ctx.author:
|
||||
await interaction.response.send_message("Only the command author can select a platform.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.send_message(f"Searching...", ephemeral=True)
|
||||
await self.perform_search(source)
|
||||
await interaction.message.delete()
|
||||
return callback
|
||||
|
||||
async def perform_search(self, source):
|
||||
results = await wavelink.Playable.search(self.query, source=source)
|
||||
if not results:
|
||||
return await self.ctx.send(view=CV2("No results found."))
|
||||
|
||||
top_results = results[:5]
|
||||
await self.ctx.send(view=SearchResultView(self.ctx, top_results, self.query, source))
|
||||
|
||||
|
||||
|
||||
class SearchResultView(LayoutView):
|
||||
def __init__(self, ctx, results, query="", source=""):
|
||||
super().__init__(timeout=60)
|
||||
self.ctx = ctx
|
||||
self.results = results
|
||||
|
||||
lines = [f"**Top 5 Results for '{query}'**", ""]
|
||||
for i, track in enumerate(results, start=1):
|
||||
dur = f"{track.length // 1000 // 60}:{track.length // 1000 % 60:02d}"
|
||||
lines.append(f"`{i}.` **{track.title}** • `{dur}`")
|
||||
|
||||
buttons = []
|
||||
for i in range(min(5, len(results))):
|
||||
btn = Button(label=str(i + 1), style=discord.ButtonStyle.primary)
|
||||
btn.callback = self.create_callback(i)
|
||||
buttons.append(btn)
|
||||
|
||||
container = build_container(
|
||||
TextDisplay("\n".join(lines)),
|
||||
Separator(visible=True),
|
||||
ActionRow(*buttons),
|
||||
)
|
||||
self.add_item(container)
|
||||
|
||||
def create_callback(self, index):
|
||||
async def callback(interaction: discord.Interaction):
|
||||
if interaction.user != self.ctx.author:
|
||||
await interaction.response.send_message("Only the command author can select a track.", ephemeral=True)
|
||||
return
|
||||
|
||||
track = self.results[index]
|
||||
vc = self.ctx.voice_client or await self.ctx.author.voice.channel.connect(cls=wavelink.Player)
|
||||
vc.ctx = self.ctx
|
||||
|
||||
if not vc.playing:
|
||||
await vc.play(track)
|
||||
await interaction.response.send_message(f"Started playing `{track.title}`.")
|
||||
await self.ctx.cog.display_player_embed(vc, track, self.ctx)
|
||||
else:
|
||||
await vc.queue.put_wait(track)
|
||||
await interaction.response.send_message(f"Added `{track.title}` to the queue.")
|
||||
|
||||
return callback
|
||||
|
||||
|
||||
class MusicControlView(LayoutView):
|
||||
def __init__(self, player, ctx, track=None, autoplay=False):
|
||||
super().__init__(timeout=None)
|
||||
self.player = player
|
||||
self.ctx = ctx
|
||||
self._build_ui(track, autoplay)
|
||||
|
||||
def _build_ui(self, track=None, autoplay=False):
|
||||
from discord.ui import Section, Thumbnail, TextDisplay
|
||||
|
||||
items = []
|
||||
|
||||
if track:
|
||||
sec = track.length // 1000
|
||||
duration = f"{sec // 60:02d}:{sec % 60:02d}"
|
||||
|
||||
requester = self.ctx.author.display_name
|
||||
if autoplay: requester += " (Autoplay)"
|
||||
|
||||
title_text = f"**Now Playing [{track.title}]({track.uri})**"
|
||||
items.append(TextDisplay(title_text))
|
||||
|
||||
bullet_text = (
|
||||
f"• **Author:** `{track.author}`\n"
|
||||
f"• **Duration:** `{duration}`\n"
|
||||
f"• **Requester:** {requester}"
|
||||
)
|
||||
|
||||
if getattr(track, 'artwork', None):
|
||||
accessory = Thumbnail(media=track.artwork)
|
||||
items.append(Section(TextDisplay(bullet_text), accessory=accessory))
|
||||
else:
|
||||
items.append(TextDisplay(bullet_text))
|
||||
|
||||
# Row 1 buttons (Main 5 from screenshot with exact styles)
|
||||
btn_pause = Button(emoji=ICONS_PAUSE, label="Pause", style=discord.ButtonStyle.primary)
|
||||
btn_skip = Button(emoji=SKIP, label="Skip", style=discord.ButtonStyle.secondary)
|
||||
btn_stop = Button(emoji=MUSICSTOP_ICONS, label="Stop", style=discord.ButtonStyle.danger)
|
||||
btn_loop = Button(emoji=ICONLOAD, label="Loop", style=discord.ButtonStyle.secondary)
|
||||
btn_autoplay = Button(emoji=ZUNMUTE, label="Autoplay", style=discord.ButtonStyle.secondary)
|
||||
|
||||
# Row 2 buttons (Remaining controls)
|
||||
btn_prev = Button(emoji=REWIND, style=discord.ButtonStyle.secondary)
|
||||
btn_shuffle = Button(emoji=SHUFFLE, style=discord.ButtonStyle.secondary)
|
||||
btn_rewind = Button(emoji=REWIND_ALT1, style=discord.ButtonStyle.secondary)
|
||||
btn_forward = Button(emoji=FORWARD, style=discord.ButtonStyle.secondary)
|
||||
btn_replay = Button(emoji=ICONS_MUSIC, style=discord.ButtonStyle.secondary)
|
||||
|
||||
btn_pause.callback = self._cb_pause
|
||||
btn_skip.callback = self._cb_skip
|
||||
btn_stop.callback = self._cb_stop
|
||||
btn_loop.callback = self._cb_loop
|
||||
btn_autoplay.callback = self._cb_autoplay
|
||||
|
||||
btn_prev.callback = self._cb_previous
|
||||
btn_shuffle.callback = self._cb_shuffle
|
||||
btn_rewind.callback = self._cb_rewind
|
||||
btn_forward.callback = self._cb_forward
|
||||
btn_replay.callback = self._cb_replay
|
||||
|
||||
items.append(ActionRow(btn_pause, btn_skip, btn_stop, btn_loop, btn_autoplay))
|
||||
items.append(ActionRow(btn_prev, btn_shuffle, btn_rewind, btn_forward, btn_replay))
|
||||
|
||||
container = build_container(*items)
|
||||
self.add_item(container)
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||
if not self.ctx.voice_client or not self.player.playing:
|
||||
await interaction.response.send_message("I'm not currently playing this anymore.", ephemeral=True)
|
||||
return False
|
||||
if interaction.user in self.ctx.voice_client.channel.members:
|
||||
return True
|
||||
await interaction.response.send_message("Only members in the same voice channel can control the player.", ephemeral=True)
|
||||
return False
|
||||
|
||||
async def _cb_autoplay(self, interaction):
|
||||
self.player.autoplay = (
|
||||
wavelink.AutoPlayMode.enabled if self.player.autoplay != wavelink.AutoPlayMode.enabled else wavelink.AutoPlayMode.disabled
|
||||
)
|
||||
await interaction.response.send_message(f"Autoplay {'enabled' if self.player.autoplay == wavelink.AutoPlayMode.enabled else 'disabled'} by **{interaction.user.display_name}**.")
|
||||
|
||||
async def _cb_previous(self, interaction):
|
||||
guild_id = interaction.guild.id
|
||||
if guild_id in track_histories and len(track_histories[guild_id]) > 1:
|
||||
track_histories[guild_id].pop()
|
||||
previous_track = track_histories[guild_id][-1]
|
||||
if self.player.playing:
|
||||
await self.player.stop()
|
||||
await self.ctx.voice_client.queue.put_wait(previous_track)
|
||||
await interaction.response.send_message(f"Playing previous track: `{previous_track.title}`.")
|
||||
else:
|
||||
await interaction.response.send_message("No previous track available.", ephemeral=True)
|
||||
|
||||
async def _cb_pause(self, interaction):
|
||||
if self.player.paused:
|
||||
await self.player.pause(False)
|
||||
await self.player.channel.edit(status=f"{ICONS_PAUSE} Playing: {self.player.current.title}")
|
||||
await interaction.response.send_message(f"Resumed by **{interaction.user.display_name}**.")
|
||||
elif self.player.playing:
|
||||
await self.player.pause(True)
|
||||
await self.player.channel.edit(status=f"{ICONS_PAUSE} Paused: {self.player.current.title}")
|
||||
await interaction.response.send_message(f"Paused by **{interaction.user.display_name}**.")
|
||||
|
||||
async def _cb_skip(self, interaction):
|
||||
if self.player.autoplay == wavelink.AutoPlayMode.enabled:
|
||||
await self.player.stop()
|
||||
return await interaction.response.send_message(f"Skipped by **{interaction.user.display_name}**.")
|
||||
if self.player and self.player.playing and not self.player.queue.is_empty:
|
||||
await self.player.stop()
|
||||
await interaction.response.send_message(f"Skipped by **{interaction.user.display_name}**.")
|
||||
else:
|
||||
await interaction.response.send_message("No song in queue to skip.", ephemeral=True)
|
||||
|
||||
async def _cb_loop(self, interaction):
|
||||
self.player.queue.mode = wavelink.QueueMode.loop if self.player.queue.mode != wavelink.QueueMode.loop else wavelink.QueueMode.normal
|
||||
await interaction.response.send_message(f"Loop {'enabled' if self.player.queue.mode == wavelink.QueueMode.loop else 'disabled'} by **{interaction.user.display_name}**.")
|
||||
|
||||
async def _cb_shuffle(self, interaction):
|
||||
if self.player.queue:
|
||||
random.shuffle(self.player.queue)
|
||||
await interaction.response.send_message(f"Queue shuffled by **{interaction.user.display_name}**.")
|
||||
else:
|
||||
await interaction.response.send_message("Queue is empty.", ephemeral=True)
|
||||
|
||||
async def _cb_rewind(self, interaction):
|
||||
if self.player.playing:
|
||||
new_position = max(self.player.position - 10000, 0)
|
||||
await self.player.seek(new_position)
|
||||
await interaction.response.send_message("Rewinded 10 seconds.", ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message("No track is currently playing.", ephemeral=True)
|
||||
|
||||
async def _cb_stop(self, interaction):
|
||||
if self.player:
|
||||
voice_channel = self.player.channel
|
||||
if voice_channel:
|
||||
await voice_channel.edit(status=None)
|
||||
await self.player.disconnect()
|
||||
await interaction.response.send_message(f"Stopped and disconnected by **{interaction.user.display_name}**.")
|
||||
else:
|
||||
await interaction.response.send_message("Not connected.", ephemeral=True)
|
||||
|
||||
async def _cb_forward(self, interaction):
|
||||
if self.player.playing:
|
||||
new_position = min(self.player.position + 10000, self.player.current.length)
|
||||
await self.player.seek(new_position)
|
||||
await interaction.response.send_message("Forwarded 10 seconds.", ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message("No track is currently playing.", ephemeral=True)
|
||||
|
||||
async def _cb_replay(self, interaction):
|
||||
if self.player.playing:
|
||||
await self.player.seek(0)
|
||||
await interaction.response.send_message("Replaying the current track.", ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message("No track is currently playing.", ephemeral=True)
|
||||
|
||||
|
||||
class Music(commands.Cog):
|
||||
def __init__(self, client: zyrox):
|
||||
self.client = client
|
||||
self.client.loop.create_task(self.connect_nodes())
|
||||
self.client.loop.create_task(self.monitor_inactivity())
|
||||
|
||||
self.inactivity_timeout = 120
|
||||
self.player_inactivity = {}
|
||||
|
||||
async def monitor_inactivity(self):
|
||||
while True:
|
||||
for guild in self.client.guilds:
|
||||
await self.check_inactivity(guild.id)
|
||||
await asyncio.sleep(60)
|
||||
|
||||
async def check_inactivity(self, guild_id):
|
||||
guild = self.client.get_guild(guild_id)
|
||||
if not guild:
|
||||
return
|
||||
|
||||
player = None
|
||||
for vc in self.client.voice_clients:
|
||||
if vc.guild.id == guild.id:
|
||||
player = vc
|
||||
break
|
||||
|
||||
if player and player.playing and len(player.channel.members) == 1:
|
||||
await self.inactivity_timer(guild)
|
||||
|
||||
async def inactivity_timer(self, guild):
|
||||
await asyncio.sleep(self.inactivity_timeout)
|
||||
if len(guild.voice_channels[0].members) == 1:
|
||||
player = None
|
||||
for vc in self.client.voice_clients:
|
||||
if vc.guild.id == guild.id:
|
||||
player = vc
|
||||
break
|
||||
if player:
|
||||
await player.disconnect(force=True)
|
||||
try:
|
||||
support = Button(label='Support', style=discord.ButtonStyle.link, url='https://discord.gg/codexdev')
|
||||
vote = Button(label='Vote', style=discord.ButtonStyle.link, url='https://top.gg/bot//vote')
|
||||
view = LayoutView(timeout=None)
|
||||
container = build_container(
|
||||
TextDisplay("**Inactive Timeout**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("Bot has been disconnected due to inactivity (being idle in Voice Channel) for more than 2 minutes."),
|
||||
Separator(visible=True),
|
||||
ActionRow(support, vote),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"*Thanks for choosing {BRAND_NAME}!*"),
|
||||
)
|
||||
view.add_item(container)
|
||||
await player.ctx.channel.send(view=view)
|
||||
except:
|
||||
pass
|
||||
|
||||
async def connect_nodes(self) -> None:
|
||||
nodes = [wavelink.Node(uri="https://lava-v4.ajieblogs.eu.org", password="https://dsc.gg/ajidevserver")]
|
||||
await wavelink.Pool.connect(nodes=nodes, client=self.client, cache_capacity=None)
|
||||
|
||||
|
||||
async def display_player_embed(self, player, track, ctx, autoplay=False):
|
||||
await ctx.send(view=MusicControlView(player, ctx, track, autoplay))
|
||||
|
||||
|
||||
async def on_track_end(self, payload: wavelink.TrackEndEventPayload):
|
||||
player = payload.player
|
||||
if not player.queue:
|
||||
if player.queue.mode == wavelink.QueueMode.loop:
|
||||
await player.play(payload.track)
|
||||
elif player.autoplay == wavelink.AutoPlayMode.enabled:
|
||||
await asyncio.sleep(5)
|
||||
if player.current:
|
||||
await self.display_player_embed(player, player.current, player.ctx, autoplay=True)
|
||||
else:
|
||||
await player.ctx.send(view=CV2("No suitable track found for autoplay."))
|
||||
else:
|
||||
await player.disconnect()
|
||||
support = Button(label='Support', style=discord.ButtonStyle.link, url='https://discord.gg/codexdev')
|
||||
vote = Button(label='Vote', style=discord.ButtonStyle.link, url='https://top.gg/bot//vote')
|
||||
view = LayoutView(timeout=None)
|
||||
container = build_container(
|
||||
TextDisplay("**Queue Ended**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay("All tracks have been played, leaving the voice channel."),
|
||||
Separator(visible=True),
|
||||
ActionRow(support, vote),
|
||||
)
|
||||
view.add_item(container)
|
||||
await player.ctx.send(view=view)
|
||||
else:
|
||||
next_track = await player.queue.get_wait()
|
||||
await player.play(next_track)
|
||||
await self.display_player_embed(player, next_track, player.ctx)
|
||||
|
||||
|
||||
|
||||
async def play_source(self, ctx, query):
|
||||
if not ctx.author.voice:
|
||||
await ctx.send(view=CV2(f"{WARNING} you need to be in a voice channel to use this command."))
|
||||
return
|
||||
|
||||
vc = ctx.voice_client or await ctx.author.voice.channel.connect(cls=wavelink.Player)
|
||||
vc.ctx = ctx
|
||||
|
||||
|
||||
if vc.playing:
|
||||
if ctx.voice_client and ctx.voice_client.channel != ctx.author.voice.channel:
|
||||
await ctx.send(view=CV2(f"You must be connected to {ctx.voice_client.channel.mention} to play."))
|
||||
return
|
||||
vc.autoplay = wavelink.AutoPlayMode.disabled
|
||||
|
||||
"""if re.match(SPOTIFY_TRACK_REGEX, query):
|
||||
await self.handle_spotify_link(ctx, vc, query, "track")
|
||||
elif re.match(SPOTIFY_PLAYLIST_REGEX, query):
|
||||
await self.handle_spotify_link(ctx, vc, query, "playlist")
|
||||
elif re.match(SPOTIFY_ALBUM_REGEX, query):
|
||||
await self.handle_spotify_link(ctx, vc, query, "album")
|
||||
|
||||
return"""
|
||||
|
||||
tracks = await wavelink.Playable.search(query)
|
||||
if not tracks:
|
||||
await ctx.send(view=CV2("No results found."))
|
||||
return
|
||||
|
||||
if isinstance(tracks, wavelink.Playlist):
|
||||
await vc.queue.put_wait(tracks.tracks)
|
||||
await ctx.send(view=CV2(f"{ZPLUS} Added playlist [{tracks.name}](https://discord.gg/codexdev) with **{len(tracks.tracks)} songs** to the queue."))
|
||||
if not vc.playing:
|
||||
track = await vc.queue.get_wait()
|
||||
await vc.play(track)
|
||||
await self.display_player_embed(vc, track, ctx)
|
||||
else:
|
||||
track = tracks[0]
|
||||
await vc.queue.put_wait(track)
|
||||
await ctx.send(view=CV2(f"{ZPLUS} Added [{track.title}](https://discord.gg/codexdev) to the queue."))
|
||||
if not vc.playing:
|
||||
await vc.play(await vc.queue.get_wait())
|
||||
await self.display_player_embed(vc, track, ctx)
|
||||
self.client.loop.create_task(self.check_inactivity(ctx.guild.id))
|
||||
# await interaction.response.defer()
|
||||
|
||||
|
||||
|
||||
async def handle_spotify_link(self, ctx, vc, link, type_):
|
||||
try:
|
||||
if type_ == "track":
|
||||
track_id = re.search(SPOTIFY_TRACK_REGEX, link).group(1)
|
||||
track_info = await spotify_api.get_track(track_id)
|
||||
|
||||
|
||||
title = track_info['name']
|
||||
author = ', '.join(artist['name'] for artist in track_info['artists'])
|
||||
|
||||
|
||||
search_query = f"{title} by {author}"
|
||||
search_results = await wavelink.Playable.search(search_query, source=wavelink.enums.TrackSource.YouTube)
|
||||
|
||||
if not search_results:
|
||||
await ctx.send("Can't play this track from Spotify, please try with another track.")
|
||||
return
|
||||
|
||||
track = search_results[0]
|
||||
await vc.queue.put_wait(track)
|
||||
await ctx.send(view=CV2(f"{ZPLUS} Added [{track.title}](https://discord.gg/codexdev) to the queue."))
|
||||
if not vc.playing:
|
||||
await vc.play(track)
|
||||
await self.display_player_embed(vc, track, ctx)
|
||||
|
||||
#await self.display_player_embed(vc, track, ctx)
|
||||
|
||||
elif type_ == "playlist":
|
||||
lmao = await ctx.send("⏳ Processing to add tracks from the playlist, this may take a while...")
|
||||
|
||||
playlist_id = re.search(SPOTIFY_PLAYLIST_REGEX, link).group(1)
|
||||
playlist_info = await spotify_api.get(f"playlists/{playlist_id}")
|
||||
tracks = playlist_info.get("tracks", {}).get("items", [])
|
||||
playlist_length = len(tracks)
|
||||
|
||||
if not tracks:
|
||||
await ctx.send("No tracks found in the playlist.")
|
||||
return
|
||||
|
||||
c = 0
|
||||
for track in tracks:
|
||||
title = track['track']['name']
|
||||
author = ', '.join(artist['name'] for artist in track['track']['artists'])
|
||||
search_query = f"{title} {author}"
|
||||
|
||||
track_results = await wavelink.Playable.search(search_query, source=wavelink.enums.TrackSource.YouTube)
|
||||
if track_results:
|
||||
await vc.queue.put_wait(track_results[0])
|
||||
c += 1
|
||||
await ctx.message.add_reaction("✅")
|
||||
|
||||
await ctx.send(view=CV2(f"{ZPLUS} Added **{c}** of **{playlist_length}** tracks from **playlist** **[{playlist_info['name']}](https://discord.gg/codexdev)** to the queue."))
|
||||
await lmao.delete()
|
||||
|
||||
if not vc.playing:
|
||||
next_track = await vc.queue.get_wait()
|
||||
await vc.play(next_track)
|
||||
await self.display_player_embed(vc, next_track, ctx)
|
||||
|
||||
|
||||
elif type_ == "album":
|
||||
await ctx.message.add_reaction("⌛")
|
||||
album_id = re.search(SPOTIFY_ALBUM_REGEX, link).group(1)
|
||||
album_info = await spotify_api.get(f"albums/{album_id}")
|
||||
tracks = album_info.get("tracks", {}).get("items", [])
|
||||
|
||||
if not tracks:
|
||||
await ctx.send("No tracks found in the album.")
|
||||
return
|
||||
|
||||
for track in tracks:
|
||||
title = track['name']
|
||||
author = ', '.join(artist['name'] for artist in track['artists'])
|
||||
search_query = f"{title} {author}"
|
||||
|
||||
track_results = await wavelink.Playable.search(search_query, source=wavelink.enums.TrackSource.YouTube)
|
||||
if track_results:
|
||||
await vc.queue.put_wait(track_results[0])
|
||||
|
||||
await ctx.send(view=CV2(f"{ZPLUS} Added all tracks from album **[{album_info['name']}](https://discord.gg/codexdev)** to the queue."))
|
||||
if not vc.playing:
|
||||
next_track = await vc.queue.get_wait()
|
||||
await vc.play(next_track)
|
||||
await self.display_player_embed(vc, next_track, ctx)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
await ctx.send(f"An error occurred while processing the Spotify link: {e}")
|
||||
|
||||
|
||||
|
||||
def create_progress_bar(self, completed, total, length=10):
|
||||
filled_length = int(length * (completed / total))
|
||||
bar = '█' * filled_length + '░' * (length - filled_length)
|
||||
return bar
|
||||
|
||||
@commands.command(name="play", aliases=['p'], usage="play <query>", help="Plays a song or playlist.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def play(self, ctx: commands.Context, *, query: str):
|
||||
|
||||
await self.play_source(ctx, query)
|
||||
|
||||
|
||||
@commands.command(name="search", usage="search <query>", help="Searches music from multiple platforms.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def search2(self, ctx: commands.Context, *, query: str):
|
||||
if not ctx.author.voice:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in a voice channel to use this command."))
|
||||
return
|
||||
|
||||
await ctx.send(view=PlatformSelectView(ctx, query))
|
||||
|
||||
|
||||
@commands.command(name="nowplaying", aliases=["nop"], usage="nowplaying", help="Shows the info about current playing song.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def nowplaying(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2("No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2("You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
track = vc.current
|
||||
position = vc.position / 1000
|
||||
length = track.length / 1000
|
||||
|
||||
progress_bar = self.create_progress_bar(position, length, length=10)
|
||||
position_str = f"{int(position // 60)}:{int(position % 60):02}"
|
||||
length_str = f"{int(length // 60)}:{int(length % 60):02}"
|
||||
|
||||
|
||||
queue_length = len(vc.queue) if vc.queue else 0
|
||||
|
||||
|
||||
if "spotify" in track.uri:
|
||||
source_name = "Spotify"
|
||||
elif "youtube" in track.uri:
|
||||
source_name = "YouTube"
|
||||
elif "soundcloud" in track.uri:
|
||||
source_name = "SoundCloud"
|
||||
elif "jiosaavn" in track.uri:
|
||||
source_name = "JioSaavn"
|
||||
else:
|
||||
source_name = "Unknown Source"
|
||||
|
||||
|
||||
view = CV2Embed(
|
||||
title="🎶 Now Playing",
|
||||
color=0x1DB954 if source_name == "Spotify" else 0xFF0000
|
||||
)
|
||||
view.add_field(name="Track", value=f"[{track.title}]({track.uri})", inline=False)
|
||||
view.add_field(name="Song By", value=track.author, inline=False)
|
||||
view.add_field(name="Progress", value=f"{position_str} [{progress_bar}] {length_str}", inline=False)
|
||||
view.add_field(name="Duration", value=length_str, inline=False)
|
||||
view.add_field(name="Queue Length", value=str(queue_length), inline=False)
|
||||
view.add_field(name="Source", value=f"{source_name} - [Link]({track.uri})", inline=False)
|
||||
view.set_thumbnail(url=track.artwork if track.artwork else "")
|
||||
view.set_footer(text=f"Requested by {ctx.author.display_name}", icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
|
||||
await ctx.send(view=view)
|
||||
|
||||
@commands.command(name="autoplay", usage="autoplay", help="Toggles autoplay mode.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def autoplay(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2(f"{WARNING} No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc:
|
||||
vc.autoplay = (
|
||||
wavelink.AutoPlayMode.enabled if vc.autoplay != wavelink.AutoPlayMode.enabled else wavelink.AutoPlayMode.disabled
|
||||
)
|
||||
await ctx.send(view=CV2(f"{TICK} Autoplay {'enabled' if vc.autoplay == wavelink.AutoPlayMode.enabled else 'disabled'} by {ctx.author.mention}."))
|
||||
|
||||
@commands.command(name="loop", usage="loop", help="Toggles loop mode.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def loop(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2(f"{WARNING} No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc:
|
||||
vc.queue.mode = wavelink.QueueMode.loop if vc.queue.mode != wavelink.QueueMode.loop else wavelink.QueueMode.normal
|
||||
await ctx.send(view=CV2(f"{TICK} Loop {'enabled' if vc.queue.mode == wavelink.QueueMode.loop else 'disabled'} by {ctx.author.mention}."))
|
||||
else:
|
||||
await ctx.send(view=CV2("I'm not connected to a voice channel."))
|
||||
|
||||
|
||||
@commands.command(name="pause", usage="pause", help="Pauses the current song.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def pause(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2(f"{WARNING} No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc and vc.playing and not vc.paused:
|
||||
await vc.pause(True)
|
||||
await vc.channel.edit(status=f"{ZMUSICPAUSE} Paused: {vc.current.title}")
|
||||
await ctx.send(view=CV2(f"Paused by {ctx.author.mention}."))
|
||||
else:
|
||||
await ctx.send(view=CV2(f"{WARNING} Nothing is playing or already paused."))
|
||||
|
||||
@commands.command(name="resume", usage="resume", help="Resumes the paused song.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def resume(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2(f"{WARNING} No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{ICONS_WARNING_ALT1} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc and vc.paused:
|
||||
await vc.pause(False)
|
||||
await vc.channel.edit(status=f"{MUSIC_ALT1} Playing: {vc.current.title}")
|
||||
await ctx.send(view=CV2(f"Resumed by {ctx.author.mention}."))
|
||||
else:
|
||||
await ctx.send(view=CV2("Player is not paused."))
|
||||
|
||||
@commands.command(name="skip", usage="skip", help="Skips the current song.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def skip(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2("No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc.autoplay == wavelink.AutoPlayMode.enabled:
|
||||
await vc.stop()
|
||||
return await ctx.send(view=CV2(f"Skipped by {ctx.author.mention}."))
|
||||
|
||||
|
||||
if vc and vc.playing and not vc.queue.is_empty:
|
||||
await vc.stop()
|
||||
await ctx.send(view=CV2(f"Skipped by {ctx.author.mention}."))
|
||||
else:
|
||||
await ctx.send(view=CV2(f"{WARNING} No song is playing or in the queue to skip."))
|
||||
|
||||
@commands.command(name="shuffle", usage="shuffle", help="Shuffles the queue.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def shuffle(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2(f"{WARNING} No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc and vc.queue:
|
||||
random.shuffle(vc.queue)
|
||||
await ctx.send(view=CV2(f"Queue shuffled by {ctx.author.mention}."))
|
||||
else:
|
||||
await ctx.send(view=CV2("Queue is empty."))
|
||||
|
||||
@commands.command(name="stop", usage="stop", help="Stops the current song and clears the queue.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def stop(self, ctx: commands.Context):
|
||||
player: wavelink.Player = cast(wavelink.Player, ctx.voice_client)
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2(f"{WARNING} No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc and player:
|
||||
await vc.channel.edit(status=None)
|
||||
vc.queue.clear()
|
||||
await vc.disconnect(force=True)
|
||||
await ctx.send(view=CV2(f"Stopped and queue cleared by {ctx.author.mention}."))
|
||||
else:
|
||||
await ctx.send(view=CV2("Nothing is playing to stop."))
|
||||
|
||||
@commands.command(name="volume", aliases=["vol"], usage="volume <level>", help="Sets the volume of the player.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def volume(self, ctx: commands.Context, level: int):
|
||||
vc = ctx.voice_client
|
||||
|
||||
if not vc:
|
||||
await ctx.send(view=CV2(f"{WARNING} I'm not connected to a voice channel."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc:
|
||||
if 1 <= level <= 150:
|
||||
await vc.set_volume(level)
|
||||
await ctx.send(view=CV2(f"{MUTE} Volume set to {level}% by {ctx.author.mention}."))
|
||||
else:
|
||||
await ctx.send(view=CV2(f"{WARNING} Volume must be between 1 and 150."))
|
||||
else:
|
||||
await ctx.send(view=CV2("Bot is not connected to a voice channel."))
|
||||
|
||||
@commands.command(name="queue", usage="queue", help="Shows the current queue.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def queue(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
|
||||
if not vc or not vc.queue or vc.queue.is_empty:
|
||||
await ctx.send(view=CV2(f"{WARNING} The queue is currently empty."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} you need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
|
||||
entries = [f"{index + 1}. [{track.title} - {track.author}]({track.uri})" for index, track in enumerate(vc.queue)]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title="Current Queue",
|
||||
description="List of upcoming songs.",
|
||||
per_page=10,
|
||||
color=0xFF0000),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@commands.command(name="clearqueue", usage="clearqueue", help="Clears the queue.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def clearqueue(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
|
||||
if not vc or not vc.queue or vc.queue.is_empty:
|
||||
await ctx.send(view=CV2(f"{WARNING} No Queue to clear."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc and vc.queue:
|
||||
vc.queue.clear()
|
||||
await ctx.send(view=CV2("Queue has been cleared."))
|
||||
else:
|
||||
await ctx.send(view=CV2("No queue to clear."))
|
||||
|
||||
@commands.command(name="replay", usage="replay", help="Replays the current song.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def replay(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2(f"{WARNING} I'm not connected to any voice channel."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc and vc.playing:
|
||||
await vc.seek(0)
|
||||
await ctx.send(view=CV2("Replaying the current track."))
|
||||
else:
|
||||
await ctx.send(view=CV2("No track is currently playing."))
|
||||
|
||||
@commands.command(name="join", aliases=["connect"], usage="join", help="Joins the voice channel.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def join(self, ctx: commands.Context):
|
||||
if ctx.author.voice:
|
||||
await ctx.author.voice.channel.connect(cls=wavelink.Player)
|
||||
await ctx.send(view=CV2("Joined the voice channel."))
|
||||
else:
|
||||
await ctx.send(view=CV2("You need to join a voice channel first."))
|
||||
|
||||
@commands.hybrid_command(name="disconnect", aliases=["dc", "leave"], usage="disconnect", help="Disconnects the bot from the voice channel.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def disconnect(self, ctx: commands.Context):
|
||||
vc = ctx.voice_client
|
||||
if not vc:
|
||||
await ctx.send(view=CV2(f"{ICONS_WARNING_ALT1} I'm not connected to any voice channel."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2(f"{WARNING} You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
if vc:
|
||||
await vc.disconnect()
|
||||
await ctx.send(view=CV2("Disconnected from the voice channel."))
|
||||
else:
|
||||
await ctx.send(view=CV2("Bot is not connected to any voice channel."))
|
||||
|
||||
@commands.command(name="seek", usage="seek <percentage>", help="Seeks to a specific percentage of the song.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def seek(self, ctx: commands.Context, percentage: int):
|
||||
if not 1 <= percentage <= 100:
|
||||
await ctx.send(view=CV2("Please provide a percentage between 1 and 100."))
|
||||
return
|
||||
|
||||
vc = ctx.voice_client
|
||||
if not vc or not vc.playing:
|
||||
await ctx.send(view=CV2("No song is currently playing."))
|
||||
return
|
||||
|
||||
if not ctx.author.voice or ctx.author.voice.channel.id != vc.channel.id:
|
||||
await ctx.send(view=CV2("You need to be in the same voice channel as me to use this command."))
|
||||
return
|
||||
|
||||
track = vc.current
|
||||
target_position = int(track.length * (percentage / 100))
|
||||
await vc.seek(target_position)
|
||||
|
||||
await ctx.send(view=CV2(f"Seeked to {percentage}% of the current track."))
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_wavelink_track_start(self, payload: wavelink.TrackStartEventPayload):
|
||||
player = payload.player
|
||||
track = player.current
|
||||
guild_id = player.guild.id
|
||||
|
||||
voice_channel = player.channel
|
||||
if voice_channel:
|
||||
await voice_channel.edit(status=f"{MUSIC_ALT1} Playing: {track.title}") # type: ignore
|
||||
|
||||
if guild_id not in track_histories:
|
||||
track_histories[guild_id] = []
|
||||
|
||||
if not track_histories[guild_id] or track_histories[guild_id][-1] != track:
|
||||
track_histories[guild_id].append(track)
|
||||
|
||||
|
||||
if len(track_histories[guild_id]) > 10:
|
||||
track_histories[guild_id].pop(0)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_wavelink_track_end(self, payload: wavelink.TrackEndEventPayload):
|
||||
player = payload.player
|
||||
voice_channel = player.channel
|
||||
|
||||
if voice_channel:
|
||||
await voice_channel.edit(status=None) # type: ignore
|
||||
await self.on_track_end(payload)
|
||||
213
bot/cogs/commands/nightmode.py
Normal file
213
bot/cogs/commands/nightmode.py
Normal file
@@ -0,0 +1,213 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import aiosqlite
|
||||
import os
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2
|
||||
from discord.ui import TextDisplay, Separator, ActionRow, LayoutView, Container
|
||||
|
||||
# Database setup
|
||||
db_folder = "db"
|
||||
db_file = "anti.db"
|
||||
db_path = os.path.join(db_folder, db_file)
|
||||
|
||||
|
||||
class Nightmode(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.initialize_db())
|
||||
self.ricky = ["870179991462236170", "767979794411028491"]
|
||||
self.color = 0xFF0000
|
||||
|
||||
async def initialize_db(self):
|
||||
self.db = await aiosqlite.connect(db_path)
|
||||
await self.db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS Nightmode (
|
||||
guildId TEXT,
|
||||
roleId TEXT,
|
||||
adminPermissions INTEGER
|
||||
)
|
||||
""")
|
||||
await self.db.commit()
|
||||
|
||||
async def is_extra_owner(self, user, guild):
|
||||
async with self.db.execute(
|
||||
"""
|
||||
SELECT owner_id FROM extraowners WHERE guild_id = ? AND owner_id = ?
|
||||
""",
|
||||
(guild.id, user.id),
|
||||
) as cursor:
|
||||
extra_owner = await cursor.fetchone()
|
||||
return extra_owner is not None
|
||||
|
||||
@commands.hybrid_group(
|
||||
name="nightmode",
|
||||
aliases=[],
|
||||
help="Manages Nightmode feature",
|
||||
invoke_without_command=True,
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
@commands.guild_only()
|
||||
async def nightmode(self, ctx):
|
||||
view = CV2(
|
||||
"__**Nightmode**__",
|
||||
"Nightmode swiftly disables dangerous permissions for roles, like stripping `ADMINISTRATION` rights, while preserving original settings for seamless restoration.\n\n**Make sure to keep my ROLE above all roles you want to protect.**",
|
||||
"**Usage**\n `nightmode enable`\n `nightmode disable`",
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
|
||||
@nightmode.command(name="enable", help="Enable nightmode")
|
||||
@commands.has_permissions(administrator=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
async def enable_nightmode(self, ctx):
|
||||
if ctx.guild.member_count < 50:
|
||||
view = CV2(
|
||||
"Access Denied",
|
||||
"Your Server Doesn't Meet My 50 Member Criteria",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
own = ctx.author.id == ctx.guild.owner_id
|
||||
check = await self.is_extra_owner(ctx.author, ctx.guild)
|
||||
if not own and not check and ctx.author.id not in self.ricky:
|
||||
view = CV2(
|
||||
"Access Denied",
|
||||
"Only Server Owner Or Extraowner Can Run This Command.!",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
if (
|
||||
not own
|
||||
and not (ctx.guild.me.top_role.position <= ctx.author.top_role.position)
|
||||
and ctx.author.id not in self.ricky
|
||||
):
|
||||
view = CV2(
|
||||
"Access Denied",
|
||||
"Only Server Owner or Extraowner Having **Higher role than me can run this command**",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
bot_highest_role = ctx.guild.me.top_role
|
||||
manageable_roles = [
|
||||
role
|
||||
for role in ctx.guild.roles
|
||||
if role.position < bot_highest_role.position
|
||||
and role.name != "@everyone"
|
||||
and role.permissions.administrator
|
||||
and not role.managed
|
||||
]
|
||||
|
||||
if not manageable_roles:
|
||||
view = CV2(
|
||||
"Error",
|
||||
"No Roles Found With Admin Permissions",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT guildId FROM Nightmode WHERE guildId = ?", (str(ctx.guild.id),)
|
||||
) as cursor:
|
||||
if await cursor.fetchone():
|
||||
view = CV2(
|
||||
"Error",
|
||||
"Nightmode is already enabled.",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
async with self.db.cursor() as cursor:
|
||||
for role in manageable_roles:
|
||||
admin_permissions = discord.Permissions(administrator=True)
|
||||
if role.permissions.administrator:
|
||||
permissions = role.permissions
|
||||
permissions.administrator = False
|
||||
|
||||
await role.edit(permissions=permissions, reason="Nightmode ENABLED")
|
||||
|
||||
await cursor.execute(
|
||||
"""
|
||||
INSERT OR REPLACE INTO Nightmode (guildId, roleId, adminPermissions)
|
||||
VALUES (?, ?, ?)
|
||||
""",
|
||||
(str(ctx.guild.id), str(role.id), int(admin_permissions.value)),
|
||||
)
|
||||
await self.db.commit()
|
||||
|
||||
view = CV2(
|
||||
"Success",
|
||||
"Nightmode enabled! Dangerous Permissions Disabled For Manageable Roles.",
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
|
||||
@nightmode.command(name="disable", help="Disable nightmode")
|
||||
@commands.has_permissions(administrator=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
async def disable_nightmode(self, ctx):
|
||||
if ctx.guild.member_count < 50:
|
||||
view = CV2(
|
||||
"Access Denied",
|
||||
"Your Server Doesn't Meet My 50 Member Criteria",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
own = ctx.author.id == ctx.guild.owner_id
|
||||
check = await self.is_extra_owner(ctx.author, ctx.guild)
|
||||
if not own and not check and ctx.author.id not in self.ricky:
|
||||
view = CV2(
|
||||
"Access Denied",
|
||||
"Only Server Owner Or Extraowner Can Run This Command.!",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
if (
|
||||
not own
|
||||
and not (ctx.guild.me.top_role.position <= ctx.author.top_role.position)
|
||||
and ctx.author.id not in self.ricky
|
||||
):
|
||||
view = CV2(
|
||||
"Access Denied",
|
||||
"Only Server Owner or Extraowner Having **Higher role than me can run this command**",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
async with self.db.execute(
|
||||
"SELECT roleId, adminPermissions FROM Nightmode WHERE guildId = ?",
|
||||
(str(ctx.guild.id),),
|
||||
) as cursor:
|
||||
stored_roles = await cursor.fetchall()
|
||||
|
||||
if not stored_roles:
|
||||
view = CV2(
|
||||
"Error",
|
||||
"Nightmode is not enabled.",
|
||||
)
|
||||
return await ctx.send(view=view)
|
||||
|
||||
async with self.db.cursor() as cursor:
|
||||
for role_id, admin_permissions in stored_roles:
|
||||
role = ctx.guild.get_role(int(role_id))
|
||||
if role:
|
||||
permissions = discord.Permissions(
|
||||
administrator=bool(admin_permissions)
|
||||
)
|
||||
await role.edit(
|
||||
permissions=permissions, reason="Nightmode DISABLED"
|
||||
)
|
||||
|
||||
await cursor.execute(
|
||||
"DELETE FROM Nightmode WHERE guildId = ? AND roleId = ?",
|
||||
(str(ctx.guild.id), role_id),
|
||||
)
|
||||
await self.db.commit()
|
||||
|
||||
view = CV2(
|
||||
"Success",
|
||||
"Nightmode disabled! Restored Permissions For Manageable Roles.",
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
40
bot/cogs/commands/nitro.py
Normal file
40
bot/cogs/commands/nitro.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ui import Button, View
|
||||
|
||||
|
||||
class Nitro(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if self.bot.user in message.mentions and (
|
||||
"nitro" in message.content.lower() or "$nitro" in message.content.lower()
|
||||
):
|
||||
ctx = await self.bot.get_context(message)
|
||||
await self.bot.invoke(ctx)
|
||||
|
||||
@commands.command(name="nitro")
|
||||
async def nitro(self, ctx):
|
||||
embed = discord.Embed(color=0x2B2D31)
|
||||
embed.add_field(
|
||||
name="A WILD NITRO GIFT APPEARS?",
|
||||
value="Expires in 12 hours\n\nClick the claim button for claiming Nitro",
|
||||
inline=False,
|
||||
)
|
||||
embed.set_image(
|
||||
url="https://media.tenor.com/ltVe8iMhgXcAAAAS/nitro-discord.gif"
|
||||
)
|
||||
|
||||
claim_button = Button(
|
||||
style=discord.ButtonStyle.primary,
|
||||
label="Click me!",
|
||||
url="https://discord.gg/codexdev",
|
||||
disabled=False,
|
||||
)
|
||||
|
||||
view = View()
|
||||
view.add_item(claim_button)
|
||||
|
||||
await ctx.send(embed=embed, view=view)
|
||||
181
bot/cogs/commands/notify.py
Normal file
181
bot/cogs/commands/notify.py
Normal file
@@ -0,0 +1,181 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import aiosqlite
|
||||
from utils.Tools import *
|
||||
from utils.cv2 import CV2
|
||||
|
||||
|
||||
class NotifCommands(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.db_path = "db/notify.db"
|
||||
self.loop_task = self.bot.loop.create_task(self.setup_db())
|
||||
|
||||
async def setup_db(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("""CREATE TABLE IF NOT EXISTS notifications (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
type TEXT NOT NULL UNIQUE,
|
||||
role_id INTEGER NOT NULL,
|
||||
channel_id INTEGER NOT NULL)""")
|
||||
await db.commit()
|
||||
|
||||
@commands.group(invoke_without_command=True)
|
||||
async def setnotif(self, ctx):
|
||||
view = CV2(
|
||||
"Notification Commands",
|
||||
"Subcommands: twitch, youtube, list, reset",
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
|
||||
@setnotif.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def twitch(self, ctx, role: discord.Role, channel: discord.TextChannel):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT * FROM notifications WHERE type = ?", ("twitch",)
|
||||
) as existing:
|
||||
row = await existing.fetchone()
|
||||
if row:
|
||||
view = CV2(
|
||||
"Access Denied",
|
||||
"Twitch notification already set. Remove it first.",
|
||||
)
|
||||
await ctx.reply(view=view)
|
||||
return
|
||||
|
||||
await db.execute(
|
||||
"INSERT INTO notifications (type, role_id, channel_id) VALUES (?, ?, ?)",
|
||||
("twitch", role.id, channel.id),
|
||||
)
|
||||
await db.commit()
|
||||
view = CV2(
|
||||
"Success",
|
||||
f"Twitch notifications set for {role.mention} in {channel.mention}.",
|
||||
)
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@setnotif.command()
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def youtube(self, ctx, role: discord.Role, channel: discord.TextChannel):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT * FROM notifications WHERE type = ?", ("youtube",)
|
||||
) as existing:
|
||||
row = await existing.fetchone()
|
||||
if row:
|
||||
view = CV2(
|
||||
"Access Denied",
|
||||
"YouTube notification already set. Remove it first.",
|
||||
)
|
||||
await ctx.reply(view=view)
|
||||
return
|
||||
|
||||
await db.execute(
|
||||
"INSERT INTO notifications (type, role_id, channel_id) VALUES (?, ?, ?)",
|
||||
("youtube", role.id, channel.id),
|
||||
)
|
||||
await db.commit()
|
||||
view = CV2(
|
||||
"Success",
|
||||
f"YouTube notifications set for {role.mention} in {channel.mention}.",
|
||||
)
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@setnotif.command()
|
||||
async def list(self, ctx):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute("SELECT * FROM notifications") as cursor:
|
||||
rows = await cursor.fetchall()
|
||||
if not rows:
|
||||
view = CV2(
|
||||
"Notification Settings",
|
||||
"No Twitch and YouTube notification channels set.",
|
||||
)
|
||||
await ctx.reply(view=view)
|
||||
return
|
||||
|
||||
lines = []
|
||||
for row in rows:
|
||||
notif_type = row[1].capitalize()
|
||||
role = ctx.guild.get_role(row[2])
|
||||
channel = ctx.guild.get_channel(row[3])
|
||||
if role and channel:
|
||||
lines.append(
|
||||
f"**{notif_type} Notifications**\nRole: {role.mention} | Channel: {channel.mention}"
|
||||
)
|
||||
else:
|
||||
lines.append(
|
||||
f"**{notif_type} Notifications**\nRole or Channel not found"
|
||||
)
|
||||
|
||||
view = CV2(
|
||||
"Current Notification Settings",
|
||||
*lines,
|
||||
)
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@setnotif.command()
|
||||
async def reset(self, ctx):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute(
|
||||
"DELETE FROM notifications WHERE type IN (?, ?)", ("twitch", "youtube")
|
||||
)
|
||||
await db.commit()
|
||||
view = CV2(
|
||||
"Success",
|
||||
"Twitch and YouTube notifications have been reset.",
|
||||
)
|
||||
await ctx.send(view=view)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_presence_update(self, before, after):
|
||||
|
||||
streaming = next(
|
||||
(
|
||||
activity
|
||||
for activity in after.activities
|
||||
if isinstance(activity, discord.Streaming)
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if streaming:
|
||||
stream_type = (
|
||||
"twitch"
|
||||
if "twitch" in streaming.url.lower()
|
||||
else "youtube"
|
||||
if "youtube" in streaming.url.lower()
|
||||
else None
|
||||
)
|
||||
if stream_type:
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT role_id, channel_id FROM notifications WHERE type = ?",
|
||||
(stream_type,),
|
||||
) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row:
|
||||
role_id, channel_id = row
|
||||
role = after.guild.get_role(role_id)
|
||||
channel = after.guild.get_channel(channel_id)
|
||||
|
||||
if role and channel:
|
||||
embed = discord.Embed(
|
||||
title=f"{after.display_name} is now live!",
|
||||
description=f"{after.mention} is now streaming on {stream_type.capitalize()}.",
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed.add_field(
|
||||
name="Stream Title",
|
||||
value=streaming.name,
|
||||
inline=False,
|
||||
)
|
||||
embed.add_field(
|
||||
name="Watch here", value=streaming.url, inline=False
|
||||
)
|
||||
await channel.send(content=role.mention, embed=embed)
|
||||
710
bot/cogs/commands/np.py
Normal file
710
bot/cogs/commands/np.py
Normal file
@@ -0,0 +1,710 @@
|
||||
from discord.ext import commands, tasks
|
||||
from discord import *
|
||||
import discord
|
||||
from utils.emoji import BOOST, ICONS_WARNING, INFO, MENTION, PREMIUM, TICK, TIME, TIMER_ALT1, U_ADMIN, ZDIL, ZHUMAN, ZYROXHAMMER, ZYROXSYS
|
||||
import aiosqlite
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
from discord.ui import View, Button, Select
|
||||
from utils.config import OWNER_IDS
|
||||
from utils import Paginator, DescriptionEmbedPaginator
|
||||
from utils.cv2 import CV2Embed, add_action_rows
|
||||
from utils.config import *
|
||||
|
||||
|
||||
def load_owner_ids():
|
||||
return OWNER_IDS
|
||||
|
||||
|
||||
async def is_staff(user, staff_ids):
|
||||
return user.id in staff_ids
|
||||
|
||||
|
||||
async def is_owner_or_staff(ctx):
|
||||
return await is_staff(ctx.author, ctx.cog.staff) or ctx.author.id in OWNER_IDS
|
||||
|
||||
|
||||
class TimeSelect(Select):
|
||||
def __init__(self, user, db_path, author):
|
||||
super().__init__(placeholder="Select the duration")
|
||||
self.user = user
|
||||
self.db_path = db_path
|
||||
self.author = author
|
||||
|
||||
self.options = [
|
||||
SelectOption(
|
||||
label="10 Minutes", description="Trial for 10 minutes", value="10m"
|
||||
),
|
||||
SelectOption(
|
||||
label="1 Week", description="No prefix for 1 week", value="1w"
|
||||
),
|
||||
SelectOption(
|
||||
label="3 Weeks", description="No prefix for 3 weeks", value="3w"
|
||||
),
|
||||
SelectOption(
|
||||
label="1 Month", description="No prefix for 1 Month", value="1m"
|
||||
),
|
||||
SelectOption(
|
||||
label="3 Months", description="No prefix for 3 Months.", value="3m"
|
||||
),
|
||||
SelectOption(
|
||||
label="6 Months", description="No prefix for 6 Months.", value="6m"
|
||||
),
|
||||
SelectOption(
|
||||
label="1 Year", description="No prefix for 1 Year.", value="1y"
|
||||
),
|
||||
SelectOption(
|
||||
label="3 Years", description="No prefix for 3 Years.", value="3y"
|
||||
),
|
||||
SelectOption(
|
||||
label="Lifetime", description="No prefix Permanently.", value="lifetime"
|
||||
),
|
||||
]
|
||||
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
if interaction.user != self.author:
|
||||
return await interaction.response.send_message(
|
||||
"You can't select this option.", ephemeral=True
|
||||
)
|
||||
|
||||
duration_mapping = {
|
||||
"10m": timedelta(minutes=10),
|
||||
"1w": timedelta(weeks=1),
|
||||
"3w": timedelta(weeks=3),
|
||||
"1m": timedelta(days=30),
|
||||
"3m": timedelta(days=90),
|
||||
"6m": timedelta(days=180),
|
||||
"1y": timedelta(days=365),
|
||||
"3y": timedelta(days=365 * 3),
|
||||
"lifetime": None,
|
||||
}
|
||||
|
||||
selected_duration = self.values[0]
|
||||
expiry_time = None
|
||||
|
||||
if selected_duration != "lifetime":
|
||||
expiry_time = datetime.utcnow() + duration_mapping[selected_duration]
|
||||
expiry_str = expiry_time.isoformat()
|
||||
else:
|
||||
expiry_str = None
|
||||
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute(
|
||||
"INSERT INTO np (id, expiry_time) VALUES (?, ?)",
|
||||
(self.user.id, expiry_str),
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
expiry_text = (
|
||||
"**Lifetime**"
|
||||
if selected_duration == "lifetime"
|
||||
else f"{expiry_time.strftime('%Y-%m-%d %H:%M:%S')} UTC"
|
||||
)
|
||||
expiry_timestamp = (
|
||||
"None (Permanent)"
|
||||
if selected_duration == "lifetime"
|
||||
else f"<t:{int(expiry_time.timestamp())}:f>"
|
||||
)
|
||||
|
||||
guild = interaction.client.get_guild(1401125905677553716)
|
||||
if guild:
|
||||
member = guild.get_member(self.user.id)
|
||||
if member:
|
||||
role = guild.get_role(1401134167873290311)
|
||||
if role:
|
||||
await member.add_roles(role, reason="No prefix added")
|
||||
|
||||
log_channel = interaction.client.get_channel(1396794297386532978)
|
||||
if log_channel:
|
||||
embed = CV2Embed(
|
||||
title="User Added to No Prefix",
|
||||
description=f"**User**: [{self.user}](https://discord.com/users/{self.user.id})\n**User Mention**: {self.user.mention}\n** ID**: {self.user.id}\n\n** Added By**: [{self.author.display_name}](https://discord.com/users/{self.author.id})\n{TIMER_ALT1}**Expiry Time**: {expiry_text}\n{ZDIL} **Timestamp**: {expiry_timestamp}\n\n{PREMIUM} **Tier**: **{self.values[0].upper()}**",
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed.set_thumbnail(
|
||||
url=self.user.avatar.url
|
||||
if self.user.avatar
|
||||
else self.user.default_avatar.url
|
||||
)
|
||||
await log_channel.send("<#1396794297386532978>", view=embed)
|
||||
|
||||
embed = CV2Embed(
|
||||
description=f"**Added Global No Prefix**:\n{ZHUMAN} User: **{self.user.mention}**\n{MENTION} User Mention: {self.user.mention}\n{ZYROXSYS} User ID: {self.user.id}\n\n__**Additional Info**__:\n{ZYROXHAMMER} Added By: **{self.author.display_name}**\n{TIME} Expiry Time: {expiry_text}\n{BOOST} Timestamp: {expiry_timestamp}",
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed.set_author(
|
||||
name="Added No Prefix",
|
||||
icon_url="https://cdn.discordapp.com/icons/1166303696263585852/eeb00b2cf541438e88cdf842394c5b30.png?size=1024",
|
||||
)
|
||||
embed.set_footer(
|
||||
text="DM will be sent to the user in case No prefix is expired."
|
||||
)
|
||||
await interaction.response.edit_message(view=embed)
|
||||
|
||||
|
||||
class TimeSelectView(View):
|
||||
def __init__(self, user, db_path, author):
|
||||
super().__init__()
|
||||
self.user = user
|
||||
self.db_path = db_path
|
||||
self.author = author
|
||||
self.add_item(TimeSelect(user, db_path, author))
|
||||
|
||||
|
||||
from utils.config import BotName
|
||||
|
||||
class NoPrefix(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.staff = set()
|
||||
self.db_path = "db/np.db"
|
||||
self.client.loop.create_task(self.load_staff())
|
||||
self.client.loop.create_task(self.setup_database())
|
||||
self.expiry_check.start()
|
||||
|
||||
async def setup_database(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS np (
|
||||
id INTEGER PRIMARY KEY
|
||||
)
|
||||
""")
|
||||
|
||||
async with db.execute("PRAGMA table_info(np);") as cursor:
|
||||
columns = [info[1] for info in await cursor.fetchall()]
|
||||
|
||||
if "expiry_time" not in columns:
|
||||
await db.execute("""
|
||||
ALTER TABLE np ADD COLUMN expiry_time TEXT NULL;
|
||||
""")
|
||||
|
||||
await db.execute("""
|
||||
UPDATE np
|
||||
SET expiry_time = NULL
|
||||
WHERE expiry_time IS NULL;
|
||||
""")
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS autonp (
|
||||
guild_id INTEGER PRIMARY KEY
|
||||
)
|
||||
""")
|
||||
|
||||
await db.commit()
|
||||
|
||||
async def load_staff(self):
|
||||
await self.client.wait_until_ready()
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute("SELECT id FROM staff") as cursor:
|
||||
self.staff = {row[0] for row in await cursor.fetchall()}
|
||||
|
||||
@tasks.loop(minutes=10)
|
||||
async def expiry_check(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
now = datetime.utcnow().isoformat()
|
||||
async with db.execute(
|
||||
"SELECT id FROM np WHERE expiry_time IS NOT NULL AND expiry_time <= ?",
|
||||
(now,),
|
||||
) as cursor:
|
||||
expired_users = [row[0] for row in await cursor.fetchall()]
|
||||
|
||||
if expired_users:
|
||||
async with db.execute(
|
||||
"DELETE FROM np WHERE id IN ({})".format(
|
||||
",".join("?" * len(expired_users))
|
||||
),
|
||||
expired_users,
|
||||
):
|
||||
await db.commit()
|
||||
|
||||
for user_id in expired_users:
|
||||
user = self.client.get_user(user_id)
|
||||
if user:
|
||||
log_channel = self.client.get_channel(1396794297386532978)
|
||||
if log_channel:
|
||||
embed_log = CV2Embed(
|
||||
title="No Prefix Expired",
|
||||
description=(
|
||||
f"**User**: [{user}](https://discord.com/users/{user.id})\n"
|
||||
f"**User Mention**: {user.mention}\n"
|
||||
f"**ID**: {user.id}\n\n"
|
||||
f"**Removed By**: **{BotName}**\n"
|
||||
),
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed_log.set_thumbnail(
|
||||
url=user.display_avatar.url
|
||||
if user.avatar
|
||||
else user.default_avatar.url
|
||||
)
|
||||
embed_log.set_footer(text="No Prefix Removal Log")
|
||||
await log_channel.send(
|
||||
"<#1396794297386532978>", view=embed_log
|
||||
)
|
||||
bot = self.client
|
||||
guild = bot.get_guild(1401125905677553716)
|
||||
if guild:
|
||||
member = guild.get_member(user.id)
|
||||
if member:
|
||||
role = guild.get_role(1401134167873290311)
|
||||
if role in member.roles:
|
||||
await member.remove_roles(role)
|
||||
|
||||
embed = CV2Embed(
|
||||
description=f"{ICONS_WARNING} Your No Prefix status has **Expired**. You will now require the prefix to use commands.",
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed.set_author(
|
||||
name="No Prefix Expired",
|
||||
icon_url=user.avatar.url
|
||||
if user.avatar
|
||||
else user.default_avatar.url,
|
||||
)
|
||||
|
||||
embed.set_footer(
|
||||
text=f"{BRAND_NAME} - No Prefix, Join support to regain access."
|
||||
)
|
||||
support = Button(
|
||||
label="Support",
|
||||
style=discord.ButtonStyle.link,
|
||||
url=f"https://discord.gg/codexdev",
|
||||
)
|
||||
view = View()
|
||||
view.add_item(support)
|
||||
|
||||
try:
|
||||
add_action_rows(embed, view.children)
|
||||
await user.send(f"{user.mention}", view=embed)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
@expiry_check.before_loop
|
||||
async def before_expiry_check(self):
|
||||
await self.client.wait_until_ready()
|
||||
|
||||
@commands.group(
|
||||
name="np",
|
||||
help="Allows you to add someone to the no-prefix list (owner-only command)",
|
||||
)
|
||||
@commands.check(is_owner_or_staff)
|
||||
async def _np(self, ctx):
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
@_np.command(name="list", help="List of no-prefix users")
|
||||
@commands.check(is_owner_or_staff)
|
||||
async def np_list(self, ctx):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute("SELECT id FROM np") as cursor:
|
||||
ids = [row[0] for row in await cursor.fetchall()]
|
||||
if not ids:
|
||||
await ctx.reply(
|
||||
f"No users in the no-prefix list.", mention_author=False
|
||||
)
|
||||
return
|
||||
entries = [
|
||||
f"`#{no + 1}` [Profile URL](https://discord.com/users/{mem}) (ID: {mem})"
|
||||
for no, mem in enumerate(ids, start=0)
|
||||
]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"No Prefix Users [{len(ids)}]",
|
||||
description="",
|
||||
per_page=10,
|
||||
color=0xFF0000,
|
||||
),
|
||||
ctx=ctx,
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
@_np.command(name="add", help="Add user to no-prefix with time options")
|
||||
@commands.check(is_owner_or_staff)
|
||||
async def np_add(self, ctx, user: discord.User):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT id FROM np WHERE id = ?", (user.id,)
|
||||
) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
if result:
|
||||
embed = CV2Embed(
|
||||
description=f"**{user}** is Already in No prefix list\n\n **Requested By**: [{ctx.author.display_name}](https://discord.com/users/{ctx.author.id})\n",
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed.set_author(name="Error")
|
||||
await ctx.reply(view=embed)
|
||||
return
|
||||
|
||||
view = TimeSelectView(user, self.db_path, ctx.author)
|
||||
from discord.ui import ActionRow
|
||||
|
||||
embed = CV2Embed(
|
||||
title="Select No Prefix Duration",
|
||||
description="**Choose the duration for how long no-prefix should be enabled for this user:**",
|
||||
color=0xFF0000,
|
||||
)
|
||||
for item in view.children:
|
||||
embed.children[0].add_item(ActionRow(item))
|
||||
await ctx.reply(view=embed)
|
||||
|
||||
@_np.command(name="remove", help="Remove user from no-prefix")
|
||||
@commands.check(is_owner_or_staff)
|
||||
async def np_remove(self, ctx, user: discord.User):
|
||||
async with aiosqlite.connect("db/np.db") as db:
|
||||
async with db.execute(
|
||||
"SELECT id FROM np WHERE id = ?", (user.id,)
|
||||
) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
if not result:
|
||||
embed = CV2Embed(
|
||||
description=f"**{user}** is Not in the No Prefix list\n\n{U_ADMIN} **Requested By**: [{ctx.author.display_name}](https://discord.com/users/{ctx.author.id})\n",
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed.set_author(name="Error")
|
||||
await ctx.reply(view=embed)
|
||||
return
|
||||
|
||||
await db.execute("DELETE FROM np WHERE id = ?", (user.id,))
|
||||
await db.commit()
|
||||
|
||||
guild = ctx.bot.get_guild(1401125905677553716)
|
||||
if guild:
|
||||
member = guild.get_member(user.id)
|
||||
if member:
|
||||
role = guild.get_role(1401134167873290311)
|
||||
if role in member.roles:
|
||||
await member.remove_roles(role)
|
||||
|
||||
embed = CV2Embed(
|
||||
description=(
|
||||
f"**User**: [{user}](https://discord.com/users/{user.id})\n"
|
||||
f"**User Mention**: {user.mention}\n"
|
||||
f"** ID**: {user.id}\n\n"
|
||||
f"** Removed By**: **{BotName}**\n"
|
||||
),
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed.set_author(name="Removed No Prefix")
|
||||
await ctx.reply(view=embed)
|
||||
|
||||
log_channel = ctx.bot.get_channel(1396794297386532978)
|
||||
if log_channel:
|
||||
embed_log = CV2Embed(
|
||||
title="No Prefix Removed",
|
||||
description=(
|
||||
f"**User**: [{user}](https://discord.com/users/{user.id})\n"
|
||||
f"**User Mention**: {user.mention}\n"
|
||||
f"** ID**: {user.id}\n\n"
|
||||
f"**Removed By**: **{BotName}**\n"
|
||||
),
|
||||
color=0xFF0000,
|
||||
)
|
||||
embed_log.set_thumbnail(
|
||||
url=user.display_avatar.url if user.avatar else user.default_avatar.url
|
||||
)
|
||||
embed_log.set_footer(text="No Prefix Removal Log")
|
||||
await log_channel.send("<@870179991462236170>", view=embed_log)
|
||||
|
||||
@_np.command(
|
||||
name="status", help="Check if a user is in the No Prefix list and show details."
|
||||
)
|
||||
@commands.check(is_owner_or_staff)
|
||||
async def np_status(self, ctx, user: discord.User):
|
||||
async with aiosqlite.connect("db/np.db") as db:
|
||||
async with db.execute(
|
||||
"SELECT id, expiry_time FROM np WHERE id = ?", (user.id,)
|
||||
) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
|
||||
if not result:
|
||||
embed = CV2Embed(
|
||||
title="No Prefix Status",
|
||||
description=f"**{user}** is Not in the No Prefix list\n\n"
|
||||
f"**Requested By**: "
|
||||
f"[{ctx.author.display_name}](https://discord.com/users/{ctx.author.id})\n",
|
||||
color=0xFF0000,
|
||||
)
|
||||
await ctx.reply(view=embed)
|
||||
return
|
||||
|
||||
user_id, expires = result
|
||||
|
||||
if expires and expires != "Null":
|
||||
expire_time = datetime.fromisoformat(expires)
|
||||
expire_timestamp = f"<t:{int(expire_time.timestamp())}:F>"
|
||||
else:
|
||||
expire_time = "Lifetime"
|
||||
expire_timestamp = "Lifetime"
|
||||
|
||||
embed = CV2Embed(
|
||||
title="No Prefix Status",
|
||||
description=(
|
||||
f"**User**: [{user}](https://discord.com/users/{user.id})\n"
|
||||
f"**User ID**: {user.id}\n\n"
|
||||
f"{TIME} Expiry: {expire_time} ({expire_timestamp})"
|
||||
),
|
||||
color=0xFF0000,
|
||||
)
|
||||
|
||||
embed.set_thumbnail(
|
||||
url=user.display_avatar.url if user.avatar else user.default_avatar.url
|
||||
)
|
||||
|
||||
await ctx.reply(view=embed)
|
||||
|
||||
@commands.group(name="autonp", help="Manage auto no-prefix for partner guilds.")
|
||||
@commands.is_owner()
|
||||
async def autonp(self, ctx):
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
@autonp.group(name="guild", help="Manage partner guilds for auto no-prefix.")
|
||||
async def autonp_guild(self, ctx):
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
@autonp_guild.command(name="add", help="Add a guild to auto no-prefix.")
|
||||
async def add_guild(self, ctx, guild_id: int):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM autonp WHERE guild_id = ?", (guild_id,)
|
||||
) as cursor:
|
||||
if await cursor.fetchone():
|
||||
await ctx.reply("Guild is already added.")
|
||||
return
|
||||
await db.execute("INSERT INTO autonp (guild_id) VALUES (?)", (guild_id,))
|
||||
await db.commit()
|
||||
await ctx.reply(f"Guild {guild_id} added to auto no-prefix.")
|
||||
|
||||
@autonp_guild.command(name="remove", help="Remove a guild from auto no-prefix.")
|
||||
async def remove_guild(self, ctx, guild_id: int):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM autonp WHERE guild_id = ?", (guild_id,)
|
||||
) as cursor:
|
||||
if not await cursor.fetchone():
|
||||
await ctx.reply("Guild is not in auto no-prefix.")
|
||||
return
|
||||
await db.execute("DELETE FROM autonp WHERE guild_id = ?", (guild_id,))
|
||||
await db.commit()
|
||||
await ctx.reply(f"Guild {guild_id} removed from auto no-prefix.")
|
||||
|
||||
@autonp_guild.command(name="list", help="List all guilds with auto no-prefix.")
|
||||
@commands.check(is_owner_or_staff)
|
||||
async def list_guilds(self, ctx):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute("SELECT guild_id FROM autonp") as cursor:
|
||||
guilds = [row[0] for row in await cursor.fetchall()]
|
||||
if not guilds:
|
||||
await ctx.reply(
|
||||
"No guilds in auto no-prefix.", mention_author=False
|
||||
)
|
||||
return
|
||||
await ctx.reply(
|
||||
f"Guilds in auto no-prefix:\n" + "\n".join(str(g) for g in guilds),
|
||||
mention_author=False,
|
||||
)
|
||||
|
||||
async def is_user_in_np(self, user_id):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM np WHERE id = ?", (user_id,)
|
||||
) as cursor:
|
||||
return await cursor.fetchone() is not None
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_update(self, before, after):
|
||||
if before.premium_since is None and after.premium_since is not None:
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM autonp WHERE guild_id = ?", (after.guild.id,)
|
||||
) as cursor:
|
||||
if not await cursor.fetchone():
|
||||
return
|
||||
if not await self.is_user_in_np(after.id):
|
||||
await self.add_np(after, timedelta(days=60))
|
||||
log_channel = self.client.get_channel(1406566123234787378)
|
||||
embed = CV2Embed(
|
||||
title="Added No prefix due to Boosting Partner Server",
|
||||
description=f"**User**: **[{after}](https://discord.com/users/{after.id})** (ID: {after.id})\n**Server**: {after.guild.name}",
|
||||
color=0xFF0000,
|
||||
)
|
||||
message = await log_channel.send("<@870179991462236170>", view=embed)
|
||||
await message.publish()
|
||||
|
||||
elif before.premium_since is not None and after.premium_since is None:
|
||||
await self.handle_boost_removal(after)
|
||||
|
||||
# @commands.Cog.listener()
|
||||
# async def on_member_remove(self, member):
|
||||
# await self.handle_boost_removal(member)
|
||||
|
||||
async def handle_boost_removal(self, user):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT 1 FROM autonp WHERE guild_id = ?", (user.guild.id,)
|
||||
) as cursor:
|
||||
if not await cursor.fetchone():
|
||||
return
|
||||
if await self.is_user_in_np(user.id):
|
||||
await self.remove_np(user)
|
||||
log_channel = self.client.get_channel(1406566257070964756)
|
||||
embed = CV2Embed(
|
||||
title="Removed No prefix due to Unboosting Partner Server",
|
||||
description=f"**User**: **[{user}](https://discord.com/users/{user.id})** (ID: {user.id})\n**Server**: {user.guild.name}",
|
||||
color=0xFF0000,
|
||||
)
|
||||
message = await log_channel.send("<@870179991462236170>", view=embed)
|
||||
await message.publish()
|
||||
|
||||
async def add_np(self, user, duration):
|
||||
expiry_time = datetime.utcnow() + duration
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute(
|
||||
"INSERT INTO np (id, expiry_time) VALUES (?, ?)",
|
||||
(user.id, expiry_time.isoformat()),
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
embed = CV2Embed(
|
||||
title="Congratulations you got 2 months No Prefix!",
|
||||
description=f"You've been credited 2 months of global No Prefix for boosting our Partnered Servers. You can now use my commands without prefix. If you wish to remove it, please reach out [Support Server](https://discord.gg/codexdev).",
|
||||
color=0xFF0000,
|
||||
)
|
||||
try:
|
||||
await user.send(view=embed)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
guild = self.client.get_guild(1401125905677553716)
|
||||
if guild:
|
||||
member = guild.get_member(user.id)
|
||||
if member is not None:
|
||||
role = guild.get_role(1401134167873290311)
|
||||
if role:
|
||||
await member.add_roles(role)
|
||||
|
||||
async def remove_np(self, user):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute(
|
||||
"SELECT expiry_time FROM np WHERE id = ?", (user.id,)
|
||||
) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row is None or row[0] is None:
|
||||
return
|
||||
|
||||
await db.execute("DELETE FROM np WHERE id = ?", (user.id,))
|
||||
await db.commit()
|
||||
|
||||
embed = CV2Embed(
|
||||
title=f"{ICONS_WARNING} Global No Prefix Expired",
|
||||
description=f"Hey {user.mention}, your global no prefix has expired!\n\n__**Reason:**__ Unboosting our partnered Server.\nIf you think this is a mistake then please reach out [Support Server](https://discord.gg/codexdev).",
|
||||
color=0xFF0000,
|
||||
)
|
||||
|
||||
try:
|
||||
await user.send(view=embed)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
guild = self.client.get_guild(1401125905677553716)
|
||||
if guild:
|
||||
member = guild.get_member(user.id)
|
||||
if member is not None:
|
||||
role = guild.get_role(1401134167873290311)
|
||||
if role and role in member.roles:
|
||||
await member.remove_roles(role)
|
||||
|
||||
@_np.command(name="reset", help="Reset/clear all users from the no-prefix list")
|
||||
@commands.is_owner()
|
||||
async def np_reset(self, ctx):
|
||||
# Confirmation message
|
||||
embed = CV2Embed(
|
||||
title="Confirm Reset",
|
||||
description="Are you sure you want to remove **ALL** users from the no-prefix list? This action cannot be undone.",
|
||||
color=0xFF0000,
|
||||
)
|
||||
|
||||
# Create confirmation buttons
|
||||
yes_button = Button(label="Yes", style=discord.ButtonStyle.danger)
|
||||
no_button = Button(label="No", style=discord.ButtonStyle.secondary)
|
||||
|
||||
view = View()
|
||||
view.add_item(yes_button)
|
||||
view.add_item(no_button)
|
||||
|
||||
# Define button callbacks
|
||||
async def yes_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message(
|
||||
"This interaction is not for you.", ephemeral=True
|
||||
)
|
||||
|
||||
# Remove all users from no-prefix list
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
# Get count of users before deletion
|
||||
async with db.execute("SELECT COUNT(*) FROM np") as cursor:
|
||||
count = (await cursor.fetchone())[0]
|
||||
|
||||
# Delete all entries
|
||||
await db.execute("DELETE FROM np")
|
||||
await db.commit()
|
||||
|
||||
# Remove roles from all members in the main guild
|
||||
guild = self.client.get_guild(1401125905677553716)
|
||||
if guild:
|
||||
role = guild.get_role(1401134167873290311)
|
||||
if role:
|
||||
members_with_role = [
|
||||
member for member in guild.members if role in member.roles
|
||||
]
|
||||
for member in members_with_role:
|
||||
try:
|
||||
await member.remove_roles(
|
||||
role, reason="Global no-prefix reset"
|
||||
)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
# Send success message
|
||||
success_embed = CV2Embed(
|
||||
title=f"{TICK} No-Prefix Reset Complete",
|
||||
description=f"Successfully removed {count} users from the no-prefix list.",
|
||||
color=0xFF0000,
|
||||
)
|
||||
await interaction.response.edit_message(view=success_embed)
|
||||
|
||||
# Log the action
|
||||
log_channel = self.client.get_channel(1396794297386532978)
|
||||
if log_channel:
|
||||
log_embed = CV2Embed(
|
||||
title="No-Prefix List Reset",
|
||||
description=f"**Reset By**: [{ctx.author.display_name}](https://discord.com/users/{ctx.author.id})\n{INFO} Users Removed: {count}",
|
||||
color=0xFF0000,
|
||||
)
|
||||
log_embed.set_footer(text="No Prefix Reset Log")
|
||||
await log_channel.send("<#1396794297386532978>", view=log_embed)
|
||||
|
||||
async def no_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message(
|
||||
"This interaction is not for you.", ephemeral=True
|
||||
)
|
||||
|
||||
cancel_embed = CV2Embed(
|
||||
title="Reset Cancelled",
|
||||
description="No changes have been made to the no-prefix list.",
|
||||
color=0xFF0000,
|
||||
)
|
||||
await interaction.response.edit_message(view=cancel_embed)
|
||||
|
||||
yes_button.callback = yes_callback
|
||||
no_button.callback = no_callback
|
||||
|
||||
add_action_rows(embed, view.children)
|
||||
await ctx.reply(view=embed)
|
||||
820
bot/cogs/commands/owner.py
Normal file
820
bot/cogs/commands/owner.py
Normal file
@@ -0,0 +1,820 @@
|
||||
from __future__ import annotations
|
||||
from discord.ext import commands
|
||||
from discord import *
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||
import discord
|
||||
import json
|
||||
import datetime
|
||||
import asyncio
|
||||
import aiosqlite
|
||||
from typing import Optional
|
||||
from utils import Paginator, DescriptionEmbedPaginator, FieldPagePaginator, TextPaginator
|
||||
from utils.Tools import *
|
||||
from utils.config import OWNER_IDS
|
||||
from utils.emoji import BOOSTS, DISCORD_BADGE_EMOJIS, LOADINGRED, NITRO_BOOST, TICK, ZWARNING
|
||||
from core import Cog, zyrox, Context
|
||||
import sqlite3
|
||||
import os
|
||||
import requests
|
||||
import numpy as np
|
||||
from io import BytesIO
|
||||
from utils.config import OWNER_IDS
|
||||
from discord.errors import Forbidden
|
||||
from discord import Embed
|
||||
from discord.ui import Button, View
|
||||
from utils.config import *
|
||||
|
||||
# p1
|
||||
|
||||
|
||||
# --- Configuration & Helpers ---
|
||||
OWNER_IDS = [1258831252748894436]
|
||||
|
||||
# Your custom bot badges, including Family and Developer
|
||||
BADGE_URLS = {
|
||||
"owner": "https://cdn.discordapp.com/emojis/1448951721479901334.png?v=1&size=48&quality=lossless",
|
||||
"staff": "https://cdn.discordapp.com/emojis/1448949616681812098.png?v=1&size=48&quality=lossless",
|
||||
"partner": "https://cdn.discordapp.com/emojis/1448949605676093540.png?v=1&size=48&quality=lossless",
|
||||
"sponsor": "https://cdn.discordapp.com/emojis/1448949571811282984.png?v=1&size=48&quality=lossless",
|
||||
"friend": "https://cdn.discordapp.com/emojis/1448951509235531869.png?v=1&size=48&quality=lossless",
|
||||
"early": "https://cdn.discordapp.com/emojis/1448949582573867039.png?v=1&size=48&quality=lossless",
|
||||
"vip": "https://cdn.discordapp.com/emojis/1448951307707748395.png?v=1&size=48&quality=lossless",
|
||||
"bug": "https://cdn.discordapp.com/emojis/1448949593923518485.png?v=1&size=48&quality=lossless",
|
||||
"developer": "https://cdn.discordapp.com/emojis/1448951697853386826.png?v=1&size=48&quality=lossless",
|
||||
"family": "https://cdn.discordapp.com/emojis/1448951456861519962.png?v=1&size=48&quality=lossless", # New Family Badge
|
||||
}
|
||||
|
||||
BADGE_NAMES = {
|
||||
"owner": "Owner", "staff": "Staff", "partner": "Partner",
|
||||
"sponsor": "Sponsor", "friend": "Friends", "early": "Early Supporter",
|
||||
"vip": "VIP", "bug": "Bug Hunter", "developer": "Developer", "family": "Family"
|
||||
}
|
||||
|
||||
# Emojis for official Discord badges (imported from utils.emoji)
|
||||
# Keep reference for backward compatibility
|
||||
BACK_COMPAT_BADGE_DICT = DISCORD_BADGE_EMOJIS
|
||||
|
||||
|
||||
db_folder = 'db'
|
||||
db_file = 'badges.db'
|
||||
db_path = os.path.join(db_folder, db_file)
|
||||
FONT_PATH = os.path.join('utils', 'arial.ttf')
|
||||
|
||||
# --- Database Setup ---
|
||||
os.makedirs(db_folder, exist_ok=True)
|
||||
conn = sqlite3.connect(db_path)
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute('CREATE TABLE IF NOT EXISTS badges (user_id INTEGER PRIMARY KEY)')
|
||||
conn.commit()
|
||||
|
||||
for badge_name in BADGE_NAMES:
|
||||
try:
|
||||
c.execute(f"ALTER TABLE badges ADD COLUMN {badge_name} INTEGER DEFAULT 0")
|
||||
conn.commit()
|
||||
except sqlite3.OperationalError:
|
||||
pass
|
||||
|
||||
# --- Helper Functions ---
|
||||
def add_badge(user_id, badge):
|
||||
try:
|
||||
if badge not in BADGE_URLS: return False
|
||||
c.execute("SELECT 1 FROM badges WHERE user_id = ?", (user_id,))
|
||||
if c.fetchone() is None:
|
||||
c.execute(f"INSERT INTO badges (user_id, {badge}) VALUES (?, 1)", (user_id,))
|
||||
else:
|
||||
c.execute(f"UPDATE badges SET {badge} = 1 WHERE user_id = ?", (user_id,))
|
||||
conn.commit()
|
||||
return True
|
||||
except sqlite3.Error:
|
||||
return False
|
||||
|
||||
def remove_badge(user_id, badge):
|
||||
try:
|
||||
if badge not in BADGE_URLS: return False
|
||||
c.execute(f"UPDATE badges SET {badge} = 0 WHERE user_id = ?", (user_id,))
|
||||
conn.commit()
|
||||
return True
|
||||
except sqlite3.Error:
|
||||
return False
|
||||
|
||||
async def is_owner_or_staff(ctx):
|
||||
owner_cog = ctx.bot.get_cog("Owner")
|
||||
is_staff_member = False
|
||||
if owner_cog:
|
||||
is_staff_member = ctx.author.id in getattr(owner_cog, 'staff', set())
|
||||
return is_staff_member or ctx.author.id in OWNER_IDS
|
||||
|
||||
def blacklist_check():
|
||||
async def predicate(ctx): return True
|
||||
return commands.check(predicate)
|
||||
|
||||
def ignore_check():
|
||||
async def predicate(ctx): return True
|
||||
return commands.check(predicate)
|
||||
|
||||
|
||||
from utils.config import BotName
|
||||
|
||||
class Owner(commands.Cog):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.staff = set()
|
||||
self.np_cache = []
|
||||
self.db_path = 'db/np.db'
|
||||
self.stop_tour = False
|
||||
self.bot_owner_ids = [870179991462236170,1432771000629596225,1382744437049790495]
|
||||
self.client.loop.create_task(self.setup_database())
|
||||
self.client.loop.create_task(self.load_staff())
|
||||
|
||||
|
||||
async def setup_database(self):
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS staff (
|
||||
id INTEGER PRIMARY KEY
|
||||
)
|
||||
''')
|
||||
await db.commit()
|
||||
|
||||
|
||||
|
||||
async def load_staff(self):
|
||||
await self.client.wait_until_ready()
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
async with db.execute('SELECT id FROM staff') as cursor:
|
||||
self.staff = {row[0] for row in await cursor.fetchall()}
|
||||
|
||||
@commands.command(name="staff_add", aliases=["staffadd", "addstaff"], help="Adds a user to the staff list.")
|
||||
@commands.is_owner()
|
||||
async def staff_add(self, ctx, user: discord.User):
|
||||
if user.id in self.staff:
|
||||
sonu = discord.Embed(title=f"{ZWARNING} Access Denied", description=f"{user} is already in the staff list.", color=0xFF0000)
|
||||
await ctx.reply(embed=sonu, mention_author=False)
|
||||
else:
|
||||
self.staff.add(user.id)
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute('INSERT OR IGNORE INTO staff (id) VALUES (?)', (user.id,))
|
||||
await db.commit()
|
||||
sonu2 = discord.Embed(title=f"{TICK} Success", description=f"Added {user} to the staff list.", color=0xFF0000)
|
||||
await ctx.reply(embed=sonu2, mention_author=False)
|
||||
|
||||
@commands.command(name="staff_remove", aliases=["staffremove", "removestaff"], help="Removes a user from the staff list.")
|
||||
@commands.is_owner()
|
||||
async def staff_remove(self, ctx, user: discord.User):
|
||||
if user.id not in self.staff:
|
||||
sonu = discord.Embed(title=f"{ZWARNING} Access Denied", description=f"{user} is not in the staff list.", color=0xFF0000)
|
||||
await ctx.reply(embed=sonu, mention_author=False)
|
||||
else:
|
||||
self.staff.remove(user.id)
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute('DELETE FROM staff WHERE id = ?', (user.id,))
|
||||
await db.commit()
|
||||
sonu2 = discord.Embed(title=f"{TICK} Success", description=f"Removed {user} from the staff list.", color=0xFF0000)
|
||||
await ctx.reply(embed=sonu2, mention_author=False)
|
||||
|
||||
@commands.command(name="staff_list", aliases=["stafflist", "liststaff", "staffs"], help="Lists all staff members.")
|
||||
@commands.is_owner()
|
||||
async def staff_list(self, ctx):
|
||||
if not self.staff:
|
||||
await ctx.send("The staff list is currently empty.")
|
||||
else:
|
||||
member_list = []
|
||||
for staff_id in self.staff:
|
||||
member = await self.client.fetch_user(staff_id)
|
||||
member_list.append(f"{member.name}#{member.discriminator} (ID: {staff_id})")
|
||||
staff_display = "\n".join(member_list)
|
||||
sonu = discord.Embed(title=f"{TICK} {BotName} Staffs", description=f"\n{staff_display}", color=0xFF0000)
|
||||
await ctx.send(embed=sonu)
|
||||
|
||||
@commands.command(name="slist")
|
||||
@commands.check(is_owner_or_staff)
|
||||
async def _slist(self, ctx):
|
||||
servers = sorted(self.client.guilds, key=lambda g: g.member_count, reverse=True)
|
||||
entries = [
|
||||
f"`#{i}` | [{g.name}](https://discord.com/guilds/{g.id}) - {g.member_count}"
|
||||
for i, g in enumerate(servers, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
description="",
|
||||
title=f"Guild List of {BotName} [{len(self.client.guilds)}]",
|
||||
color=0xFF0000,
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
|
||||
@commands.command(name="mutuals", aliases=["mutual"])
|
||||
@commands.is_owner()
|
||||
async def mutuals(self, ctx, user: discord.User):
|
||||
guilds = [guild for guild in self.client.guilds if user in guild.members]
|
||||
entries = [
|
||||
f"`#{no}` | [{guild.name}](https://discord.com/channels/{guild.id}) - {guild.member_count}"
|
||||
for no, guild in enumerate(guilds, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
description="",
|
||||
title=f"Mutual Guilds of {user.name} [{len(guilds)}]",
|
||||
color=0xFF0000,
|
||||
per_page=10),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
@commands.command(name="getinvite", aliases=["gi", "guildinvite"])
|
||||
@commands.is_owner()
|
||||
async def getinvite(self, ctx: Context, guild= discord.Guild):
|
||||
if not guild:
|
||||
await ctx.send("Invalid server.")
|
||||
return
|
||||
|
||||
perms_ha = guild.me.guild_permissions.view_audit_log
|
||||
invite_krskta = guild.me.guild_permissions.create_instant_invite
|
||||
|
||||
try:
|
||||
invites = await guild.invites()
|
||||
if invites:
|
||||
entries = [f"{invite.url} - {invite.uses} uses" for invite in invites]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"Active Invites for {guild.name}",
|
||||
description="",
|
||||
per_page=10,
|
||||
color=0xFF0000),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
elif invite_krskta:
|
||||
channel = guild.system_channel or next((ch for ch in guild.text_channels if ch.permissions_for(guild.me).create_instant_invite), None)
|
||||
if channel:
|
||||
invite = await channel.create_invite(max_age=86400, max_uses=1, reason="No active invites found, creating a new one.")
|
||||
await ctx.send(f"Created new invite: {invite.url}")
|
||||
else:
|
||||
await ctx.send("No channel found.")
|
||||
else:
|
||||
await ctx.send("Can't create invites.")
|
||||
except discord.Forbidden:
|
||||
await ctx.send("Forbidden.")
|
||||
|
||||
|
||||
@commands.command(name="reload", help="Restarts the client.")
|
||||
@commands.is_owner()
|
||||
async def _restart(self, ctx: Context):
|
||||
await ctx.reply(f"{TICK} | **Successfully Restarting {BotName} It Takes 10 seconds**")
|
||||
restart_program()
|
||||
|
||||
@commands.command(name="sync", help="Syncs all database.")
|
||||
@commands.is_owner()
|
||||
async def _sync(self, ctx):
|
||||
await ctx.reply("Syncing...", mention_author=False)
|
||||
with open('events.json', 'r') as f:
|
||||
data = json.load(f)
|
||||
for guild in self.client.guilds:
|
||||
if str(guild.id) not in data['guild']:
|
||||
data['guilds'][str(guild.id)] = 'on'
|
||||
with open('events.json', 'w') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
else:
|
||||
pass
|
||||
with open('config.json', 'r') as f:
|
||||
data = json.load(f)
|
||||
for op in data["guilds"]:
|
||||
g = self.client.get_guild(int(op))
|
||||
if not g:
|
||||
data["guilds"].pop(str(op))
|
||||
with open('config.json', 'w') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
|
||||
@commands.command(name="owners")
|
||||
@commands.is_owner()
|
||||
async def own_list(self, ctx):
|
||||
nplist = OWNER_IDS
|
||||
npl = ([await self.client.fetch_user(nplu) for nplu in nplist])
|
||||
npl = sorted(npl, key=lambda nop: nop.created_at)
|
||||
entries = [
|
||||
f"`#{no}` | [{mem}](https://discord.com/users/{mem.id}) (ID: {mem.id})"
|
||||
for no, mem in enumerate(npl, start=1)
|
||||
]
|
||||
paginator = Paginator(source=DescriptionEmbedPaginator(
|
||||
entries=entries,
|
||||
title=f"{BRAND_NAME} Owners [{len(nplist)}]",
|
||||
description="",
|
||||
per_page=10,
|
||||
color=0xFF0000),
|
||||
ctx=ctx)
|
||||
await paginator.paginate()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.command()
|
||||
@commands.is_owner()
|
||||
async def dm(self, ctx, user: discord.User, *, message: str):
|
||||
""" DM the user of your choice """
|
||||
try:
|
||||
await user.send(message)
|
||||
await ctx.send(f"{TICK} | Successfully Sent a DM to **{user}**")
|
||||
except discord.Forbidden:
|
||||
await ctx.send("This user might be having DMs blocked or it's a bot account...")
|
||||
|
||||
|
||||
|
||||
@commands.group()
|
||||
@commands.is_owner()
|
||||
async def change(self, ctx):
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help(str(ctx.command))
|
||||
|
||||
|
||||
@change.command(name="nickname")
|
||||
@commands.is_owner()
|
||||
async def change_nickname(self, ctx, *, name: str = None):
|
||||
""" Change nickname. """
|
||||
try:
|
||||
await ctx.guild.me.edit(nick=name)
|
||||
if name:
|
||||
await ctx.send(f"{TICK} | Successfully changed nickname to **{name}**")
|
||||
else:
|
||||
await ctx.send(f"{TICK} | Successfully removed nickname")
|
||||
except Exception as err:
|
||||
await ctx.send(err)
|
||||
|
||||
|
||||
@commands.command(name="ownerban", aliases=["forceban", "dna"])
|
||||
@commands.is_owner()
|
||||
async def _ownerban(self, ctx: Context, user_id: int, *, reason: str = "No reason provided"):
|
||||
|
||||
member = ctx.guild.get_member(user_id)
|
||||
if member:
|
||||
try:
|
||||
await member.ban(reason=reason)
|
||||
embed = discord.Embed(
|
||||
title="Successfully Banned",
|
||||
description=f"{TICK} | **{member.name}** has been successfully banned from {ctx.guild.name} by the Bot Owner.",
|
||||
color=0xFF0000)
|
||||
await ctx.reply(embed=embed, mention_author=False, delete_after=3)
|
||||
await ctx.message.delete()
|
||||
except discord.Forbidden:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description=f"{ZWARNING} I do not have permission to ban **{member.name}** in this guild.",
|
||||
color=0xFF0000
|
||||
)
|
||||
await ctx.reply(embed=embed, mention_author=False, delete_after=5)
|
||||
await ctx.message.delete()
|
||||
except discord.HTTPException:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description=f"{ZWARNING} An error occurred while banning **{member.name}**.",
|
||||
color=0xFF0000
|
||||
)
|
||||
await ctx.reply(embed=embed, mention_author=False, delete_after=5)
|
||||
await ctx.message.delete()
|
||||
else:
|
||||
await ctx.reply("User not found in this guild.", mention_author=False, delete_after=3)
|
||||
await ctx.message.delete()
|
||||
|
||||
@commands.command(name="ownerunban", aliases=["forceunban"])
|
||||
@commands.is_owner()
|
||||
async def _ownerunban(self, ctx: Context, user_id: int, *, reason: str = "No reason provided"):
|
||||
user = self.client.get_user(user_id)
|
||||
if user:
|
||||
try:
|
||||
await ctx.guild.unban(user, reason=reason)
|
||||
embed = discord.Embed(
|
||||
title="Successfully Unbanned",
|
||||
description=f"{TICK} | **{user.name}** has been successfully unbanned from {ctx.guild.name} by the Bot Owner.",
|
||||
color=0xFF0000
|
||||
)
|
||||
await ctx.reply(embed=embed, mention_author=False)
|
||||
except discord.Forbidden:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description=f"{ZWARNING} I do not have permission to unban **{user.name}** in this guild.",
|
||||
color=0xFF0000
|
||||
)
|
||||
await ctx.reply(embed=embed, mention_author=False)
|
||||
except discord.HTTPException:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description=f"{ZWARNING} An error occurred while unbanning **{user.name}**.",
|
||||
color=0xFF0000
|
||||
)
|
||||
await ctx.reply(embed=embed, mention_author=False)
|
||||
else:
|
||||
await ctx.reply("User not found.", mention_author=False)
|
||||
|
||||
|
||||
|
||||
@commands.command(name="globalunban")
|
||||
@commands.is_owner()
|
||||
async def globalunban(self, ctx: Context, user: discord.User):
|
||||
success_guilds = []
|
||||
error_guilds = []
|
||||
|
||||
for guild in self.client.guilds:
|
||||
bans = await guild.bans()
|
||||
if any(ban_entry.user.id == user.id for ban_entry in bans):
|
||||
try:
|
||||
await guild.unban(user, reason="Global Unban")
|
||||
success_guilds.append(guild.name)
|
||||
except discord.HTTPException:
|
||||
error_guilds.append(guild.name)
|
||||
except discord.Forbidden:
|
||||
error_guilds.append(guild.name)
|
||||
|
||||
user_mention = f"{user.mention} (**{user.name}**)"
|
||||
|
||||
success_message = f"Successfully unbanned {user_mention} from the following guild(s):\n{', '.join(success_guilds)}" if success_guilds else "No guilds where the user was successfully unbanned."
|
||||
error_message = f"Failed to unban {user_mention} from the following guild(s):\n{', '.join(error_guilds)}" if error_guilds else "No errors during unbanning."
|
||||
|
||||
await ctx.reply(f"{success_message}\n{error_message}", mention_author=False)
|
||||
|
||||
@commands.command(name="guildban")
|
||||
@commands.is_owner()
|
||||
async def guildban(self, ctx: Context, guild_id: int, user_id: int, *, reason: str = "No reason provided"):
|
||||
guild = self.client.get_guild(guild_id)
|
||||
if not guild:
|
||||
await ctx.reply("Bot is not present in the specified guild.", mention_author=False)
|
||||
return
|
||||
|
||||
member = guild.get_member(user_id)
|
||||
if member:
|
||||
try:
|
||||
await guild.ban(member, reason=reason)
|
||||
await ctx.reply(f"Successfully banned **{member.name}** from {guild.name}.", mention_author=False)
|
||||
except discord.Forbidden:
|
||||
await ctx.reply(f"Missing permissions to ban **{member.name}** in {guild.name}.", mention_author=False)
|
||||
except discord.HTTPException as e:
|
||||
await ctx.reply(f"An error occurred while banning **{member.name}** in {guild.name}: {str(e)}", mention_author=False)
|
||||
else:
|
||||
await ctx.reply(f"User not found in the specified guild {guild.name}.", mention_author=False)
|
||||
|
||||
@commands.command(name="guildunban")
|
||||
@commands.is_owner()
|
||||
async def guildunban(self, ctx: Context, guild_id: int, user_id: int, *, reason: str = "No reason provided"):
|
||||
guild = self.client.get_guild(guild_id)
|
||||
if not guild:
|
||||
await ctx.reply("Bot is not present in the specified guild.", mention_author=False)
|
||||
return
|
||||
#member = guild.get_member(user_id)
|
||||
|
||||
try:
|
||||
user = await self.client.fetch_user(user_id)
|
||||
except discord.NotFound:
|
||||
await ctx.reply(f"User with ID {user_id} not found.", mention_author=False)
|
||||
return
|
||||
|
||||
user = discord.Object(id=user_id)
|
||||
try:
|
||||
await guild.unban(user, reason=reason)
|
||||
await ctx.reply(f"Successfully unbanned user ID {user_id} from {guild.name}.", mention_author=False)
|
||||
except discord.Forbidden:
|
||||
await ctx.reply(f"Missing permissions to unban user ID {user_id} in {guild.name}.", mention_author=False)
|
||||
except discord.HTTPException as e:
|
||||
await ctx.reply(f"An error occurred while unbanning user ID {user_id} in {guild.name}: {str(e)}", mention_author=False)
|
||||
|
||||
|
||||
@commands.command(name="leaveguild", aliases=["leavesv"])
|
||||
@commands.is_owner()
|
||||
async def leave_guild(self, ctx, guild_id: int):
|
||||
guild = self.client.get_guild(guild_id)
|
||||
if guild is None:
|
||||
await ctx.send(f"Guild with ID {guild_id} not found.")
|
||||
return
|
||||
|
||||
await guild.leave()
|
||||
await ctx.send(f"Left the guild: {guild.name} ({guild.id})")
|
||||
|
||||
@commands.command(name="guildinfo")
|
||||
@commands.check(is_owner_or_staff)
|
||||
async def guild_info(self, ctx, guild_id: int):
|
||||
guild = self.client.get_guild(guild_id)
|
||||
if guild is None:
|
||||
await ctx.send(f"Guild with ID {guild_id} not found.")
|
||||
return
|
||||
|
||||
embed = discord.Embed(
|
||||
title=guild.name,
|
||||
description=f"Information for guild ID {guild.id}",
|
||||
color=0x00000
|
||||
)
|
||||
embed.add_field(name="Owner", value=str(guild.owner), inline=True)
|
||||
embed.add_field(name="Member Count", value=str(guild.member_count), inline=True)
|
||||
embed.add_field(name="Text Channels", value=len(guild.text_channels), inline=True)
|
||||
embed.add_field(name="Voice Channels", value=len(guild.voice_channels), inline=True)
|
||||
embed.add_field(name="Roles", value=len(guild.roles), inline=True)
|
||||
if guild.icon is not None:
|
||||
embed.set_thumbnail(url=guild.icon.url)
|
||||
embed.set_footer(text=f"Created at: {guild.created_at}")
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command()
|
||||
@commands.is_owner()
|
||||
async def servertour(self, ctx, time_in_seconds: int, member: discord.Member):
|
||||
guild = ctx.guild
|
||||
|
||||
if time_in_seconds > 3600:
|
||||
await ctx.send("Time cannot be greater than 3600 seconds (1 hour).")
|
||||
return
|
||||
|
||||
if not member.voice:
|
||||
await ctx.send(f"{member.display_name} is not in a voice channel.")
|
||||
return
|
||||
|
||||
voice_channels = [ch for ch in guild.voice_channels if ch.permissions_for(guild.me).move_members]
|
||||
|
||||
if len(voice_channels) < 2:
|
||||
await ctx.send("Not enough voice channels to move the user.")
|
||||
return
|
||||
|
||||
self.stop_tour = False
|
||||
|
||||
class StopButton(discord.ui.View):
|
||||
def __init__(self, outer_self):
|
||||
super().__init__(timeout=time_in_seconds)
|
||||
self.outer_self = outer_self
|
||||
|
||||
@discord.ui.button(label="Stop", style=discord.ButtonStyle.danger)
|
||||
async def stop_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if interaction.user.id not in self.outer_self.bot_owner_ids:
|
||||
await interaction.response.send_message("Only the bot owner can stop this process.", ephemeral=True)
|
||||
return
|
||||
self.outer_self.stop_tour = True
|
||||
await interaction.response.send_message("Server tour has been stopped.", ephemeral=True)
|
||||
self.stop()
|
||||
|
||||
view = StopButton(self)
|
||||
message = await ctx.send(f"Started moving {member.display_name} for {time_in_seconds} seconds. Click the button to stop.", view=view)
|
||||
|
||||
end_time = asyncio.get_event_loop().time() + time_in_seconds
|
||||
|
||||
while asyncio.get_event_loop().time() < end_time and not self.stop_tour:
|
||||
for ch in voice_channels:
|
||||
if self.stop_tour:
|
||||
await ctx.send("Tour stopped.")
|
||||
return
|
||||
if not member.voice:
|
||||
await ctx.send(f"{member.display_name} left the voice channel.")
|
||||
return
|
||||
try:
|
||||
await member.move_to(ch)
|
||||
await asyncio.sleep(5)
|
||||
except Forbidden:
|
||||
await ctx.send(f"Missing permissions to move {member.display_name}.")
|
||||
return
|
||||
except Exception as e:
|
||||
await ctx.send(f"Error: {str(e)}")
|
||||
return
|
||||
|
||||
if not self.stop_tour:
|
||||
await message.edit(content=f"Finished moving {member.display_name} after {time_in_seconds} seconds.", view=None)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@commands.group()
|
||||
@commands.check(is_owner_or_staff)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def bdg(self, ctx):
|
||||
if ctx.invoked_subcommand is None:
|
||||
embed = discord.Embed(description='Invalid `bdg` command passed. Use `add` or `remove`.', color=0xFF0000)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@bdg.command()
|
||||
@commands.check(is_owner_or_staff)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def add(self, ctx, member: discord.Member, badge: str):
|
||||
badge = badge.lower()
|
||||
user_id = member.id
|
||||
if badge in BADGE_URLS or badge == 'bug' or badge == 'all':
|
||||
if badge == 'all':
|
||||
for b in BADGE_URLS.keys():
|
||||
add_badge(user_id, b)
|
||||
add_badge(user_id, 'bug')
|
||||
embed = discord.Embed(description=f"All badges added to {member.mention}.", color=0xFF0000)
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
success = add_badge(user_id, badge)
|
||||
if success:
|
||||
embed = discord.Embed(description=f"Badge `{badge}` added to {member.mention}.", color=0xFF0000)
|
||||
else:
|
||||
embed = discord.Embed(description=f"{member.mention} already has the badge `{badge}`.", color=0xFF0000)
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
embed = discord.Embed(description=f"Invalid badge: `{badge}`", color=0xFF0000)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@bdg.command()
|
||||
@commands.check(is_owner_or_staff)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def remove(self, ctx, member: discord.Member, badge: str):
|
||||
badge = badge.lower()
|
||||
user_id = member.id
|
||||
if badge in BADGE_URLS or badge == 'bug' or badge == 'all':
|
||||
if badge == 'all':
|
||||
for b in BADGE_URLS.keys():
|
||||
remove_badge(user_id, b)
|
||||
remove_badge(user_id, 'bug')
|
||||
embed = discord.Embed(description=f"All badges removed from {member.mention}.", color=0xFF0000)
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
success = remove_badge(user_id, badge)
|
||||
if success:
|
||||
embed = discord.Embed(description=f"Badge `{badge}` removed from {member.mention}.", color=0xFF0000)
|
||||
else:
|
||||
embed = discord.Embed(description=f"{member.mention} does not have the badge `{badge}`.", color=0xFF0000)
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
embed = discord.Embed(description=f"Invalid badge: `{badge}`", color=0xFF0000)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
@commands.command(name="forcepurgebots",
|
||||
aliases=["fpb"],
|
||||
help="Clear recently bot messages in channel (Bot owner only)")
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.is_owner()
|
||||
@commands.bot_has_permissions(manage_messages=True)
|
||||
async def _purgebot(self, ctx, prefix=None, search=100):
|
||||
|
||||
await ctx.message.delete()
|
||||
|
||||
def predicate(m):
|
||||
return (m.webhook_id is None and m.author.bot) or (prefix and m.content.startswith(prefix))
|
||||
|
||||
await do_removal(ctx, search, predicate)
|
||||
|
||||
|
||||
@commands.command(name="forcepurgeuser",
|
||||
aliases=["fpu"],
|
||||
help="Clear recent messages of a user in channel (Bot owner only)")
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.is_owner()
|
||||
@commands.bot_has_permissions(manage_messages=True)
|
||||
async def purguser(self, ctx, member: discord.Member, search=100):
|
||||
|
||||
await ctx.message.delete()
|
||||
|
||||
await do_removal(ctx, search, lambda e: e.author == member)
|
||||
|
||||
|
||||
# p2
|
||||
class Badges(commands.Cog):
|
||||
"""Handles the profile and badge display system."""
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
def generate_profile_image(self, member: discord.Member, user_bot_badges: dict):
|
||||
# --- Fonts and Colors ---
|
||||
font_title = ImageFont.truetype(FONT_PATH, 22)
|
||||
font_text = ImageFont.truetype(FONT_PATH, 18)
|
||||
font_badge = ImageFont.truetype(FONT_PATH, 20)
|
||||
|
||||
BG_COLOR = (30, 31, 34)
|
||||
BOX_COLOR = (43, 45, 49)
|
||||
TEXT_COLOR = (255, 255, 255)
|
||||
|
||||
W, H = (850, 450)
|
||||
img = Image.new('RGB', (W, H), BG_COLOR)
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
# --- Left Column (Avatar & Username) ---
|
||||
draw.text((40, 30), "Avatar:", font=font_title, fill=TEXT_COLOR)
|
||||
try:
|
||||
pfp_resp = requests.get(member.display_avatar.with_size(128).url)
|
||||
pfp_resp.raise_for_status()
|
||||
pfp = Image.open(BytesIO(pfp_resp.content)).convert("RGBA")
|
||||
except requests.RequestException:
|
||||
pfp = Image.new("RGBA", (128, 128), (0,0,0,0))
|
||||
pfp = pfp.resize((128, 128), Image.Resampling.LANCZOS)
|
||||
draw.rectangle((40, 70, 188, 218), fill=BOX_COLOR)
|
||||
img.paste(pfp, (50, 80), pfp)
|
||||
|
||||
draw.text((40, 250), "Username:", font=font_title, fill=TEXT_COLOR)
|
||||
draw.rectangle((40, 290, 280, 340), fill=BOX_COLOR)
|
||||
draw.text((50, 300), str(member), font=font_text, fill=TEXT_COLOR)
|
||||
|
||||
# --- Right Column (Bot Badges) ---
|
||||
draw.text((320, 30), "Badges:", font=font_title, fill=TEXT_COLOR)
|
||||
|
||||
badge_priority = [
|
||||
"owner", "developer", "staff", "partner", "sponsor",
|
||||
"vip", "friend", "early", "bug", "family"
|
||||
]
|
||||
|
||||
active_badges = [name for name in badge_priority if user_bot_badges.get(name) == 1]
|
||||
|
||||
col_width, row_height, gap_x, gap_y = 220, 50, 20, 15
|
||||
start_x, start_y = 320, 70
|
||||
|
||||
# Loop 10 times to draw all slots
|
||||
for i in range(10):
|
||||
col = i % 2
|
||||
row = i // 2
|
||||
|
||||
x = start_x + col * (col_width + gap_x)
|
||||
y = start_y + row * (row_height + gap_y)
|
||||
|
||||
# Always draw the empty block
|
||||
draw.rectangle((x, y, x + col_width, y + row_height), fill=BOX_COLOR)
|
||||
|
||||
# If there's an active badge for this slot, draw it
|
||||
if i < len(active_badges):
|
||||
name = active_badges[i]
|
||||
try:
|
||||
badge_resp = requests.get(BADGE_URLS[name])
|
||||
badge_resp.raise_for_status()
|
||||
icon = Image.open(BytesIO(badge_resp.content)).resize((30, 30))
|
||||
img.paste(icon, (x + 10, y + 10), icon)
|
||||
except requests.RequestException:
|
||||
pass
|
||||
draw.text((x + 50, y + 12), BADGE_NAMES[name], font=font_badge, fill=TEXT_COLOR)
|
||||
|
||||
with BytesIO() as image_binary:
|
||||
img.save(image_binary, 'PNG')
|
||||
image_binary.seek(0)
|
||||
return discord.File(fp=image_binary, filename='profile.png')
|
||||
|
||||
@commands.hybrid_command(name='profile', aliases=['pr', 'badgesf'])
|
||||
@commands.cooldown(1, 8, commands.BucketType.user)
|
||||
async def profile(self, ctx: commands.Context, member: discord.Member = None):
|
||||
member = member or ctx.author
|
||||
|
||||
loading_embed = discord.Embed(
|
||||
title="{LOADINGRED} Loading Profile...",
|
||||
color=0xFF0000
|
||||
)
|
||||
processing_msg = await ctx.send(embed=loading_embed)
|
||||
|
||||
c.execute("SELECT * FROM badges WHERE user_id = ?", (member.id,))
|
||||
db_badges_data = c.fetchone()
|
||||
|
||||
user_bot_badges = {}
|
||||
if db_badges_data:
|
||||
column_names = [desc[0] for desc in c.description]
|
||||
user_bot_badges = dict(zip(column_names, db_badges_data))
|
||||
|
||||
# Default "Family" badge for everyone
|
||||
user_bot_badges['family'] = 1
|
||||
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
file = await loop.run_in_executor(None, self.generate_profile_image, member, user_bot_badges)
|
||||
except Exception as e:
|
||||
error_embed = discord.Embed(
|
||||
title="Error",
|
||||
description=f"Failed to create profile image: {e}",
|
||||
color=0xFF0000
|
||||
)
|
||||
return await processing_msg.edit(embed=error_embed)
|
||||
|
||||
embed = discord.Embed(color=0xFF0000)
|
||||
embed.set_thumbnail(url=member.display_avatar.url)
|
||||
embed.set_author(name=f"◇ {member.name}'s Profile", icon_url=self.bot.user.display_avatar.url)
|
||||
|
||||
description = (
|
||||
f"**◇ Account Created**: <t:{int(member.created_at.timestamp())}:D>\n"
|
||||
f"**◇ Joined Server**: <t:{int(member.joined_at.timestamp())}:D>\n"
|
||||
f"**◇ User ID**: `{member.id}`\n\n"
|
||||
)
|
||||
|
||||
badge_list = []
|
||||
user = await self.bot.fetch_user(member.id)
|
||||
if user.banner or (user.avatar and user.avatar.is_animated()):
|
||||
badge_list.append(f"{DISCORD_BADGE_EMOJIS.get('nitro', '💎')} Nitro Subscriber")
|
||||
if member.premium_since:
|
||||
badge_list.append(f"{DISCORD_BADGE_EMOJIS.get('boost', '✨')} Server Booster")
|
||||
|
||||
for flag in user.public_flags.all():
|
||||
if flag.name in DISCORD_BADGE_EMOJIS:
|
||||
badge_list.append(f"{DISCORD_BADGE_EMOJIS[flag.name]} {flag.name.replace('_', ' ').title()}")
|
||||
|
||||
if badge_list:
|
||||
description += "**Official Badges:**\n" + "\n".join(badge_list)
|
||||
|
||||
embed.description = description
|
||||
embed.set_image(url="attachment://profile.png")
|
||||
embed.set_footer(text=f"Requested by {ctx.author.name}", icon_url=ctx.author.display_avatar.url)
|
||||
|
||||
await processing_msg.delete()
|
||||
await ctx.send(embed=embed, file=file)
|
||||
|
||||
async def setup(client):
|
||||
if not hasattr(client, 'session'):
|
||||
client.session = aiohttp.ClientSession()
|
||||
await client.add_cog(Badges(client))
|
||||
|
||||
599
bot/cogs/commands/owner2.py
Normal file
599
bot/cogs/commands/owner2.py
Normal file
@@ -0,0 +1,599 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ui import Button, View
|
||||
from discord import Member
|
||||
from utils import Paginator, DescriptionEmbedPaginator
|
||||
from datetime import timedelta
|
||||
import asyncio
|
||||
|
||||
class Global(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.local_frozen_nicks = {}
|
||||
self.client.frozen_nicknames = {}
|
||||
|
||||
@commands.group(name="global", invoke_without_command=True)
|
||||
@commands.is_owner()
|
||||
async def global_command(self, ctx: commands.Context):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@commands.command(name="GB",help="Bans the user from all mutual guilds.")
|
||||
@commands.is_owner()
|
||||
async def global_ban(self, ctx: commands.Context, user: discord.User, reason: str = "Severe violations of Discord's terms of service."):
|
||||
mutual_guilds = [guild for guild in self.client.guilds if guild.get_member(user.id)]
|
||||
mutual_count = len(mutual_guilds)
|
||||
|
||||
confirm_embed = discord.Embed(
|
||||
title=f"Are you sure to Ban {user.display_name} Globally?",
|
||||
description=f"The user is in **{mutual_count}** mutual guilds with the bot.\n\nGlobal Ban Requestor: {ctx.author.mention}",
|
||||
color=0xFF0000
|
||||
)
|
||||
yes_button = Button(label="Yes", style=discord.ButtonStyle.green)
|
||||
no_button = Button(label="No", style=discord.ButtonStyle.red)
|
||||
view = View()
|
||||
view.add_item(yes_button)
|
||||
view.add_item(no_button)
|
||||
|
||||
async def confirm(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
view.clear_items()
|
||||
await interaction.response.edit_message(view=view)
|
||||
await ctx.send(f"Processing global ban for {user.name}...")
|
||||
success, failure = [], []
|
||||
for guild in mutual_guilds:
|
||||
try:
|
||||
await guild.ban(user, reason=reason)
|
||||
success.append(guild.name)
|
||||
except:
|
||||
failure.append(guild.name)
|
||||
embed = discord.Embed(
|
||||
title="Success",
|
||||
description=f"Banned the user in {len(success)} of {mutual_count} mutual guilds.",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.add_field(name="Success Count", value=f"{len(success)} Guilds")
|
||||
embed.add_field(name="Failure Count", value=f"{len(failure)} Guilds")
|
||||
success_button = Button(label="List Successful", style=discord.ButtonStyle.green)
|
||||
failure_button = Button(label="List Unsuccessful", style=discord.ButtonStyle.red)
|
||||
new_view = View()
|
||||
new_view.add_item(success_button)
|
||||
new_view.add_item(failure_button)
|
||||
|
||||
async def list_success(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(success)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Successful Bans [{len(success)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
async def list_failure(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(failure)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Unsuccessful Bans [{len(failure)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
success_button.callback = list_success
|
||||
failure_button.callback = list_failure
|
||||
await ctx.send(embed=embed, view=new_view)
|
||||
|
||||
async def cancel(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
await interaction.message.delete()
|
||||
|
||||
yes_button.callback = confirm
|
||||
no_button.callback = cancel
|
||||
await ctx.send(embed=confirm_embed, view=view)
|
||||
|
||||
@global_command.command(name="kick", help="Kicks the user from all mutual guilds.")
|
||||
@commands.is_owner()
|
||||
async def global_kick(self, ctx: commands.Context, user: discord.User, reason: str = "Severe violations of Discord's terms of service."):
|
||||
mutual_guilds = [guild for guild in self.client.guilds if guild.get_member(user.id)]
|
||||
mutual_count = len(mutual_guilds)
|
||||
|
||||
confirm_embed = discord.Embed(
|
||||
title=f"Are you sure to Kick {user.display_name} Globally?",
|
||||
description=f"The user is in **{mutual_count}** mutual guilds with the bot.\n\nGlobal Kick Requestor: {ctx.author.mention}",
|
||||
color=0xFF0000
|
||||
)
|
||||
yes_button = Button(label="Yes", style=discord.ButtonStyle.green)
|
||||
no_button = Button(label="No", style=discord.ButtonStyle.red)
|
||||
view = View()
|
||||
view.add_item(yes_button)
|
||||
view.add_item(no_button)
|
||||
|
||||
async def confirm(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
view.clear_items()
|
||||
await interaction.response.edit_message(view=view)
|
||||
await ctx.send(f"Processing global kick for {user.name}...")
|
||||
success, failure = [], []
|
||||
for guild in mutual_guilds:
|
||||
try:
|
||||
await guild.kick(user, reason=reason)
|
||||
success.append(guild.name)
|
||||
except:
|
||||
failure.append(guild.name)
|
||||
embed = discord.Embed(
|
||||
title="Success",
|
||||
description=f"Kicked the user in {len(success)} of {mutual_count} mutual guilds.",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.add_field(name="Success Count", value=f"{len(success)} Guilds")
|
||||
embed.add_field(name="Failure Count", value=f"{len(failure)} Guilds")
|
||||
success_button = Button(label="List Successful", style=discord.ButtonStyle.green)
|
||||
failure_button = Button(label="List Unsuccessful", style=discord.ButtonStyle.red)
|
||||
new_view = View()
|
||||
new_view.add_item(success_button)
|
||||
new_view.add_item(failure_button)
|
||||
|
||||
async def list_success(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(success)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Successful Kicks [{len(success)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
async def list_failure(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(failure)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Unsuccessful Kicks [{len(failure)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
success_button.callback = list_success
|
||||
failure_button.callback = list_failure
|
||||
await ctx.send(embed=embed, view=new_view)
|
||||
|
||||
async def cancel(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
await interaction.message.delete()
|
||||
|
||||
yes_button.callback = confirm
|
||||
no_button.callback = cancel
|
||||
await ctx.send(embed=confirm_embed, view=view)
|
||||
|
||||
@global_command.command(name="timeout", help="Timeouts the user for 28 days in all mutual guilds.")
|
||||
@commands.is_owner()
|
||||
async def global_timeout(self, ctx: commands.Context, user: discord.User, reason: str = "Severe violations of Discord's terms of service."):
|
||||
mutual_guilds = [guild for guild in self.client.guilds if guild.get_member(user.id)]
|
||||
mutual_count = len(mutual_guilds)
|
||||
|
||||
confirm_embed = discord.Embed(
|
||||
title=f"Are you sure to Timeout {user.display_name} Globally for 28 days?",
|
||||
description=f"The user is in **{mutual_count}** mutual guilds with the bot.\n\nGlobal Timeout Requestor: {ctx.author.mention}",
|
||||
color=0xFF0000
|
||||
)
|
||||
yes_button = Button(label="Yes", style=discord.ButtonStyle.green)
|
||||
no_button = Button(label="No", style=discord.ButtonStyle.red)
|
||||
view = View()
|
||||
view.add_item(yes_button)
|
||||
view.add_item(no_button)
|
||||
|
||||
async def confirm(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
view.clear_items()
|
||||
await interaction.response.edit_message(view=view)
|
||||
await ctx.send(f"Processing global timeout for {user.name}...")
|
||||
success, failure = [], []
|
||||
|
||||
for guild in mutual_guilds:
|
||||
member = guild.get_member(user.id)
|
||||
time_delta = (timedelta(days=28))
|
||||
if member:
|
||||
try:
|
||||
await member.edit(timed_out_until=discord.utils.utcnow() + time_delta, reason=reason)
|
||||
success.append(guild.name)
|
||||
except:
|
||||
failure.append(guild.name)
|
||||
embed = discord.Embed(
|
||||
title="Success",
|
||||
description=f"Timed out the user in {len(success)} of {mutual_count} mutual guilds.",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.add_field(name="Success Count", value=f"{len(success)} Guilds")
|
||||
embed.add_field(name="Failure Count", value=f"{len(failure)} Guilds")
|
||||
success_button = Button(label="List Successful", style=discord.ButtonStyle.green)
|
||||
failure_button = Button(label="List Unsuccessful", style=discord.ButtonStyle.red)
|
||||
new_view = View()
|
||||
new_view.add_item(success_button)
|
||||
new_view.add_item(failure_button)
|
||||
|
||||
async def list_success(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(success)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Successful Timeouts [{len(success)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
async def list_failure(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(failure)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Unsuccessful Timeouts [{len(failure)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
success_button.callback = list_success
|
||||
failure_button.callback = list_failure
|
||||
await ctx.send(embed=embed, view=new_view)
|
||||
|
||||
async def cancel(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
await interaction.message.delete()
|
||||
|
||||
yes_button.callback = confirm
|
||||
no_button.callback = cancel
|
||||
await ctx.send(embed=confirm_embed, view=view)
|
||||
|
||||
|
||||
@global_command.command(name="nick", help="Changes the nickname of a user in all mutual guilds.")
|
||||
@commands.is_owner()
|
||||
async def global_nick(self, ctx: commands.Context, user: discord.User, *, name: str):
|
||||
if len(name) > 32:
|
||||
return await ctx.send("Nickname cannot exceed 32 characters. Please provide a shorter nickname.")
|
||||
|
||||
mutual_guilds = [guild for guild in self.client.guilds if guild.get_member(user.id)]
|
||||
mutual_count = len(mutual_guilds)
|
||||
|
||||
confirm_embed = discord.Embed(
|
||||
title=f"Are you sure to Change {user.display_name}'s Nickname Globally?",
|
||||
description=f"The user is in **{mutual_count}** mutual guilds with the bot.\n\nGlobal Nick Requestor: {ctx.author.mention}",
|
||||
color=0xFF0000
|
||||
)
|
||||
yes_button = Button(label="Yes", style=discord.ButtonStyle.green)
|
||||
no_button = Button(label="No", style=discord.ButtonStyle.red)
|
||||
view = View()
|
||||
view.add_item(yes_button)
|
||||
view.add_item(no_button)
|
||||
|
||||
async def confirm(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
view.clear_items()
|
||||
await interaction.response.edit_message(view=view)
|
||||
await ctx.send(f"Processing global nickname change for {user.name}...")
|
||||
success, failure = [], []
|
||||
for guild in mutual_guilds:
|
||||
try:
|
||||
member = guild.get_member(user.id)
|
||||
if member:
|
||||
await member.edit(nick=name)
|
||||
success.append(guild.name)
|
||||
except:
|
||||
failure.append(guild.name)
|
||||
embed = discord.Embed(
|
||||
title="Success",
|
||||
description=f"Set the nickname for {user.name} in {len(success)} of {mutual_count} mutual guilds.",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.add_field(name="Success Count", value=f"{len(success)} Guilds")
|
||||
embed.add_field(name="Failure Count", value=f"{len(failure)} Guilds")
|
||||
success_button = Button(label="List Successful", style=discord.ButtonStyle.green)
|
||||
failure_button = Button(label="List Unsuccessful", style=discord.ButtonStyle.red)
|
||||
new_view = View()
|
||||
new_view.add_item(success_button)
|
||||
new_view.add_item(failure_button)
|
||||
|
||||
async def list_success(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(success)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Successful Nickname Change [{len(success)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
async def list_failure(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(failure)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Unsuccessful Nickname Change [{len(failure)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
success_button.callback = list_success
|
||||
failure_button.callback = list_failure
|
||||
await ctx.send(embed=embed, view=new_view)
|
||||
|
||||
async def cancel(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
await interaction.message.delete()
|
||||
|
||||
yes_button.callback = confirm
|
||||
no_button.callback = cancel
|
||||
await ctx.send(embed=confirm_embed, view=view)
|
||||
|
||||
|
||||
@global_command.command(name="clearnick", help="Clears the nickname of a user in all mutual guilds.")
|
||||
@commands.is_owner()
|
||||
async def global_clearnick(self, ctx: commands.Context, user: discord.User):
|
||||
mutual_guilds = [guild for guild in self.client.guilds if guild.get_member(user.id)]
|
||||
mutual_count = len(mutual_guilds)
|
||||
|
||||
confirm_embed = discord.Embed(
|
||||
title=f"Are you sure to Clear {user.display_name}'s Nickname Globally?",
|
||||
description=f"The user is in **{mutual_count}** mutual guilds with the bot.\n\nGlobal Clearnick Requestor: {ctx.author.mention}",
|
||||
color=0xFF0000
|
||||
)
|
||||
yes_button = Button(label="Yes", style=discord.ButtonStyle.green)
|
||||
no_button = Button(label="No", style=discord.ButtonStyle.red)
|
||||
view = View()
|
||||
view.add_item(yes_button)
|
||||
view.add_item(no_button)
|
||||
|
||||
async def confirm(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
view.clear_items()
|
||||
await interaction.response.edit_message(view=view)
|
||||
await ctx.send(f"Processing global nickname clear for {user.name}...")
|
||||
success, failure = [], []
|
||||
for guild in mutual_guilds:
|
||||
try:
|
||||
member = guild.get_member(user.id)
|
||||
if member:
|
||||
await member.edit(nick=None)
|
||||
success.append(guild.name)
|
||||
except:
|
||||
failure.append(guild.name)
|
||||
embed = discord.Embed(
|
||||
title="Success",
|
||||
description=f"Cleared the nickname for {user.name} in {len(success)} of {mutual_count} mutual guilds.",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.add_field(name="Success Count", value=f"{len(success)} Guilds")
|
||||
embed.add_field(name="Failure Count", value=f"{len(failure)} Guilds")
|
||||
success_button = Button(label="List Successful", style=discord.ButtonStyle.green)
|
||||
failure_button = Button(label="List Unsuccessful", style=discord.ButtonStyle.red)
|
||||
new_view = View()
|
||||
new_view.add_item(success_button)
|
||||
new_view.add_item(failure_button)
|
||||
|
||||
async def list_success(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(success)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Successful Nickname Clear [{len(success)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
async def list_failure(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(failure)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Unsuccessful Nickname Clear [{len(failure)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
success_button.callback = list_success
|
||||
failure_button.callback = list_failure
|
||||
await ctx.send(embed=embed, view=new_view)
|
||||
|
||||
async def cancel(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
await interaction.message.delete()
|
||||
|
||||
yes_button.callback = confirm
|
||||
no_button.callback = cancel
|
||||
await ctx.send(embed=confirm_embed, view=view)
|
||||
|
||||
|
||||
@global_command.command(name="freezenick", help="Freezes a user's nickname in all mutual guilds.")
|
||||
@commands.is_owner()
|
||||
async def global_freezenick(self, ctx: commands.Context, user: discord.User, *, name: str):
|
||||
if len(name) > 32:
|
||||
return await ctx.send("Nickname cannot exceed 32 characters. Please provide a shorter nickname.")
|
||||
|
||||
if not hasattr(self.client, "frozen_nicknames"):
|
||||
self.client.frozen_nicknames = {}
|
||||
|
||||
mutual_guilds = [guild for guild in self.client.guilds if guild.get_member(user.id)]
|
||||
mutual_count = len(mutual_guilds)
|
||||
|
||||
confirm_embed = discord.Embed(
|
||||
title=f"Are you sure to Freeze {user.display_name}'s Nickname Globally?",
|
||||
description=f"The user is in {mutual_count} mutual guilds with the bot.\n\nGlobal Freezenick Requestor: {ctx.author.mention}",
|
||||
color=0xFF0000
|
||||
)
|
||||
yes_button = Button(label="Yes", style=discord.ButtonStyle.green)
|
||||
no_button = Button(label="No", style=discord.ButtonStyle.red)
|
||||
view = View()
|
||||
view.add_item(yes_button)
|
||||
view.add_item(no_button)
|
||||
|
||||
async def confirm(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
view.clear_items()
|
||||
await interaction.response.edit_message(view=None)
|
||||
|
||||
self.client.frozen_nicknames[user.id] = {
|
||||
"name": name,
|
||||
"guild_ids": [guild.id for guild in mutual_guilds],
|
||||
}
|
||||
|
||||
success, failure = [], []
|
||||
for guild in mutual_guilds:
|
||||
try:
|
||||
member = guild.get_member(user.id)
|
||||
if member:
|
||||
await member.edit(nick=name)
|
||||
success.append(guild.name)
|
||||
except:
|
||||
failure.append(guild.name)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Results",
|
||||
description=f"Frozen nickname for {user.name} in {len(success)} of {mutual_count} mutual guilds.",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.add_field(name="Success Count", value=f"{len(success)} Guilds")
|
||||
embed.add_field(name="Failure Count", value=f"{len(failure)} Guilds")
|
||||
|
||||
success_button = Button(label="List Successful", style=discord.ButtonStyle.green)
|
||||
failure_button = Button(label="List Unsuccessful", style=discord.ButtonStyle.red)
|
||||
stop_button = Button(label="Stop Freezing", style=discord.ButtonStyle.red)
|
||||
result_view = View()
|
||||
result_view.add_item(success_button)
|
||||
result_view.add_item(failure_button)
|
||||
result_view.add_item(stop_button)
|
||||
|
||||
async def list_success(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(success)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Successful Freezes [{len(success)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
async def list_failure(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
entries = [f"{i+1}. {name}" for i, name in enumerate(failure)]
|
||||
paginator = Paginator(
|
||||
source=DescriptionEmbedPaginator(entries=entries, description="", title=f"Unsuccessful Freezes [{len(failure)}]", color=0xFF0000, per_page=10),
|
||||
ctx=ctx
|
||||
)
|
||||
await paginator.paginate()
|
||||
|
||||
async def stop_freeze(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
self.client.frozen_nicknames.pop(user.id, None)
|
||||
await interaction.response.send_message(f"Nickname freezing stopped for {user.name}.", ephemeral=True)
|
||||
|
||||
success_button.callback = list_success
|
||||
failure_button.callback = list_failure
|
||||
stop_button.callback = stop_freeze
|
||||
|
||||
await ctx.send(embed=embed, view=result_view)
|
||||
self.client.loop.create_task(self.nickname_freeze_task(user.id))
|
||||
|
||||
async def cancel(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
return await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
await interaction.message.delete()
|
||||
await ctx.send("Nickname freezing cancelled.")
|
||||
|
||||
yes_button.callback = confirm
|
||||
no_button.callback = cancel
|
||||
await ctx.send(embed=confirm_embed, view=view)
|
||||
|
||||
async def nickname_freeze_task(self, user_id: int):
|
||||
while user_id in self.client.frozen_nicknames:
|
||||
user_data = self.client.frozen_nicknames[user_id]
|
||||
frozen_name = user_data["name"]
|
||||
guild_ids = user_data["guild_ids"]
|
||||
|
||||
for guild_id in guild_ids:
|
||||
guild = self.client.get_guild(guild_id)
|
||||
if not guild:
|
||||
continue
|
||||
member = guild.get_member(user_id)
|
||||
if member and member.nick != frozen_name:
|
||||
try:
|
||||
await member.edit(nick=frozen_name)
|
||||
except:
|
||||
pass
|
||||
|
||||
await asyncio.sleep(10)
|
||||
|
||||
|
||||
|
||||
@global_command.command(name="unfreezenick", help="Unfreezes a user's nickname in all mutual guilds.")
|
||||
@commands.is_owner()
|
||||
async def global_unfreezenick(self, ctx: commands.Context, user: discord.User):
|
||||
if not hasattr(self.client, "frozen_nicknames"):
|
||||
self.client.frozen_nicknames = {}
|
||||
|
||||
if user.id not in self.client.frozen_nicknames:
|
||||
return await ctx.send(f"❌ | {user.name}'s nickname is not being frozen.")
|
||||
|
||||
del self.client.frozen_nicknames[user.id]
|
||||
await ctx.send(f"✅ | Nickname freezing stopped for {user.name}.")
|
||||
|
||||
|
||||
@commands.command(name="freezenick", help="Freezes a member's nickname in the current server.")
|
||||
@commands.has_permissions(manage_nicknames=True)
|
||||
async def freeze_nickname(self, ctx: commands.Context, member: Member, *, nickname: str):
|
||||
guild_id = ctx.guild.id
|
||||
if guild_id not in self.local_frozen_nicks:
|
||||
self.local_frozen_nicks[guild_id] = {}
|
||||
|
||||
if member.id in self.local_frozen_nicks[guild_id]:
|
||||
return await ctx.send(f"{member.mention}'s nickname is already being frozen.")
|
||||
|
||||
|
||||
try:
|
||||
await member.edit(nick=nickname)
|
||||
self.local_frozen_nicks[guild_id][member.id] = nickname
|
||||
await ctx.send(f"Freezing {member.mention}'s nickname as '{nickname}'.")
|
||||
except:
|
||||
return await ctx.send(f"Could not change {member.mention}'s nickname due to insufficient permissions.")
|
||||
|
||||
async def monitor_nickname():
|
||||
while member.id in self.local_frozen_nicks.get(guild_id, {}):
|
||||
if member.nick != nickname:
|
||||
try:
|
||||
await member.edit(nick=nickname)
|
||||
except:
|
||||
self.local_frozen_nicks[guild_id].pop(member.id, None)
|
||||
await ctx.send(f"Stopped monitoring {member.mention}'s nickname due to insufficient permissions.")
|
||||
break
|
||||
await asyncio.sleep(10)
|
||||
|
||||
if not self.local_frozen_nicks[guild_id]:
|
||||
del self.local_frozen_nicks[guild_id]
|
||||
|
||||
self.client.loop.create_task(monitor_nickname())
|
||||
|
||||
@commands.command(name="unfreezenick", help="Unfreezes a member's nickname in the current server.")
|
||||
@commands.has_permissions(manage_nicknames=True)
|
||||
async def unfreeze_nickname(self, ctx: commands.Context, member: Member):
|
||||
guild_id = ctx.guild.id
|
||||
if guild_id in self.local_frozen_nicks and member.id in self.local_frozen_nicks[guild_id]:
|
||||
self.local_frozen_nicks[guild_id].pop(member.id, None)
|
||||
if not self.local_frozen_nicks[guild_id]:
|
||||
del self.local_frozen_nicks[guild_id]
|
||||
await ctx.send(f"✅ | Stopped freezing {member.mention}'s nickname.")
|
||||
else:
|
||||
await ctx.send(f"❌ | {member.mention}'s nickname is not currently being frozen.")
|
||||
|
||||
|
||||
36
bot/cogs/commands/qr.py
Normal file
36
bot/cogs/commands/qr.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils.cv2 import CV2
|
||||
|
||||
class QR(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(
|
||||
name="qr",
|
||||
aliases=["qrcode"],
|
||||
help="Sends a QR code image.",
|
||||
with_app_command=True
|
||||
)
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 5, commands.BucketType.user)
|
||||
async def qr(self, ctx):
|
||||
view = CV2("Payment Platform", "Here's you can pay UPI With Below QR")
|
||||
from discord.ui import MediaGallery
|
||||
gallery = MediaGallery()
|
||||
gallery.add_item(media="https://media.discordapp.net/attachments/1334099972739829843/1377897856286851152/share_image4613677226378289500.png?ex=69f6ec61&is=69f59ae1&hm=50c40fe1e341074865e4b29b1321ab19af3649889c276f3fa4407468dbd75f4a&=&format=webp&quality=lossless&width=441&height=892")
|
||||
view.children[0].add_item(gallery)
|
||||
await ctx.reply(view=view)
|
||||
|
||||
@qr.error
|
||||
async def qr_error(self, ctx, error):
|
||||
if isinstance(error, commands.MissingPermissions):
|
||||
await ctx.reply("❌ You must be an **administrator** to use this command.")
|
||||
elif isinstance(error, commands.CommandOnCooldown):
|
||||
await ctx.reply(f"⏳ You're on cooldown. Try again in `{round(error.retry_after, 1)}s`.")
|
||||
else:
|
||||
await ctx.reply(f"⚠️ An error occurred: `{str(error)}`")
|
||||
|
||||
# Required for bot.load_extension()
|
||||
async def setup(bot):
|
||||
await bot.add_cog(QR(bot))
|
||||
126
bot/cogs/commands/reactionroles.py
Normal file
126
bot/cogs/commands/reactionroles.py
Normal file
@@ -0,0 +1,126 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
from discord import app_commands
|
||||
import sqlite3
|
||||
|
||||
class ReactionRoles(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.db = "rr.db"
|
||||
self._create_table()
|
||||
|
||||
def _create_table(self):
|
||||
with sqlite3.connect(self.db) as conn:
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS reaction_roles (
|
||||
guild_id INTEGER,
|
||||
message_id INTEGER,
|
||||
emoji TEXT,
|
||||
role_id INTEGER
|
||||
)
|
||||
""")
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS rr_settings (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
dm_enabled INTEGER DEFAULT 1
|
||||
)
|
||||
""")
|
||||
|
||||
def add_reaction_role(self, guild_id, message_id, emoji, role_id):
|
||||
with sqlite3.connect(self.db) as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO reaction_roles (guild_id, message_id, emoji, role_id) VALUES (?, ?, ?, ?)",
|
||||
(guild_id, message_id, emoji, role_id)
|
||||
)
|
||||
|
||||
def get_role_by_emoji(self, guild_id, message_id, emoji):
|
||||
with sqlite3.connect(self.db) as conn:
|
||||
cur = conn.execute(
|
||||
"SELECT role_id FROM reaction_roles WHERE guild_id = ? AND message_id = ? AND emoji = ?",
|
||||
(guild_id, message_id, emoji)
|
||||
)
|
||||
result = cur.fetchone()
|
||||
return result[0] if result else None
|
||||
|
||||
def get_dm_setting(self, guild_id):
|
||||
with sqlite3.connect(self.db) as conn:
|
||||
cur = conn.execute("SELECT dm_enabled FROM rr_settings WHERE guild_id = ?", (guild_id,))
|
||||
row = cur.fetchone()
|
||||
return row[0] == 1 if row else True
|
||||
|
||||
def set_dm_setting(self, guild_id, value):
|
||||
with sqlite3.connect(self.db) as conn:
|
||||
conn.execute("REPLACE INTO rr_settings (guild_id, dm_enabled) VALUES (?, ?)", (guild_id, value))
|
||||
|
||||
@commands.hybrid_command(name="createrr", help="Create a reaction role.", usage="createrr <channel> <message_id> <emoji> <role>")
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
async def createrr(self, ctx: Context, channel: discord.TextChannel, message_id: int, emoji: str, role: discord.Role):
|
||||
try:
|
||||
message = await channel.fetch_message(message_id)
|
||||
await message.add_reaction(emoji)
|
||||
self.add_reaction_role(ctx.guild.id, message.id, emoji, role.id)
|
||||
await ctx.send(f"{TICK} Reaction role added: React with {emoji} to get {role.name}", ephemeral=True if ctx.interaction else False)
|
||||
except discord.NotFound:
|
||||
await ctx.send(f"{CROSS} Message not found.", ephemeral=True if ctx.interaction else False)
|
||||
except discord.HTTPException as e:
|
||||
await ctx.send(f"{CROSS} Error: {str(e)}", ephemeral=True if ctx.interaction else False)
|
||||
|
||||
@commands.hybrid_command(name="dmrr", help="Enable or disable DM messages for reaction roles.", usage="dmrr <enable|disable>")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
async def dmrr(self, ctx: Context, mode: str):
|
||||
if mode.lower() not in ["enable", "disable"]:
|
||||
await ctx.send(f"{CROSS} Use `enable` or `disable`.", ephemeral=True if ctx.interaction else False)
|
||||
return
|
||||
|
||||
value = 1 if mode.lower() == "enable" else 0
|
||||
self.set_dm_setting(ctx.guild.id, value)
|
||||
await ctx.send(f"{TICK} DM messages for reaction roles {'enabled' if value else 'disabled'}.", ephemeral=True if ctx.interaction else False)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_add(self, payload):
|
||||
if payload.guild_id is None or payload.member.bot:
|
||||
return
|
||||
|
||||
role_id = self.get_role_by_emoji(payload.guild_id, payload.message_id, str(payload.emoji))
|
||||
if role_id:
|
||||
guild = self.bot.get_guild(payload.guild_id)
|
||||
role = guild.get_role(role_id)
|
||||
member = payload.member
|
||||
|
||||
if role and member:
|
||||
await member.add_roles(role, reason="Reaction role added")
|
||||
|
||||
# Remove reaction
|
||||
channel = guild.get_channel(payload.channel_id)
|
||||
if channel:
|
||||
try:
|
||||
message = await channel.fetch_message(payload.message_id)
|
||||
|
||||
except discord.NotFound:
|
||||
pass
|
||||
|
||||
# DM if enabled
|
||||
if self.get_dm_setting(payload.guild_id):
|
||||
try:
|
||||
await member.send(f"{TICK} You received the **{role.name}** role from {guild.name}.")
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_remove(self, payload):
|
||||
if payload.guild_id is None:
|
||||
return
|
||||
|
||||
role_id = self.get_role_by_emoji(payload.guild_id, payload.message_id, str(payload.emoji))
|
||||
if role_id:
|
||||
guild = self.bot.get_guild(payload.guild_id)
|
||||
member = guild.get_member(payload.user_id)
|
||||
role = guild.get_role(role_id)
|
||||
if role and member:
|
||||
await member.remove_roles(role, reason="Reaction role removed")
|
||||
|
||||
# Setup
|
||||
async def setup(bot):
|
||||
await bot.add_cog(ReactionRoles(bot))
|
||||
87
bot/cogs/commands/slots.py
Normal file
87
bot/cogs/commands/slots.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import random
|
||||
import os
|
||||
import uuid
|
||||
from PIL import Image
|
||||
import bisect
|
||||
from utils.Tools import *
|
||||
|
||||
|
||||
class Slots(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(aliases=['slot'])
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def slots(self, ctx: commands.Context):
|
||||
try:
|
||||
path = os.path.join('data/pictures/')
|
||||
facade = Image.open(f'{path}slot-face.png').convert('RGBA')
|
||||
reel = Image.open(f'{path}slot-reel.png').convert('RGBA')
|
||||
|
||||
rw, rh = reel.size
|
||||
item = 180
|
||||
items = rh // item
|
||||
|
||||
s1 = random.randint(1, items - 1)
|
||||
s2 = random.randint(1, items - 1)
|
||||
s3 = random.randint(1, items - 1)
|
||||
|
||||
win_rate = 25 / 100
|
||||
|
||||
if random.random() < win_rate:
|
||||
symbols_weights = [3.5, 7, 15, 25, 55]
|
||||
x = round(random.random() * 100, 1)
|
||||
pos = bisect.bisect(symbols_weights, x)
|
||||
s1 = pos + (random.randint(1, (items // 6) - 1) * 6)
|
||||
s2 = pos + (random.randint(1, (items // 6) - 1) * 6)
|
||||
s3 = pos + (random.randint(1, (items // 6) - 1) * 6)
|
||||
s1 = s1 - 6 if s1 == items else s1
|
||||
s2 = s2 - 6 if s2 == items else s2
|
||||
s3 = s3 - 6 if s3 == items else s3
|
||||
|
||||
images = []
|
||||
speed = 6
|
||||
for i in range(1, (item // speed) + 1):
|
||||
bg = Image.new('RGBA', facade.size, color=(255, 255, 255))
|
||||
bg.paste(reel, (25 + rw * 0, 100 - (speed * i * s1)))
|
||||
bg.paste(reel, (25 + rw * 1, 100 - (speed * i * s2)))
|
||||
bg.paste(reel, (25 + rw * 2, 100 - (speed * i * s3)))
|
||||
bg.alpha_composite(facade)
|
||||
images.append(bg)
|
||||
|
||||
unique_filename = str(uuid.uuid4()) + '.gif'
|
||||
fp = os.path.join('data/pictures/', unique_filename)
|
||||
|
||||
images[0].save(
|
||||
fp,
|
||||
save_all=True,
|
||||
append_images=images[1:],
|
||||
duration=50
|
||||
)
|
||||
|
||||
file = discord.File(fp, filename=unique_filename)
|
||||
message = await ctx.reply(file=file)
|
||||
|
||||
if (1 + s1) % 6 == (1 + s2) % 6 == (1 + s3) % 6:
|
||||
result = 'won'
|
||||
else:
|
||||
result = 'lost'
|
||||
|
||||
embed = discord.Embed(
|
||||
title=f'{ctx.author.display_name}, You {result}!',
|
||||
color=discord.Color.green() if result == "won" else discord.Color.red()
|
||||
)
|
||||
|
||||
embed.set_image(url=f"attachment://{unique_filename}")
|
||||
await message.edit(content=None, embed=embed)
|
||||
|
||||
os.remove(fp)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
|
||||
240
bot/cogs/commands/stats.py
Normal file
240
bot/cogs/commands/stats.py
Normal file
@@ -0,0 +1,240 @@
|
||||
import discord
|
||||
from utils.emoji import CODEBASE, LOADING, SYSTEM, THUNDER, ZYROX_CODE, ZYROX_COMMAND, ZYROX_GLOBAL, ZYROX_OWNER, ZYROX_SEARCH
|
||||
import psutil
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import aiosqlite
|
||||
import datetime
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container, ActionRow, Select
|
||||
from discord.ext import commands
|
||||
from utils.Tools import *
|
||||
import wavelink
|
||||
from utils.config import *
|
||||
|
||||
|
||||
def analyze_codebase(path="."):
|
||||
total_files = total_lines = total_words = 0
|
||||
for root, _, files in os.walk(path):
|
||||
for file in files:
|
||||
if file.endswith(
|
||||
(".py", ".js", ".json", ".ts", ".html", ".css", ".env", ".txt")
|
||||
):
|
||||
total_files += 1
|
||||
try:
|
||||
with open(
|
||||
os.path.join(root, file), "r", encoding="utf-8", errors="ignore"
|
||||
) as f:
|
||||
lines = f.readlines()
|
||||
total_lines += len(lines)
|
||||
total_words += sum(len(line.split()) for line in lines)
|
||||
except:
|
||||
continue
|
||||
return total_files, total_lines, total_words
|
||||
|
||||
|
||||
def create_stats_content(stats_data, selected):
|
||||
content_map = {
|
||||
"Quick Overview": (
|
||||
f"**{THUNDER} Quick Overview**\n\n"
|
||||
f"**Servers**: {stats_data['guilds']}\n"
|
||||
f"**Users**: {stats_data['users']}\n"
|
||||
f"**Uptime**: {stats_data['uptime']}\n\n"
|
||||
f"_Use the dropdown to view more stats._"
|
||||
),
|
||||
"System Info": (
|
||||
f"**{SYSTEM} Hardware**\n"
|
||||
f"Cpu Usage: **{stats_data['cpu']}%**\n"
|
||||
f"Ram Usage: **{stats_data['ram']}%**\n\n"
|
||||
f"**{ZYROX_CODE} Software**\n"
|
||||
f"Python: **{sys.version_info.major}.{sys.version_info.minor}**\n"
|
||||
f"Discord.py: **{discord.__version__}**"
|
||||
),
|
||||
"General Info": (
|
||||
f"**Uptime**: `{stats_data['uptime']}`\n\n"
|
||||
f"**{ZYROX_GLOBAL} Server Stats**\n"
|
||||
f"Guilds: **{stats_data['guilds']}**\n"
|
||||
f"Users: **{stats_data['users']}**\n\n"
|
||||
f"**{ZYROX_COMMAND} Commands Stats**\n"
|
||||
f"Total Commands: **{stats_data['all_cmds']}**\n"
|
||||
f"Slash Commands: **{stats_data['slash_cmds']}**"
|
||||
),
|
||||
"Team Info": (
|
||||
"There is only one person who made me. Thanks to him ❤️.\n\n"
|
||||
f"**{ZYROX_OWNER} Main Owner**\n"
|
||||
"[01]. [runxking](https://discord.com/users/767979794411028491)\n"
|
||||
"[02]. [Ray](https://discord.com/users/870179991462236170)"
|
||||
),
|
||||
"Code Info": (
|
||||
f"**{ZYROX_SEARCH} Codebase Overview**\n\n"
|
||||
f"Files: **{stats_data['files']}**\n"
|
||||
f"Lines: **{stats_data['lines']}**\n"
|
||||
f"Words: **{stats_data['words']}**"
|
||||
),
|
||||
}
|
||||
return content_map.get(selected, "")
|
||||
|
||||
|
||||
class StatsView(LayoutView):
|
||||
def __init__(self, ctx, stats_data):
|
||||
super().__init__(timeout=300)
|
||||
self.ctx = ctx
|
||||
self.stats_data = stats_data
|
||||
|
||||
self.select = Select(
|
||||
placeholder=f"{BRAND_NAME} Statistics",
|
||||
options=[
|
||||
discord.SelectOption(
|
||||
label="Quick Overview",
|
||||
emoji=THUNDER,
|
||||
description="Quick stats overview",
|
||||
),
|
||||
discord.SelectOption(
|
||||
label="System Info",
|
||||
emoji=SYSTEM,
|
||||
description="System usage",
|
||||
),
|
||||
discord.SelectOption(
|
||||
label="General Info",
|
||||
emoji=ZYROX_GLOBAL,
|
||||
description="General info",
|
||||
),
|
||||
discord.SelectOption(
|
||||
label="Team Info",
|
||||
emoji=CODEBASE,
|
||||
description="Bot team",
|
||||
),
|
||||
discord.SelectOption(
|
||||
label="Code Info",
|
||||
emoji=ZYROX_SEARCH,
|
||||
description="Code stats",
|
||||
),
|
||||
],
|
||||
)
|
||||
self.select.callback = self.on_select
|
||||
|
||||
self.add_item(
|
||||
Container(
|
||||
TextDisplay(f"**{BRAND_NAME} Stats Panel**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(create_stats_content(stats_data, "Quick Overview")),
|
||||
ActionRow(self.select),
|
||||
)
|
||||
)
|
||||
|
||||
async def on_select(self, interaction: discord.Interaction):
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
await interaction.response.send_message(
|
||||
"Only the command invoker can use this menu.", ephemeral=True
|
||||
)
|
||||
return
|
||||
|
||||
selected_value = interaction.data.get("values", ["Quick Overview"])[0]
|
||||
|
||||
new_container = Container(
|
||||
TextDisplay(f"**{BRAND_NAME} Stats Panel**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(create_stats_content(self.stats_data, selected_value)),
|
||||
ActionRow(self.select),
|
||||
)
|
||||
|
||||
self.clear_items()
|
||||
self.add_item(new_container)
|
||||
|
||||
await interaction.response.edit_message(view=self)
|
||||
|
||||
|
||||
class StatsLoadingView(LayoutView):
|
||||
def __init__(self):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
Container(
|
||||
TextDisplay(
|
||||
f"{LOADING} **Generating {BRAND_NAME} Statistics...**"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Stats(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.start_time = time.time()
|
||||
self.total_songs_played = 0
|
||||
self.command_usage_count = 0
|
||||
self.bot.loop.create_task(self.setup_database())
|
||||
|
||||
async def setup_database(self):
|
||||
async with aiosqlite.connect("db/stats.db") as db:
|
||||
await db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS stats (key TEXT PRIMARY KEY, value INTEGER)"
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
cursor = await db.execute(
|
||||
"SELECT value FROM stats WHERE key = 'total_songs_played'"
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
self.total_songs_played = row[0] if row else 0
|
||||
|
||||
cursor = await db.execute(
|
||||
"SELECT value FROM stats WHERE key = 'command_usage_count'"
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
self.command_usage_count = row[0] if row else 0
|
||||
|
||||
def format_number(self, num):
|
||||
if num < 1000:
|
||||
return str(num)
|
||||
elif num < 1_000_000:
|
||||
return f"{num / 1_000:.4f}k"
|
||||
elif num < 1_000_000_000:
|
||||
return f"{num / 1_000_000:.2f}M"
|
||||
else:
|
||||
return f"{num / 1_000_000_000:.2f}B"
|
||||
|
||||
@commands.hybrid_command(
|
||||
name="stats",
|
||||
aliases=["botstats", "statistics", "botinfo"],
|
||||
help="Shows the bot's information.",
|
||||
)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 7, commands.BucketType.user)
|
||||
async def stats(self, ctx):
|
||||
loading_view = StatsLoadingView()
|
||||
loading_msg = await ctx.reply(view=loading_view)
|
||||
|
||||
uptime = str(
|
||||
datetime.timedelta(seconds=int(round(time.time() - self.start_time)))
|
||||
)
|
||||
total_users = sum(g.member_count for g in self.bot.guilds if g.member_count)
|
||||
slash_cmds = len(self.bot.tree.get_commands())
|
||||
all_cmds = len(set(self.bot.walk_commands()))
|
||||
cpu_usage = psutil.cpu_percent(interval=None)
|
||||
ram_usage = psutil.virtual_memory().percent
|
||||
|
||||
stats_data = {
|
||||
"guilds": len(self.bot.guilds),
|
||||
"users": total_users,
|
||||
"uptime": uptime,
|
||||
"cpu": cpu_usage,
|
||||
"ram": ram_usage,
|
||||
"all_cmds": all_cmds,
|
||||
"slash_cmds": slash_cmds,
|
||||
"files": 0,
|
||||
"lines": 0,
|
||||
"words": 0,
|
||||
}
|
||||
|
||||
files, lines, words = analyze_codebase(".")
|
||||
stats_data["files"] = files
|
||||
stats_data["lines"] = lines
|
||||
stats_data["words"] = words
|
||||
|
||||
main_view = StatsView(ctx, stats_data)
|
||||
await loading_msg.edit(view=main_view)
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Stats(bot))
|
||||
153
bot/cogs/commands/status.py
Normal file
153
bot/cogs/commands/status.py
Normal file
@@ -0,0 +1,153 @@
|
||||
import discord
|
||||
from utils.emoji import DND, ICON_BROWSER, IDLE, LOADING_ALT1, MOBILE, OFFLINE, ONLINE, PC
|
||||
from discord.ext import commands
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import aiohttp
|
||||
import os
|
||||
from utils.Tools import *
|
||||
|
||||
class Status(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(name="status", help="Shows the status of the user in detail.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
async def status(self, ctx, user: discord.User = None):
|
||||
user = user or ctx.author
|
||||
processing = await ctx.send(f"{LOADING_ALT1} Loading Status...")
|
||||
embed = discord.Embed(title=f"{user.display_name}'s Status", color=0xFF0000)
|
||||
|
||||
status_emoji = {
|
||||
"online": f"{ONLINE} Online",
|
||||
"idle": f"{IDLE} Idle",
|
||||
"dnd": f"{DND} Do Not Disturb",
|
||||
"offline": f"{OFFLINE} Offline"
|
||||
}
|
||||
|
||||
member = None
|
||||
for guild in self.bot.guilds:
|
||||
member = guild.get_member(user.id)
|
||||
if member:
|
||||
break
|
||||
|
||||
if member:
|
||||
status = status_emoji.get(str(member.status), f"{OFFLINE} Offline")
|
||||
embed.add_field(name="Status:", value=status, inline=False)
|
||||
|
||||
avatar_url = member.avatar.url if member.avatar else member.default_avatar.url
|
||||
embed.set_thumbnail(url=avatar_url)
|
||||
|
||||
platform = self.get_platform(member)
|
||||
embed.add_field(name="Platform:", value=platform, inline=False)
|
||||
|
||||
custom_status = self.get_custom_status(member)
|
||||
if custom_status:
|
||||
embed.add_field(name="Custom Status:", value=custom_status, inline=False)
|
||||
|
||||
activity_text = self.get_activity_text(member.activities)
|
||||
if activity_text:
|
||||
embed.add_field(name="__Activity__:", value=activity_text, inline=False)
|
||||
|
||||
for activity in member.activities:
|
||||
if isinstance(activity, discord.Spotify):
|
||||
song_name = activity.title
|
||||
album_cover_url = str(activity.album_cover_url)
|
||||
|
||||
album_image_path = 'data/pictures/album_image.png'
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(album_cover_url) as resp:
|
||||
if resp.status == 200:
|
||||
album_data = await resp.read()
|
||||
with open(album_image_path, 'wb') as f:
|
||||
f.write(album_data)
|
||||
|
||||
card_image_path = self.create_spotify_card(song_name, album_image_path)
|
||||
|
||||
if os.path.exists(card_image_path):
|
||||
file = discord.File(card_image_path, filename="spotify_card.png")
|
||||
embed.set_image(url="attachment://spotify_card.png")
|
||||
else:
|
||||
await ctx.send("Failed to generate the Spotify card image.")
|
||||
else:
|
||||
try:
|
||||
user = await self.bot.fetch_user(user.id)
|
||||
embed.add_field(name="Status:", value=f"{OFFLINE} Offline", inline=False)
|
||||
avatar_url = user.default_avatar.url
|
||||
embed.set_thumbnail(url=avatar_url)
|
||||
except discord.NotFound:
|
||||
await ctx.send("User not found.")
|
||||
return
|
||||
|
||||
requester_avatar_url = ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url
|
||||
embed.set_footer(text=f"Requested by {ctx.author.display_name}", icon_url=requester_avatar_url)
|
||||
|
||||
await ctx.send(embed=embed, file=file if 'file' in locals() else None)
|
||||
await processing.delete()
|
||||
|
||||
def create_spotify_card(self, song_name, album_image_path):
|
||||
card_path = 'data/pictures/spotify.png'
|
||||
output_path = 'data/pictures/spotify_card_output.png'
|
||||
|
||||
base_img = Image.open(card_path).convert("RGBA")
|
||||
draw = ImageDraw.Draw(base_img)
|
||||
|
||||
album_img = Image.open(album_image_path).convert("RGBA")
|
||||
album_img = album_img.resize((160, 160))
|
||||
|
||||
mask = Image.new("L", album_img.size, 0)
|
||||
draw_mask = ImageDraw.Draw(mask)
|
||||
draw_mask.ellipse((0, 0, 160, 160), fill=255)
|
||||
|
||||
base_img.paste(album_img, (30, 30), mask)
|
||||
|
||||
font_path = 'utils/arial.ttf'
|
||||
font = ImageFont.truetype(font_path, 40)
|
||||
|
||||
truncated_song_name = song_name if len(song_name) <= 60 else song_name[:57] + "..."
|
||||
song_name_position = (220, 70)
|
||||
draw.text(song_name_position, truncated_song_name, font=font, fill="white")
|
||||
|
||||
base_img.save(output_path)
|
||||
return output_path
|
||||
|
||||
def get_platform(self, member):
|
||||
if member.desktop_status != discord.Status.offline:
|
||||
return f"{PC} Desktop"
|
||||
elif member.mobile_status != discord.Status.offline:
|
||||
return f"{MOBILE} Mobile"
|
||||
elif member.web_status != discord.Status.offline:
|
||||
return f"{ICON_BROWSER} Browser"
|
||||
return "Unknown"
|
||||
|
||||
def get_custom_status(self, member):
|
||||
for activity in member.activities:
|
||||
if isinstance(activity, discord.CustomActivity):
|
||||
status_text = activity.name or ""
|
||||
if activity.name == "Custom Status":
|
||||
status_text = ""
|
||||
status_emoji = str(activity.emoji) if activity.emoji else ""
|
||||
|
||||
if status_emoji and not status_text:
|
||||
return status_emoji
|
||||
elif status_emoji and status_text:
|
||||
return f"{status_emoji} {status_text}"
|
||||
elif status_text:
|
||||
return status_text
|
||||
return None
|
||||
|
||||
def get_activity_text(self, activities):
|
||||
activity_list = []
|
||||
for activity in activities:
|
||||
if isinstance(activity, discord.Game):
|
||||
activity_list.append(f"Playing {activity.name}")
|
||||
elif isinstance(activity, discord.Streaming):
|
||||
activity_list.append(f"Streaming {activity.name} on **[Twitch]({activity.url})**")
|
||||
elif isinstance(activity, discord.Spotify):
|
||||
activity_list.append(f"**[Listening to Spotify](https://open.spotify.com/track/{activity.track_id})**")
|
||||
elif isinstance(activity, discord.Activity):
|
||||
activity_list.append(f"{activity.type.name.capitalize()} {activity.name}")
|
||||
return "\n".join(activity_list) if activity_list else None
|
||||
|
||||
186
bot/cogs/commands/steal.py
Normal file
186
bot/cogs/commands/steal.py
Normal file
@@ -0,0 +1,186 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ui import View, Button
|
||||
import requests
|
||||
from io import BytesIO
|
||||
import re
|
||||
from utils.Tools import *
|
||||
|
||||
class Steal(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
|
||||
|
||||
@commands.hybrid_command(name="steal", help="Steal an emoji or sticker", usage="steal <emoji>", aliases=["eadd"], with_app_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 3, commands.BucketType.user)
|
||||
@commands.has_permissions(manage_emojis=True)
|
||||
async def steal(self, ctx, emote=None):
|
||||
if ctx.message.reference:
|
||||
ref_message = await ctx.channel.fetch_message(ctx.message.reference.message_id)
|
||||
attachments = ref_message.attachments
|
||||
stickers = ref_message.stickers
|
||||
emojis = [emote for emote in ref_message.content.split() if emote.startswith('<:') or emote.startswith('<a:')]
|
||||
|
||||
if attachments or stickers or emojis:
|
||||
await self.create_buttons(ctx, attachments, stickers, emojis)
|
||||
return
|
||||
|
||||
if emote:
|
||||
await self.process_emoji(ctx, emote)
|
||||
else:
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description="No emoji or sticker found", color=0xFF0000))
|
||||
|
||||
async def process_emoji(self, ctx, emote):
|
||||
try:
|
||||
if emote[0] == '<':
|
||||
name = emote.split(':')[1]
|
||||
emoji_id = emote.split(':')[2][:-1]
|
||||
anim = emote.split(':')[0]
|
||||
if anim == '<a':
|
||||
url = f'https://cdn.discordapp.com/emojis/{emoji_id}.gif'
|
||||
else:
|
||||
url = f'https://cdn.discordapp.com/emojis/{emoji_id}.png'
|
||||
await self.add_emoji(ctx, url, name, animated=(anim == '<a'))
|
||||
else:
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description="Invalid emoji", color=0xFF0000))
|
||||
except Exception as e:
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description=f"Failed to add emoji: {str(e)}", color=0xFF0000))
|
||||
|
||||
async def add_emoji(self, ctx, url, name, animated):
|
||||
try:
|
||||
if not self.has_emoji_slot(ctx.guild, animated):
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description="No more emoji slots available", color=0xFF0000))
|
||||
return
|
||||
|
||||
sanitized_name = self.sanitize_name(name)
|
||||
response = requests.get(url)
|
||||
img = response.content
|
||||
emote = await ctx.guild.create_custom_emoji(name=sanitized_name, image=img)
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description=f"Added emoji \"**{emote}**\"!", color=0xFF0000))
|
||||
except Exception as e:
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description=f"Failed to add emoji: {str(e)}", color=0xFF0000))
|
||||
|
||||
async def add_sticker(self, ctx, url, name):
|
||||
try:
|
||||
if len(ctx.guild.stickers) >= self.get_max_sticker_count(ctx.guild):
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description="No more sticker slots available", color=0xFF0000))
|
||||
return
|
||||
|
||||
sanitized_name = self.sanitize_name(name)
|
||||
response = requests.get(url)
|
||||
img = BytesIO(response.content)
|
||||
emoji = "⭐"
|
||||
await ctx.guild.create_sticker(name=sanitized_name, description="Added by bot", file=discord.File(img, filename="sticker.png"), emoji=emoji)
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description=f"Added sticker \"**{sanitized_name}**\"!", color=0xFF0000))
|
||||
except Exception as e:
|
||||
await ctx.send(embed=discord.Embed(title="Steal", description=f"Failed to add sticker: {str(e)}", color=0xFF0000))
|
||||
|
||||
def sanitize_name(self, name):
|
||||
sanitized = re.sub(r'[^a-zA-Z0-9_]', '_', name)
|
||||
return sanitized[:32]
|
||||
|
||||
def has_emoji_slot(self, guild, animated):
|
||||
normal_emojis = [emoji for emoji in guild.emojis if not emoji.animated]
|
||||
animated_emojis = [emoji for emoji in guild.emojis if emoji.animated]
|
||||
max_normal, max_animated = self.get_max_emoji_count(guild)
|
||||
|
||||
if animated:
|
||||
return len(animated_emojis) < max_animated
|
||||
else:
|
||||
return len(normal_emojis) < max_normal
|
||||
|
||||
def get_max_emoji_count(self, guild):
|
||||
if guild.premium_tier == 3:
|
||||
return 250, 250
|
||||
elif guild.premium_tier == 2:
|
||||
return 150, 150
|
||||
elif guild.premium_tier == 1:
|
||||
return 100, 100
|
||||
else:
|
||||
return 50, 50
|
||||
|
||||
def get_max_sticker_count(self, guild):
|
||||
if guild.premium_tier == 3:
|
||||
return 60
|
||||
elif guild.premium_tier == 2:
|
||||
return 30
|
||||
elif guild.premium_tier == 1:
|
||||
return 15
|
||||
else:
|
||||
return 5
|
||||
|
||||
async def create_buttons(self, ctx, attachments, stickers, emojis):
|
||||
class StealView(View):
|
||||
def __init__(self, bot, ctx, attachments, stickers, emojis):
|
||||
super().__init__()
|
||||
self.bot = bot
|
||||
self.ctx = ctx
|
||||
self.attachments = attachments
|
||||
self.stickers = stickers
|
||||
self.emojis = emojis
|
||||
|
||||
@discord.ui.button(label="Steal as Emoji", style=discord.ButtonStyle.primary)
|
||||
async def steal_as_emoji(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
for sticker in self.stickers:
|
||||
|
||||
if sticker.format in [discord.StickerFormatType.png, discord.StickerFormatType.apng, discord.StickerFormatType.lottie]:
|
||||
animated = sticker.format == discord.StickerFormatType.apng
|
||||
await self.bot.cogs['Steal'].add_emoji(self.ctx, sticker.url, sticker.name.replace(' ', '_'), animated=animated)
|
||||
else:
|
||||
await self.ctx.send(embed=discord.Embed(title="Steal", description=f"Unsupported sticker format for {sticker.name}", color=0xFF0000))
|
||||
for attachment in self.attachments:
|
||||
await self.bot.cogs['Steal'].add_emoji(self.ctx, attachment.url, attachment.filename.split('.')[0].replace(' ', '_'), animated=False)
|
||||
for emote in self.emojis:
|
||||
name = emote.split(':')[1]
|
||||
emoji_id = emote.split(':')[2][:-1]
|
||||
anim = emote.split(':')[0]
|
||||
if anim == '<a':
|
||||
url = f'https://cdn.discordapp.com/emojis/{emoji_id}.gif'
|
||||
else:
|
||||
url = f'https://cdn.discordapp.com/emojis/{emoji_id}.png'
|
||||
await self.bot.cogs['Steal'].add_emoji(self.ctx, url, name, animated=(anim == '<a'))
|
||||
|
||||
@discord.ui.button(label="Steal as Sticker", style=discord.ButtonStyle.success)
|
||||
async def steal_as_sticker(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
await interaction.response.send_message("This interaction is not for you.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
for sticker in self.stickers:
|
||||
await self.bot.cogs['Steal'].add_sticker(self.ctx, sticker.url, sticker.name)
|
||||
for attachment in self.attachments:
|
||||
await self.bot.cogs['Steal'].add_sticker(self.ctx, attachment.url, attachment.filename.split('.')[0])
|
||||
for emote in self.emojis:
|
||||
name = emote.split(':')[1]
|
||||
emoji_id = emote.split(':')[2][:-1]
|
||||
anim = emote.split(':')[0]
|
||||
if anim == '<a':
|
||||
url = f'https://cdn.discordapp.com/emojis/{emoji_id}.gif'
|
||||
else:
|
||||
url = f'https://cdn.discordapp.com/emojis/{emoji_id}.png'
|
||||
await self.bot.cogs['Steal'].add_sticker(self.ctx, url, name)
|
||||
|
||||
embed = discord.Embed(description="Choose what to steal:", color=0xFF0000)
|
||||
if attachments:
|
||||
embed.set_image(url=attachments[0].url)
|
||||
elif stickers:
|
||||
embed.set_image(url=stickers[0].url)
|
||||
elif emojis:
|
||||
for emote in emojis:
|
||||
emoji_id = emote.split(':')[2][:-1]
|
||||
url = f"https://cdn.discordapp.com/emojis/{emoji_id}.png"
|
||||
embed.set_image(url=url)
|
||||
|
||||
view = StealView(self.bot, ctx, attachments, stickers, emojis)
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
|
||||
401
bot/cogs/commands/stickymessage.py
Normal file
401
bot/cogs/commands/stickymessage.py
Normal file
@@ -0,0 +1,401 @@
|
||||
import discord
|
||||
import aiosqlite
|
||||
import json
|
||||
import asyncio
|
||||
import os
|
||||
from discord.ext import commands
|
||||
from typing import Optional
|
||||
|
||||
RED_THEME_COLOR = 0xFF0000
|
||||
|
||||
class StickyMessage(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
asyncio.create_task(self.setup_database())
|
||||
|
||||
async def setup_database(self):
|
||||
if not os.path.exists("db"):
|
||||
os.makedirs("db")
|
||||
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS sticky_messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id INTEGER NOT NULL,
|
||||
channel_id INTEGER NOT NULL,
|
||||
message_type TEXT DEFAULT 'plain',
|
||||
message_content TEXT,
|
||||
embed_data TEXT,
|
||||
last_message_id INTEGER,
|
||||
enabled BOOLEAN DEFAULT 1,
|
||||
delay_seconds INTEGER DEFAULT 2,
|
||||
auto_delete_after INTEGER DEFAULT 0,
|
||||
ignore_bots BOOLEAN DEFAULT 1,
|
||||
ignore_commands BOOLEAN DEFAULT 1,
|
||||
trigger_count INTEGER DEFAULT 1,
|
||||
current_count INTEGER DEFAULT 0,
|
||||
UNIQUE(guild_id, channel_id)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message: discord.Message):
|
||||
if not message.guild or (message.author.bot and message.author.id != self.bot.user.id):
|
||||
return
|
||||
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
cursor = await db.execute(
|
||||
"SELECT * FROM sticky_messages WHERE channel_id = ? AND enabled = 1",
|
||||
(message.channel.id,)
|
||||
)
|
||||
sticky_data = await cursor.fetchone()
|
||||
|
||||
if not sticky_data:
|
||||
return
|
||||
|
||||
if sticky_data['ignore_bots'] and message.author.bot:
|
||||
return
|
||||
|
||||
if sticky_data['ignore_commands']:
|
||||
prefixes = await self.bot.get_prefix(message)
|
||||
if isinstance(prefixes, str):
|
||||
prefixes = [prefixes]
|
||||
if any(message.content.startswith(p) for p in prefixes):
|
||||
return
|
||||
|
||||
new_count = sticky_data['current_count'] + 1
|
||||
if new_count < sticky_data['trigger_count']:
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute(
|
||||
"UPDATE sticky_messages SET current_count = ? WHERE id = ?",
|
||||
(new_count, sticky_data['id'])
|
||||
)
|
||||
await db.commit()
|
||||
return
|
||||
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute("UPDATE sticky_messages SET current_count = 0 WHERE id = ?", (sticky_data['id'],))
|
||||
await db.commit()
|
||||
|
||||
await asyncio.sleep(sticky_data['delay_seconds'])
|
||||
|
||||
if sticky_data['last_message_id']:
|
||||
try:
|
||||
last_msg = await message.channel.fetch_message(sticky_data['last_message_id'])
|
||||
await last_msg.delete()
|
||||
except discord.NotFound:
|
||||
pass
|
||||
|
||||
content = None
|
||||
embed = None
|
||||
delete_after = sticky_data['auto_delete_after'] if sticky_data['auto_delete_after'] > 0 else None
|
||||
|
||||
if sticky_data['message_type'] == 'plain':
|
||||
content = sticky_data['message_content']
|
||||
else:
|
||||
try:
|
||||
embed_data = json.loads(sticky_data['embed_data'])
|
||||
embed = discord.Embed(
|
||||
title=embed_data.get('title'),
|
||||
description=embed_data.get('description'),
|
||||
color=RED_THEME_COLOR
|
||||
)
|
||||
if footer := embed_data.get('footer'):
|
||||
embed.set_footer(text=footer)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
embed = discord.Embed(description="Error: Could not decode embed data.", color=RED_THEME_COLOR)
|
||||
|
||||
new_sticky = await message.channel.send(content=content, embed=embed, delete_after=delete_after)
|
||||
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute(
|
||||
"UPDATE sticky_messages SET last_message_id = ? WHERE id = ?",
|
||||
(new_sticky.id, sticky_data['id'])
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
@commands.group(aliases=['sticky', 'sm'], invoke_without_command=True)
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def stickymessage(self, ctx: commands.Context):
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
|
||||
@stickymessage.command(name='setup')
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def sticky_setup(self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None):
|
||||
channel = channel or ctx.channel
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
cursor = await db.execute("SELECT 1 FROM sticky_messages WHERE channel_id = ?", (channel.id,))
|
||||
if await cursor.fetchone():
|
||||
embed = discord.Embed(title="Setup Error", description=f"A sticky message already exists in {channel.mention}.", color=RED_THEME_COLOR)
|
||||
return await ctx.send(embed=embed)
|
||||
|
||||
embed = discord.Embed(title="Sticky Message Setup", description=f"Choose the message type for {channel.mention}:", color=RED_THEME_COLOR)
|
||||
await ctx.send(embed=embed, view=StickySetupView(ctx, channel))
|
||||
|
||||
@stickymessage.command(name='remove', aliases=['delete', 'del'])
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def sticky_remove(self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None):
|
||||
channel = channel or ctx.channel
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
cursor = await db.execute("SELECT last_message_id FROM sticky_messages WHERE channel_id = ?", (channel.id,))
|
||||
data = await cursor.fetchone()
|
||||
if not data:
|
||||
return await ctx.send(embed=discord.Embed(title="Not Found", description=f"No sticky message found in {channel.mention}.", color=RED_THEME_COLOR))
|
||||
|
||||
if data[0]:
|
||||
try:
|
||||
msg = await channel.fetch_message(data[0])
|
||||
await msg.delete()
|
||||
except discord.NotFound:
|
||||
pass
|
||||
|
||||
await db.execute("DELETE FROM sticky_messages WHERE channel_id = ?", (channel.id,))
|
||||
await db.commit()
|
||||
|
||||
embed = discord.Embed(title="Success", description=f"Sticky message in {channel.mention} has been removed.", color=RED_THEME_COLOR)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@stickymessage.command(name='list')
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def sticky_list(self, ctx: commands.Context):
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
cursor = await db.execute("SELECT * FROM sticky_messages WHERE guild_id = ?", (ctx.guild.id,))
|
||||
stickies = await cursor.fetchall()
|
||||
|
||||
if not stickies:
|
||||
return await ctx.send(embed=discord.Embed(title="No Stickies Found", description="There are no active sticky messages on this server.", color=RED_THEME_COLOR))
|
||||
|
||||
embed = discord.Embed(title=f"Sticky Messages in {ctx.guild.name}", color=RED_THEME_COLOR)
|
||||
for sticky in stickies:
|
||||
channel = self.bot.get_channel(sticky['channel_id'])
|
||||
status = "✅ Enabled" if sticky['enabled'] else "❌ Disabled"
|
||||
embed.add_field(
|
||||
name=f"#{channel.name if channel else 'Unknown Channel'}",
|
||||
value=f"**Type**: {sticky['message_type'].title()}\n**Status**: {status}\n**Delay**: {sticky['delay_seconds']}s",
|
||||
inline=True
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@stickymessage.command(name='edit')
|
||||
@commands.has_permissions(manage_messages=True)
|
||||
async def sticky_edit(self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None):
|
||||
channel = channel or ctx.channel
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
cursor = await db.execute("SELECT * FROM sticky_messages WHERE channel_id = ?", (channel.id,))
|
||||
sticky_data = await cursor.fetchone()
|
||||
|
||||
if not sticky_data:
|
||||
return await ctx.send(embed=discord.Embed(title="Not Found", description=f"No sticky message found in {channel.mention}.", color=RED_THEME_COLOR))
|
||||
|
||||
embed = discord.Embed(title="Edit Sticky Message", description=f"Editing sticky for {channel.mention}. Choose an option:", color=RED_THEME_COLOR)
|
||||
await ctx.send(embed=embed, view=StickyEditView(ctx, channel, sticky_data))
|
||||
|
||||
class AuthorOnlyView(discord.ui.View):
|
||||
def __init__(self, ctx: commands.Context, timeout: float = 300.0):
|
||||
super().__init__(timeout=timeout)
|
||||
self.ctx = ctx
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
await interaction.response.send_message("You are not authorized to use this.", ephemeral=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
async def on_timeout(self):
|
||||
try:
|
||||
for item in self.children:
|
||||
item.disabled = True
|
||||
if hasattr(self, 'message'):
|
||||
await self.message.edit(view=self)
|
||||
except discord.NotFound:
|
||||
pass
|
||||
|
||||
class StickySetupView(AuthorOnlyView):
|
||||
def __init__(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
super().__init__(ctx)
|
||||
self.channel = channel
|
||||
|
||||
@discord.ui.button(label='Plain Text', style=discord.ButtonStyle.primary, emoji='📝')
|
||||
async def plain_text(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
await interaction.response.send_modal(PlainTextModal(self.ctx, self.channel))
|
||||
|
||||
@discord.ui.button(label='Embed', style=discord.ButtonStyle.secondary, emoji='📋')
|
||||
async def embed_message(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
await interaction.response.send_modal(EmbedModal(self.ctx, self.channel))
|
||||
|
||||
@discord.ui.button(label='Cancel', style=discord.ButtonStyle.danger)
|
||||
async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
embed = discord.Embed(title="Cancelled", description="Sticky message setup has been cancelled.", color=RED_THEME_COLOR)
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
|
||||
class PlainTextModal(discord.ui.Modal, title='Plain Text Sticky Message'):
|
||||
def __init__(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
super().__init__()
|
||||
self.ctx = ctx
|
||||
self.channel = channel
|
||||
|
||||
message_content = discord.ui.TextInput(label='Message Content', style=discord.TextStyle.long, required=True, max_length=2000)
|
||||
delay_seconds = discord.ui.TextInput(label='Delay (seconds)', default='2', required=False, max_length=3)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
try:
|
||||
delay = int(self.delay_seconds.value or "2")
|
||||
except ValueError:
|
||||
delay = 2
|
||||
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute(
|
||||
"INSERT INTO sticky_messages (guild_id, channel_id, message_type, message_content, delay_seconds) VALUES (?, ?, ?, ?, ?)",
|
||||
(self.ctx.guild.id, self.channel.id, 'plain', self.message_content.value, delay)
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
embed = discord.Embed(title="Sticky Created", description=f"Successfully created a plain text sticky in {self.channel.mention}.", color=RED_THEME_COLOR)
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
|
||||
class EmbedModal(discord.ui.Modal, title='Embed Sticky Message'):
|
||||
def __init__(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
super().__init__()
|
||||
self.ctx = ctx
|
||||
self.channel = channel
|
||||
|
||||
title = discord.ui.TextInput(label='Embed Title', required=False, max_length=256)
|
||||
description = discord.ui.TextInput(label='Embed Description', style=discord.TextStyle.long, required=True, max_length=4000)
|
||||
footer = discord.ui.TextInput(label='Embed Footer', required=False, max_length=2048)
|
||||
delay_seconds = discord.ui.TextInput(label='Delay (seconds)', default='2', required=False, max_length=3)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
try:
|
||||
delay = int(self.delay_seconds.value or "2")
|
||||
except ValueError:
|
||||
delay = 2
|
||||
|
||||
embed_data = {
|
||||
"title": self.title.value,
|
||||
"description": self.description.value,
|
||||
"color": f"#{RED_THEME_COLOR:06x}",
|
||||
"footer": self.footer.value
|
||||
}
|
||||
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute(
|
||||
"INSERT INTO sticky_messages (guild_id, channel_id, message_type, embed_data, delay_seconds) VALUES (?, ?, ?, ?, ?)",
|
||||
(self.ctx.guild.id, self.channel.id, 'embed', json.dumps(embed_data), delay)
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
embed = discord.Embed(title="Sticky Embed Created", description=f"Successfully created an embed sticky in {self.channel.mention}.", color=RED_THEME_COLOR)
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
|
||||
class StickyEditView(AuthorOnlyView):
|
||||
def __init__(self, ctx: commands.Context, channel: discord.TextChannel, sticky_data):
|
||||
super().__init__(ctx)
|
||||
self.channel = channel
|
||||
self.sticky_data = sticky_data
|
||||
|
||||
@discord.ui.button(label='Edit Content', style=discord.ButtonStyle.primary)
|
||||
async def edit_content(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if self.sticky_data['message_type'] == 'plain':
|
||||
await interaction.response.send_modal(EditPlainTextModal(self.ctx, self.channel, self.sticky_data))
|
||||
else:
|
||||
await interaction.response.send_modal(EditEmbedModal(self.ctx, self.channel, self.sticky_data))
|
||||
|
||||
@discord.ui.button(label='Edit Settings', style=discord.ButtonStyle.secondary)
|
||||
async def edit_settings(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
await interaction.response.send_modal(EditSettingsModal(self.ctx, self.channel, self.sticky_data))
|
||||
|
||||
@discord.ui.button(label='Cancel', style=discord.ButtonStyle.danger)
|
||||
async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
embed = discord.Embed(title="Cancelled", description="Editing has been cancelled.", color=RED_THEME_COLOR)
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
|
||||
class EditPlainTextModal(discord.ui.Modal, title='Edit Plain Text Content'):
|
||||
def __init__(self, ctx: commands.Context, channel: discord.TextChannel, sticky_data):
|
||||
super().__init__()
|
||||
self.ctx = ctx
|
||||
self.channel = channel
|
||||
self.message_content = discord.ui.TextInput(
|
||||
label='Message Content',
|
||||
style=discord.TextStyle.long,
|
||||
default=sticky_data['message_content'],
|
||||
max_length=2000
|
||||
)
|
||||
self.add_item(self.message_content)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute(
|
||||
"UPDATE sticky_messages SET message_content = ? WHERE channel_id = ?",
|
||||
(self.message_content.value, self.channel.id)
|
||||
)
|
||||
await db.commit()
|
||||
embed = discord.Embed(title="Content Updated", description="The sticky message content has been updated.", color=RED_THEME_COLOR)
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
|
||||
class EditEmbedModal(discord.ui.Modal, title='Edit Embed Content'):
|
||||
def __init__(self, ctx: commands.Context, channel: discord.TextChannel, sticky_data):
|
||||
super().__init__()
|
||||
self.ctx = ctx
|
||||
self.channel = channel
|
||||
embed_data = json.loads(sticky_data['embed_data'])
|
||||
|
||||
self.title = discord.ui.TextInput(label='Embed Title', default=embed_data.get('title', ''), required=False, max_length=256)
|
||||
self.description = discord.ui.TextInput(label='Embed Description', style=discord.TextStyle.long, default=embed_data.get('description', ''), required=True, max_length=4000)
|
||||
self.footer = discord.ui.TextInput(label='Embed Footer', default=embed_data.get('footer', ''), required=False, max_length=2048)
|
||||
|
||||
self.add_item(self.title)
|
||||
self.add_item(self.description)
|
||||
self.add_item(self.footer)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
embed_data = {
|
||||
"title": self.title.value, "description": self.description.value,
|
||||
"color": f"#{RED_THEME_COLOR:06x}", "footer": self.footer.value
|
||||
}
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute(
|
||||
"UPDATE sticky_messages SET embed_data = ? WHERE channel_id = ?",
|
||||
(json.dumps(embed_data), self.channel.id)
|
||||
)
|
||||
await db.commit()
|
||||
embed = discord.Embed(title="Embed Updated", description="The sticky embed content has been updated.", color=RED_THEME_COLOR)
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
|
||||
class EditSettingsModal(discord.ui.Modal, title='Edit Sticky Settings'):
|
||||
def __init__(self, ctx: commands.Context, channel: discord.TextChannel, sticky_data):
|
||||
super().__init__()
|
||||
self.ctx = ctx
|
||||
self.channel = channel
|
||||
|
||||
self.delay_seconds = discord.ui.TextInput(label='Delay (seconds)', default=str(sticky_data['delay_seconds']), max_length=3)
|
||||
self.auto_delete_after = discord.ui.TextInput(label='Auto-delete (seconds, 0=off)', default=str(sticky_data['auto_delete_after']), max_length=4)
|
||||
self.trigger_count = discord.ui.TextInput(label='Trigger After X Msgs', default=str(sticky_data['trigger_count']), max_length=2)
|
||||
|
||||
self.add_item(self.delay_seconds)
|
||||
self.add_item(self.auto_delete_after)
|
||||
self.add_item(self.trigger_count)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
try:
|
||||
delay = int(self.delay_seconds.value)
|
||||
auto_del = int(self.auto_delete_after.value)
|
||||
trigger = int(self.trigger_count.value)
|
||||
except ValueError:
|
||||
return await interaction.response.send_message("Please enter valid numbers.", ephemeral=True)
|
||||
|
||||
async with aiosqlite.connect("db/stickymessages.db") as db:
|
||||
await db.execute(
|
||||
"UPDATE sticky_messages SET delay_seconds = ?, auto_delete_after = ?, trigger_count = ? WHERE channel_id = ?",
|
||||
(delay, auto_del, trigger, self.channel.id)
|
||||
)
|
||||
await db.commit()
|
||||
embed = discord.Embed(title="Settings Updated", description="The sticky message settings have been updated.", color=RED_THEME_COLOR)
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
|
||||
async def setup(bot: commands.Bot):
|
||||
await bot.add_cog(StickyMessage(bot))
|
||||
507
bot/cogs/commands/ticket.py
Normal file
507
bot/cogs/commands/ticket.py
Normal file
@@ -0,0 +1,507 @@
|
||||
# cogs/commands/ticket.py
|
||||
|
||||
import discord
|
||||
from utils.emoji import CROSS, DELETE_ALT1, HANDSHAKE, LOCK, TICK, UNLOCK, ZBAN, ZMODULE, ZWRENCH
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
import asyncio
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
from utils.config import *
|
||||
|
||||
# --- Configurable Variables ---
|
||||
EMBED_COLOR = 0xFF0000
|
||||
TICKET_CHANNEL_IMAGE_URL = "https://cdn.discordapp.com/attachments/1403014653214330951/1403022431303630900/images_2.jpg?ex=68a1e776&is=68a095f6&hm=2c4e74b079fa409410920507bfb55549d485aafa89e194d15ab548eaba684555&"
|
||||
|
||||
# --- Emoji Variables ---
|
||||
SUCCESS_EMOJI = TICK
|
||||
ERROR_EMOJI = CROSS
|
||||
LOCK_EMOJI = f"{UNLOCK} "
|
||||
UNLOCK_EMOJI = LOCK
|
||||
CLAIM_EMOJI = HANDSHAKE
|
||||
CLOSE_EMOJI = ZBAN
|
||||
DELETE_EMOJI = DELETE_ALT1
|
||||
REOPEN_EMOJI = ZWRENCH
|
||||
TRANSCRIPT_EMOJI = ZMODULE
|
||||
|
||||
# --- Constants ---
|
||||
if not os.path.exists('db'):
|
||||
os.makedirs('db')
|
||||
DB_PATH = 'db/ticket.db'
|
||||
MAX_CATEGORIES = 15
|
||||
TICKET_LIMIT_PER_USER = 3
|
||||
|
||||
# --- Database Class ---
|
||||
class TicketDatabase:
|
||||
def __init__(self, path):
|
||||
self.conn = sqlite3.connect(path, check_same_thread=False)
|
||||
self.conn.row_factory = sqlite3.Row
|
||||
self._create_tables()
|
||||
|
||||
def _create_tables(self):
|
||||
with self.conn:
|
||||
self.conn.execute("CREATE TABLE IF NOT EXISTS guild_configs (guild_id INTEGER PRIMARY KEY, panel_channel_id INTEGER, logging_channel_id INTEGER, panel_message_id INTEGER, panel_type TEXT, embed_title TEXT, embed_description TEXT, embed_color INTEGER, embed_image_url TEXT, embed_thumbnail_url TEXT, closed_category_id INTEGER)")
|
||||
self.conn.execute("CREATE TABLE IF NOT EXISTS ticket_categories (category_id INTEGER PRIMARY KEY AUTOINCREMENT, guild_id INTEGER, name TEXT NOT NULL, emoji TEXT, notified_roles TEXT, button_style INTEGER, discord_category_id INTEGER, FOREIGN KEY (guild_id) REFERENCES guild_configs(guild_id) ON DELETE CASCADE)")
|
||||
self.conn.execute("CREATE TABLE IF NOT EXISTS open_tickets (channel_id INTEGER PRIMARY KEY, ticket_number INTEGER, guild_id INTEGER, creator_id INTEGER NOT NULL, category_db_id INTEGER, created_at TEXT NOT NULL, closed_by_id INTEGER, closed_at TEXT, is_locked BOOLEAN DEFAULT FALSE, is_claimed BOOLEAN DEFAULT FALSE, claimed_by_id INTEGER, FOREIGN KEY (guild_id) REFERENCES guild_configs(guild_id) ON DELETE CASCADE, FOREIGN KEY (category_db_id) REFERENCES ticket_categories(category_id) ON DELETE SET NULL)")
|
||||
self.conn.execute("CREATE TABLE IF NOT EXISTS user_ticket_counts (guild_id INTEGER, user_id INTEGER, ticket_count INTEGER DEFAULT 0, PRIMARY KEY (guild_id, user_id))")
|
||||
|
||||
def execute(self, q, p=()):
|
||||
with self.conn: return self.conn.execute(q, p)
|
||||
def fetchone(self, q, p=()):
|
||||
cur = self.conn.cursor(); cur.execute(q, p); return cur.fetchone()
|
||||
def fetchall(self, q, p=()):
|
||||
cur = self.conn.cursor(); cur.execute(q, p); return cur.fetchall()
|
||||
def close(self):
|
||||
if self.conn: self.conn.close()
|
||||
|
||||
# --- Utility Functions ---
|
||||
async def get_or_create_log_channel(db, guild):
|
||||
config = db.fetchone("SELECT logging_channel_id FROM guild_configs WHERE guild_id = ?", (guild.id,))
|
||||
if config and config["logging_channel_id"] and (ch := guild.get_channel(config["logging_channel_id"])): return ch
|
||||
overwrites = {guild.default_role: discord.PermissionOverwrite(view_channel=False)}
|
||||
try:
|
||||
ch = await guild.create_text_channel(f"{BRAND_NAME}-ticket-logs", overwrites=overwrites)
|
||||
db.execute("INSERT INTO guild_configs (guild_id, logging_channel_id) VALUES (?,?) ON CONFLICT(guild_id) DO UPDATE SET logging_channel_id=excluded.logging_channel_id", (guild.id, ch.id))
|
||||
return ch
|
||||
except: return None
|
||||
|
||||
async def log_ticket_action(db, guild, user, action, details):
|
||||
if log_channel := await get_or_create_log_channel(db, guild):
|
||||
embed = discord.Embed(title=f"Ticket Action: {action}", color=EMBED_COLOR, timestamp=datetime.now())
|
||||
embed.add_field(name="Action By", value=user.mention).add_field(name="Details", value=details, inline=False)
|
||||
try: await log_channel.send(embed=embed)
|
||||
except: pass
|
||||
|
||||
async def get_or_create_closed_category(db, guild):
|
||||
config = db.fetchone("SELECT closed_category_id FROM guild_configs WHERE guild_id = ?", (guild.id,))
|
||||
if config and config["closed_category_id"] and (cat := guild.get_channel(config["closed_category_id"])): return cat
|
||||
overwrites = {guild.default_role: discord.PermissionOverwrite(view_channel=False)}
|
||||
try:
|
||||
cat = await guild.create_category("Closed Tickets", overwrites=overwrites)
|
||||
db.execute("UPDATE guild_configs SET closed_category_id = ? WHERE guild_id = ?", (cat.id, guild.id))
|
||||
return cat
|
||||
except: return None
|
||||
|
||||
# --- Setup Views ---
|
||||
class EmbedEditorView(discord.ui.View):
|
||||
def __init__(self, cog, ctx, panel_channel, panel_type):
|
||||
super().__init__(timeout=600)
|
||||
self.cog, self.ctx, self.panel_channel, self.panel_type = cog, ctx, panel_channel, panel_type
|
||||
self.message = None
|
||||
self.embed_data = {"title": "Support Tickets", "description": "Click a button or select an option below to create a ticket.", "color": EMBED_COLOR}
|
||||
|
||||
def _create_preview_embed(self):
|
||||
embed = discord.Embed.from_dict(self.embed_data)
|
||||
if img_url := self.embed_data.get("image", {}).get("url"): embed.set_image(url=img_url)
|
||||
if thumb_url := self.embed_data.get("thumbnail", {}).get("url"): embed.set_thumbnail(url=thumb_url)
|
||||
return embed
|
||||
|
||||
async def start(self, interaction):
|
||||
await interaction.response.send_message("Use the buttons to customize the panel embed.", embed=self._create_preview_embed(), view=self, ephemeral=True)
|
||||
self.message = await interaction.original_response()
|
||||
|
||||
async def _prompt(self, inter, prompt):
|
||||
await inter.response.send_message(prompt, ephemeral=True)
|
||||
try:
|
||||
msg = await self.cog.bot.wait_for("message", check=lambda m: m.author.id == self.ctx.author.id and m.channel.id == self.ctx.channel.id, timeout=120)
|
||||
try: await msg.delete()
|
||||
except: pass
|
||||
return msg.content
|
||||
except: return None
|
||||
|
||||
@discord.ui.button(label="Title", style=discord.ButtonStyle.green, row=0)
|
||||
async def edit_title(self, inter, button):
|
||||
if title := await self._prompt(inter, "Enter new title:"):
|
||||
self.embed_data["title"] = title
|
||||
await self.message.edit(embed=self._create_preview_embed())
|
||||
|
||||
@discord.ui.button(label="Description", style=discord.ButtonStyle.green, row=0)
|
||||
async def edit_desc(self, inter, button):
|
||||
if desc := await self._prompt(inter, "Enter new description:"):
|
||||
self.embed_data["description"] = desc
|
||||
await self.message.edit(embed=self._create_preview_embed())
|
||||
|
||||
@discord.ui.button(label="Image URL", style=discord.ButtonStyle.blurple, row=1)
|
||||
async def edit_image(self, inter, button):
|
||||
if url := await self._prompt(inter, "Enter image URL (`none` to remove):"):
|
||||
self.embed_data["image"] = {"url": url} if url.lower() != 'none' else {}
|
||||
await self.message.edit(embed=self._create_preview_embed())
|
||||
|
||||
@discord.ui.button(label="Thumbnail URL", style=discord.ButtonStyle.blurple, row=1)
|
||||
async def edit_thumb(self, inter, button):
|
||||
if url := await self._prompt(inter, "Enter thumbnail URL (`none` to remove):"):
|
||||
self.embed_data["thumbnail"] = {"url": url} if url.lower() != 'none' else {}
|
||||
await self.message.edit(embed=self._create_preview_embed())
|
||||
|
||||
@discord.ui.button(label="Submit & Continue", style=discord.ButtonStyle.primary, row=2)
|
||||
async def submit(self, inter, button):
|
||||
await inter.response.defer()
|
||||
for item in self.children: item.disabled = True
|
||||
try: await self.message.edit(view=self)
|
||||
except: pass
|
||||
self.cog.db.execute("INSERT INTO guild_configs (guild_id, panel_channel_id, panel_type, embed_title, embed_description, embed_color, embed_image_url, embed_thumbnail_url) VALUES (?,?,?,?,?,?,?,?) ON CONFLICT(guild_id) DO UPDATE SET panel_channel_id=excluded.panel_channel_id, panel_type=excluded.panel_type, embed_title=excluded.embed_title, embed_description=excluded.embed_description, embed_color=excluded.embed_color, embed_image_url=excluded.embed_image_url, embed_thumbnail_url=excluded.embed_thumbnail_url", (self.ctx.guild.id, self.panel_channel.id, self.panel_type, self.embed_data["title"], self.embed_data["description"], self.embed_data["color"], self.embed_data.get("image",{}).get("url"), self.embed_data.get("thumbnail",{}).get("url")))
|
||||
await CategoryConfigView(self.cog, self.ctx).start(inter)
|
||||
self.stop()
|
||||
|
||||
class CategoryConfigView(discord.ui.View):
|
||||
def __init__(self, cog, ctx):
|
||||
super().__init__(timeout=900)
|
||||
self.cog, self.ctx, self.message, self.categories = cog, ctx, None, []
|
||||
self._setup_buttons()
|
||||
|
||||
def _setup_buttons(self):
|
||||
panel_type = self.cog.db.fetchone("SELECT panel_type FROM guild_configs WHERE guild_id=?", (self.ctx.guild.id,))['panel_type']
|
||||
self.add_item(discord.ui.Button(label=f"Add Category", style=discord.ButtonStyle.success, custom_id="add_cat"))
|
||||
self.remove_select = discord.ui.Select(placeholder="Select a category to remove...", custom_id="remove_cat")
|
||||
self.add_item(self.remove_select)
|
||||
self.add_item(discord.ui.Button(label="Finish Setup", style=discord.ButtonStyle.primary, custom_id="finish_setup", row=2))
|
||||
|
||||
async def start(self, interaction):
|
||||
self._update_remove_select()
|
||||
await interaction.followup.send(embed=self._update_embed(), view=self, ephemeral=True)
|
||||
self.message = await interaction.original_response()
|
||||
|
||||
def _update_embed(self):
|
||||
embed = discord.Embed(title="Category Configuration", description="Add or remove ticket categories for your panel.", color=EMBED_COLOR)
|
||||
embed.add_field(name="Current Categories", value="\n".join([f"{c['emoji'] or ''} {c['name']}" for c in self.categories]) or "None yet. Click 'Add Category' to begin.")
|
||||
return embed
|
||||
|
||||
def _update_remove_select(self):
|
||||
self.remove_select.options = [discord.SelectOption(label=c['name'], value=str(i), emoji=c.get('emoji')) for i, c in enumerate(self.categories)] or [discord.SelectOption(label="No categories to remove", value="placeholder")]
|
||||
|
||||
async def _prompt(self, inter: discord.Interaction, prompt_text: str, followup: bool = False):
|
||||
send_method = inter.followup.send if followup else inter.response.send_message
|
||||
await send_method(prompt_text, ephemeral=True)
|
||||
try:
|
||||
msg = await self.cog.bot.wait_for("message", check=lambda m: m.author.id == self.ctx.author.id and m.channel.id == inter.channel.id, timeout=120.0)
|
||||
try: await msg.delete()
|
||||
except discord.HTTPException: pass
|
||||
return msg.content
|
||||
except asyncio.TimeoutError:
|
||||
return None
|
||||
|
||||
async def interaction_check(self, interaction):
|
||||
if interaction.user.id != self.ctx.author.id: return False
|
||||
custom_id = interaction.data["custom_id"]
|
||||
if custom_id == "add_cat": await self._add_category_flow(interaction)
|
||||
elif custom_id == "remove_cat": await self._remove_category(interaction, interaction.data["values"][0])
|
||||
elif custom_id == "finish_setup": await self._finish_setup(interaction)
|
||||
return True
|
||||
|
||||
async def _add_category_flow(self, inter: discord.Interaction):
|
||||
await inter.response.defer()
|
||||
if len(self.categories) >= MAX_CATEGORIES:
|
||||
return await inter.followup.send(f"Max {MAX_CATEGORIES} categories reached.", ephemeral=True)
|
||||
|
||||
cat_name = await self._prompt(inter, "Please type the name for the new category (e.g., General Support).", followup=True)
|
||||
if not cat_name: return await inter.followup.send("Timed out.", ephemeral=True)
|
||||
|
||||
emoji = await self._prompt(inter, 'Please provide an emoji for the category, or type `skip`.', followup=True)
|
||||
if not emoji: return await inter.followup.send("Timed out.", ephemeral=True)
|
||||
if emoji.lower() == 'skip': emoji = None
|
||||
|
||||
role_input = await self._prompt(inter, 'Please mention one or more staff roles to ping, separated by spaces (e.g., `@Ticket Support @Moderator`), or type `none`.', followup=True)
|
||||
if not role_input: return await inter.followup.send("Timed out.", ephemeral=True)
|
||||
|
||||
role_ids = []
|
||||
if role_input.lower() != 'none':
|
||||
role_mentions = re.findall(r'<@&(\d+)>', role_input)
|
||||
for role_id_str in role_mentions:
|
||||
role_ids.append(int(role_id_str))
|
||||
|
||||
self.categories.append({
|
||||
"name": cat_name,
|
||||
"emoji": emoji,
|
||||
"notified_roles": ",".join(map(str, role_ids)) if role_ids else None,
|
||||
"button_style": discord.ButtonStyle.secondary.value
|
||||
})
|
||||
self._update_remove_select()
|
||||
await self.message.edit(embed=self._update_embed(), view=self)
|
||||
await inter.followup.send(f"Category '{cat_name}' added/removed successfully.", ephemeral=True)
|
||||
|
||||
async def _remove_category(self, inter, value):
|
||||
if value == "placeholder": return await inter.response.defer()
|
||||
try:
|
||||
idx = int(value)
|
||||
if 0 <= idx < len(self.categories):
|
||||
self.categories.pop(idx)
|
||||
except ValueError:
|
||||
pass
|
||||
self._update_remove_select()
|
||||
await self.message.edit(embed=self._update_embed(), view=self)
|
||||
await inter.response.defer()
|
||||
|
||||
async def _finish_setup(self, inter):
|
||||
if not self.categories: return await inter.response.send_message("Add at least one category.", ephemeral=True)
|
||||
await inter.response.defer()
|
||||
db, guild_id = self.cog.db, self.ctx.guild.id
|
||||
db.execute("DELETE FROM ticket_categories WHERE guild_id = ?", (guild_id,))
|
||||
for cat in self.categories:
|
||||
try: cat_ch = await self.ctx.guild.create_category(f"{cat['name']} Tickets", overwrites={self.ctx.guild.default_role: discord.PermissionOverwrite(view_channel=False)})
|
||||
except: return await inter.followup.send(f"Can't create category for `{cat['name']}`.", ephemeral=True)
|
||||
db.execute('INSERT INTO ticket_categories (guild_id, name, emoji, notified_roles, button_style, discord_category_id) VALUES (?,?,?,?,?,?)', (guild_id,cat['name'],cat['emoji'],cat['notified_roles'],cat['button_style'],cat_ch.id))
|
||||
config = db.fetchone("SELECT * FROM guild_configs WHERE guild_id=?", (guild_id,))
|
||||
panel_ch = self.ctx.guild.get_channel(config['panel_channel_id'])
|
||||
panel_embed = discord.Embed(title=config['embed_title'], description=config['embed_description'], color=config['embed_color'])
|
||||
if img_url := config['embed_image_url']: panel_embed.set_image(url=img_url)
|
||||
if thumb_url := config['embed_thumbnail_url']: panel_embed.set_thumbnail(url=thumb_url)
|
||||
final_view = self.cog.create_panel_view(guild_id)
|
||||
msg = await panel_ch.send(embed=panel_embed, view=final_view)
|
||||
db.execute("UPDATE guild_configs SET panel_message_id = ? WHERE guild_id = ?", (msg.id, guild_id))
|
||||
await self.message.edit(content=f"{SUCCESS_EMOJI} Setup complete! Panel sent to {panel_ch.mention}.", view=None, embed=None)
|
||||
self.stop()
|
||||
|
||||
class TicketCog(commands.Cog, name="Ticket System"):
|
||||
def __init__(self, bot):
|
||||
self.bot, self.db = bot, TicketDatabase(DB_PATH)
|
||||
self.bot.loop.create_task(self.load_persistent_views())
|
||||
|
||||
async def load_persistent_views(self):
|
||||
await self.bot.wait_until_ready()
|
||||
for config in self.db.fetchall("SELECT guild_id, panel_message_id FROM guild_configs WHERE panel_message_id IS NOT NULL"):
|
||||
if view := self.create_panel_view(config['guild_id']): self.bot.add_view(view, message_id=config['panel_message_id'])
|
||||
|
||||
def create_panel_view(self, guild_id):
|
||||
config = self.db.fetchone("SELECT panel_type FROM guild_configs WHERE guild_id=?", (guild_id,))
|
||||
categories = self.db.fetchall("SELECT * FROM ticket_categories WHERE guild_id=?", (guild_id,))
|
||||
if not config or not categories: return None
|
||||
view_class = TicketPanelSelect if config['panel_type'] == 'dropdown' else TicketPanelButtons
|
||||
view = view_class(self)
|
||||
if config['panel_type'] == 'dropdown':
|
||||
view.children[0].options = [discord.SelectOption(label=c['name'], value=str(c['category_id']), emoji=c['emoji']) for c in categories]
|
||||
else:
|
||||
for c in categories: view.add_item(discord.ui.Button(label=c['name'], style=discord.ButtonStyle(c['button_style']), emoji=c['emoji'], custom_id=f"create_ticket_{c['category_id']}"))
|
||||
return view
|
||||
|
||||
def cog_unload(self): self.db.close()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_interaction(self, inter):
|
||||
if inter.type == discord.InteractionType.component and (cid := inter.data.get("custom_id","")).startswith("create_ticket_"): await self.create_ticket_flow(inter, int(cid.split("_")[-1]))
|
||||
|
||||
async def create_ticket_flow(self, inter, cat_id):
|
||||
await inter.response.defer(ephemeral=True)
|
||||
guild, user = inter.guild, inter.user
|
||||
if (count := self.db.fetchone("SELECT ticket_count FROM user_ticket_counts WHERE guild_id=? AND user_id=?",(guild.id,user.id))) and count['ticket_count'] >= TICKET_LIMIT_PER_USER: return await inter.followup.send(f"You have reached the max of {TICKET_LIMIT_PER_USER} open tickets.",ephemeral=True)
|
||||
|
||||
cat_info = self.db.fetchone("SELECT * FROM ticket_categories WHERE category_id=?", (cat_id,))
|
||||
disc_cat = guild.get_channel(cat_info['discord_category_id'])
|
||||
if not cat_info or not disc_cat: return await inter.followup.send("This ticket category has been deleted or is misconfigured.", ephemeral=True)
|
||||
|
||||
t_num = (self.db.fetchone("SELECT MAX(ticket_number) as n FROM open_tickets WHERE guild_id=?", (guild.id,))['n'] or 0) + 1
|
||||
|
||||
overwrites = {guild.default_role:discord.PermissionOverwrite(view_channel=False), user:discord.PermissionOverwrite(view_channel=True), guild.me:discord.PermissionOverwrite(view_channel=True, manage_channels=True)}
|
||||
|
||||
pings = [user.mention]
|
||||
if cat_info['notified_roles']:
|
||||
for role_id in cat_info['notified_roles'].split(','):
|
||||
if role := guild.get_role(int(role_id)):
|
||||
overwrites[role] = discord.PermissionOverwrite(view_channel=True)
|
||||
pings.append(role.mention)
|
||||
|
||||
try: ch = await disc_cat.create_text_channel(name=f"ticket-{t_num:04d}-{user.name.lower()}", overwrites=overwrites)
|
||||
except: return await inter.followup.send("I lack permissions to create a channel.", ephemeral=True)
|
||||
|
||||
self.db.execute('INSERT INTO open_tickets VALUES (?,?,?,?,?,?,?,?,?,?,?)', (ch.id,t_num,guild.id,user.id,cat_id,datetime.now().isoformat(),None,None,False,False,None))
|
||||
self.db.execute('INSERT INTO user_ticket_counts VALUES (?,?,1) ON CONFLICT(guild_id,user_id) DO UPDATE SET ticket_count=ticket_count+1', (guild.id,user.id))
|
||||
await log_ticket_action(self.db, guild, user, "Ticket Created", f"Ticket {ch.mention} by {user.mention} (Category: {cat_info['name']}).")
|
||||
|
||||
ticket_embed = discord.Embed(title=f"Welcome to your Ticket ( #{t_num:04d} )", description="Thank you for reaching out for support. Our staff team has been notified and will be with you as soon as possible.\n\nPlease describe your issue in detail while you wait.", color=EMBED_COLOR)
|
||||
ticket_embed.set_image(url=TICKET_CHANNEL_IMAGE_URL)
|
||||
await ch.send(content=" ".join(pings), embed=ticket_embed, view=TicketActionsView(self, ch.id, cat_id))
|
||||
await inter.followup.send(f"Your ticket has been successfully created: {ch.mention}", ephemeral=True)
|
||||
|
||||
@commands.hybrid_group(name="ticket", description="Main command group for the ticket system.")
|
||||
@commands.guild_only()
|
||||
async def ticket(self, ctx):
|
||||
if ctx.invoked_subcommand is None: await ctx.send_help(ctx.command)
|
||||
|
||||
@ticket.command(name="setup", description="Start the interactive setup for the ticket panel.")
|
||||
@commands.has_permissions(manage_guild=True)
|
||||
@app_commands.describe(style="The style of the ticket creation panel.", channel="The channel where the ticket panel will be sent.")
|
||||
@app_commands.choices(style=[app_commands.Choice(name="Dropdown Menu", value="dropdown"), app_commands.Choice(name="Buttons", value="button")])
|
||||
async def setup(self, ctx, style: app_commands.Choice[str], channel: discord.TextChannel):
|
||||
await EmbedEditorView(self, ctx, channel, style.value).start(ctx.interaction)
|
||||
|
||||
@ticket.command(name="close", description="Close the current ticket channel.")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def close(self, ctx): await self._dispatch_action(ctx, "close")
|
||||
|
||||
@ticket.command(name="lock", description="Lock the ticket, preventing the user from sending messages.")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def lock(self, ctx): await self._dispatch_action(ctx, "lock")
|
||||
|
||||
@ticket.command(name="unlock", description="Unlock the ticket, allowing the user to send messages again.")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def unlock(self, ctx): await self._dispatch_action(ctx, "unlock")
|
||||
|
||||
@ticket.command(name="claim", description="Claim the ticket to notify others that you are handling it.")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def claim(self, ctx): await self._dispatch_action(ctx, "claim")
|
||||
|
||||
@ticket.command(name="transcript", description="Generate a transcript of a closed ticket.")
|
||||
@commands.has_permissions(manage_channels=True)
|
||||
async def transcript(self, ctx):
|
||||
if not ctx.interaction: return await ctx.send("Please use the slash command version of this command.")
|
||||
await ClosedTicketActionsView(self, ctx.channel.id)._generate_transcript(ctx.interaction, False)
|
||||
|
||||
class TicketPanelSelect(discord.ui.View):
|
||||
def __init__(self, cog): super().__init__(timeout=None); self.cog = cog
|
||||
@discord.ui.select(placeholder="Select a category to open a ticket...", custom_id="ticket_panel_select")
|
||||
async def select_ticket(self, inter, select): await self.cog.create_ticket_flow(inter, int(select.values[0]))
|
||||
|
||||
class TicketPanelButtons(discord.ui.View):
|
||||
def __init__(self, cog): super().__init__(timeout=None); self.cog = cog
|
||||
|
||||
class TicketActionsView(discord.ui.View):
|
||||
def __init__(self, cog, ch_id, cat_id):
|
||||
super().__init__(timeout=None)
|
||||
self.cog, self.ch_id, self.cat_id = cog, ch_id, cat_id
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||
cat_info = self.cog.db.fetchone("SELECT notified_roles FROM ticket_categories WHERE category_id=?", (self.cat_id,))
|
||||
if not cat_info or not cat_info['notified_roles']:
|
||||
await interaction.response.send_message("This ticket is misconfigured; no staff roles are assigned.", ephemeral=True)
|
||||
return False
|
||||
|
||||
allowed_role_ids = {int(r_id) for r_id in cat_info['notified_roles'].split(',')}
|
||||
user_role_ids = {role.id for role in interaction.user.roles}
|
||||
|
||||
if not user_role_ids.intersection(allowed_role_ids):
|
||||
await interaction.response.send_message("You do not have the required role to perform this action.", ephemeral=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
@discord.ui.button(label="Lock", emoji=LOCK_EMOJI, custom_id="t_lock", style=discord.ButtonStyle.secondary)
|
||||
async def b_lock(self, i, b):
|
||||
t = self.cog.db.fetchone("SELECT * FROM open_tickets WHERE channel_id=?", (self.ch_id,))
|
||||
if t['is_locked']: return await i.response.send_message("This ticket is already locked.", ephemeral=True)
|
||||
creator = i.guild.get_member(t['creator_id'])
|
||||
if creator: await i.channel.set_permissions(creator, send_messages=False)
|
||||
self.cog.db.execute("UPDATE open_tickets SET is_locked=1 WHERE channel_id=?", (self.ch_id,))
|
||||
await i.response.send_message(f"{LOCK_EMOJI} Ticket locked by {i.user.mention}.")
|
||||
await log_ticket_action(self.cog.db, i.guild, i.user, "Locked", f"{i.channel.mention}")
|
||||
|
||||
@discord.ui.button(label="Unlock", emoji=UNLOCK_EMOJI, custom_id="t_unlock", style=discord.ButtonStyle.secondary)
|
||||
async def b_unlock(self, i, b):
|
||||
t = self.cog.db.fetchone("SELECT * FROM open_tickets WHERE channel_id=?", (self.ch_id,))
|
||||
if not t['is_locked']: return await i.response.send_message("This ticket is already unlocked.", ephemeral=True)
|
||||
creator = i.guild.get_member(t['creator_id'])
|
||||
if creator: await i.channel.set_permissions(creator, send_messages=True)
|
||||
self.cog.db.execute("UPDATE open_tickets SET is_locked=0 WHERE channel_id=?", (self.ch_id,))
|
||||
await i.response.send_message(f"{UNLOCK_EMOJI} Ticket unlocked by {i.user.mention}.")
|
||||
await log_ticket_action(self.cog.db, i.guild, i.user, "Unlocked", f"{i.channel.mention}")
|
||||
|
||||
@discord.ui.button(label="Claim", emoji=CLAIM_EMOJI, custom_id="t_claim", style=discord.ButtonStyle.primary)
|
||||
async def b_claim(self, i, b):
|
||||
t = self.cog.db.fetchone("SELECT * FROM open_tickets WHERE channel_id=?", (self.ch_id,))
|
||||
if t['is_claimed']: return await i.response.send_message(f"This ticket is already claimed by <@{t['claimed_by_id']}>.", ephemeral=True)
|
||||
self.cog.db.execute("UPDATE open_tickets SET is_claimed=1, claimed_by_id=? WHERE channel_id=?", (i.user.id, self.ch_id))
|
||||
await i.response.send_message(f"{CLAIM_EMOJI} Ticket claimed by {i.user.mention}. They will now handle this request.")
|
||||
await log_ticket_action(self.cog.db, i.guild, i.user, "Claimed", f"{i.channel.mention}")
|
||||
|
||||
@discord.ui.button(label="Close", emoji=CLOSE_EMOJI, style=discord.ButtonStyle.danger, custom_id="t_close")
|
||||
async def b_close(self, i, b):
|
||||
await i.response.defer(ephemeral=True)
|
||||
t = self.cog.db.fetchone("SELECT * FROM open_tickets WHERE channel_id=?", (self.ch_id,))
|
||||
creator = i.guild.get_member(t['creator_id'])
|
||||
if creator:
|
||||
self.cog.db.execute("UPDATE user_ticket_counts SET ticket_count=MAX(0,ticket_count-1) WHERE guild_id=? AND user_id=?", (i.guild.id, creator.id))
|
||||
await i.channel.set_permissions(creator, send_messages=False, view_channel=False)
|
||||
|
||||
category_info = self.cog.db.fetchone("SELECT name FROM ticket_categories WHERE category_id=?", (self.cat_id,))
|
||||
category_name = category_info['name'] if category_info else "Unknown"
|
||||
|
||||
closed_category = await get_or_create_closed_category(self.cog.db, i.guild)
|
||||
if closed_category: await i.channel.edit(category=closed_category)
|
||||
|
||||
self.cog.db.execute("UPDATE open_tickets SET closed_by_id=?, closed_at=? WHERE channel_id=?", (i.user.id, datetime.now().isoformat(), self.ch_id))
|
||||
await log_ticket_action(self.cog.db, i.guild, i.user, "Closed", f"Ticket {i.channel.mention} (Category: {category_name})")
|
||||
|
||||
closed_embed = discord.Embed(
|
||||
title="Ticket Closed",
|
||||
description=f"This ticket has been officially closed and archived by {i.user.mention}.\nThe user has been removed from the channel.\n\nStaff can use the buttons below to reopen, create a transcript, or permanently delete the channel.",
|
||||
color=EMBED_COLOR,
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
closed_embed.add_field(name="Ticket Creator", value=f"<@{t['creator_id']}>", inline=True)
|
||||
closed_embed.add_field(name="Closed By", value=i.user.mention, inline=True)
|
||||
closed_embed.add_field(name="Original Category", value=category_name, inline=True)
|
||||
|
||||
await i.channel.send(embed=closed_embed, view=ClosedTicketActionsView(self.cog, self.ch_id, self.cat_id))
|
||||
await i.message.edit(view=None)
|
||||
await i.followup.send("Ticket successfully closed and archived.", ephemeral=True)
|
||||
self.stop()
|
||||
|
||||
class ClosedTicketActionsView(discord.ui.View):
|
||||
def __init__(self, cog, ch_id, cat_id):
|
||||
super().__init__(timeout=None)
|
||||
self.cog, self.ch_id, self.cat_id = cog, ch_id, cat_id
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||
cat_info = self.cog.db.fetchone("SELECT notified_roles FROM ticket_categories WHERE category_id=?", (self.cat_id,))
|
||||
if not cat_info or not cat_info['notified_roles']: return False
|
||||
allowed_role_ids = {int(r_id) for r_id in cat_info['notified_roles'].split(',')}
|
||||
user_role_ids = {role.id for role in interaction.user.roles}
|
||||
if not user_role_ids.intersection(allowed_role_ids):
|
||||
await interaction.response.send_message("You do not have the required role for this action.", ephemeral=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
@discord.ui.button(label="Reopen", emoji=REOPEN_EMOJI, style=discord.ButtonStyle.success)
|
||||
async def b_reopen(self, i: discord.Interaction, button: discord.ui.Button):
|
||||
await i.response.defer(ephemeral=True)
|
||||
t = self.cog.db.fetchone("SELECT * FROM open_tickets WHERE channel_id=?", (self.ch_id,))
|
||||
cat_info = self.cog.db.fetchone("SELECT discord_category_id FROM ticket_categories WHERE category_id=?", (self.cat_id,))
|
||||
|
||||
original_category = i.guild.get_channel(cat_info['discord_category_id'])
|
||||
if original_category: await i.channel.edit(category=original_category)
|
||||
|
||||
creator = i.guild.get_member(t['creator_id'])
|
||||
if creator:
|
||||
await i.channel.set_permissions(creator, view_channel=True, send_messages=True)
|
||||
self.cog.db.execute("INSERT INTO user_ticket_counts VALUES (?,?,1) ON CONFLICT(guild_id,user_id) DO UPDATE SET ticket_count=ticket_count+1", (i.guild.id, creator.id))
|
||||
|
||||
self.cog.db.execute("UPDATE open_tickets SET closed_by_id=NULL, closed_at=NULL WHERE channel_id=?", (self.ch_id,))
|
||||
|
||||
reopen_embed = discord.Embed(title="Ticket Reopened", description=f"This ticket has been reopened by {i.user.mention}.", color=EMBED_COLOR)
|
||||
await i.channel.send(embed=reopen_embed, view=TicketActionsView(self.cog, self.ch_id, self.cat_id))
|
||||
await i.message.edit(view=None)
|
||||
await log_ticket_action(self.cog.db, i.guild, i.user, "Reopened", f"{i.channel.mention}")
|
||||
self.stop()
|
||||
|
||||
@discord.ui.button(label="Transcript", emoji=TRANSCRIPT_EMOJI, style=discord.ButtonStyle.primary)
|
||||
async def b_transcript(self, i, b): await self._generate_transcript(i, False)
|
||||
|
||||
@discord.ui.button(label="Delete", emoji=DELETE_EMOJI, style=discord.ButtonStyle.danger)
|
||||
async def b_delete(self, i, b): await self._generate_transcript(i, True)
|
||||
|
||||
async def _generate_transcript(self, i, delete_after):
|
||||
await i.response.defer(ephemeral=True, thinking=True)
|
||||
ch = i.guild.get_channel(self.ch_id)
|
||||
if not ch: return await i.followup.send("Channel not found.", ephemeral=True)
|
||||
|
||||
messages = [m async for m in ch.history(limit=None, oldest_first=True)]
|
||||
content = f"Transcript for ticket #{ch.name} in {i.guild.name}\n\n"
|
||||
for m in messages:
|
||||
content += f"[{m.created_at.strftime('%Y-%m-%d %H:%M:%S')}] {m.author.display_name}: {m.clean_content}\n"
|
||||
for attachment in m.attachments: content += f" [Attachment: {attachment.url}]\n"
|
||||
file = discord.File(io.BytesIO(content.encode()), filename=f"transcript-{ch.name}.txt")
|
||||
|
||||
try:
|
||||
await i.user.send(f"Transcript for ticket {ch.mention} in {i.guild.name}:", file=file)
|
||||
await i.followup.send(f"Transcript sent to your DMs.", ephemeral=True)
|
||||
except: await i.followup.send("Could not DM you the transcript. Do you have DMs disabled?", file=file, ephemeral=True)
|
||||
|
||||
if delete_after:
|
||||
await i.followup.send("This ticket channel will be permanently deleted in 10 seconds...", ephemeral=True)
|
||||
await log_ticket_action(self.cog.db, i.guild, i.user, "Deletion Scheduled", f"{ch.mention}")
|
||||
await asyncio.sleep(10)
|
||||
await ch.delete()
|
||||
self.cog.db.execute("DELETE FROM open_tickets WHERE channel_id=?", (self.ch_id,))
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(TicketCog(bot))
|
||||
114
bot/cogs/commands/timer.py
Normal file
114
bot/cogs/commands/timer.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import time
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import asyncio
|
||||
from utils.Tools import *
|
||||
from datetime import datetime
|
||||
|
||||
class Timer(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.hybrid_command(name="timer", aliases=['tstart'], description="Starts a timer")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.cooldown(1, 30, commands.BucketType.user)
|
||||
async def _timer(self, ctx, times, *, title: str = None):
|
||||
if title is None:
|
||||
title = 'Timer'
|
||||
|
||||
try:
|
||||
try:
|
||||
time = int(times)
|
||||
except:
|
||||
convertTimeList = {'s':1, 'm':60, 'h':3600, 'd':86400, 'S':1, 'M':60, 'H':3600, 'D':86400}
|
||||
time = int(times[:-1]) * convertTimeList[times[-1]]
|
||||
if time > 86400:
|
||||
await ctx.send("Timers should not exceed a day in duration.")
|
||||
return
|
||||
if time <= 0:
|
||||
await ctx.send("Timers do not go into negatives.")
|
||||
return
|
||||
if time >= 3600:
|
||||
embed = discord.Embed(
|
||||
title=f'{title}',
|
||||
description=f"**{time//3600}** hours, **{time%3600//60}** minutes, **{time%60}** seconds",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.set_footer(text=f'Requested by {ctx.author.name}')
|
||||
message = await ctx.send(embed=embed)
|
||||
await message.add_reaction('⏱️')
|
||||
elif time >= 60:
|
||||
embed = discord.Embed(
|
||||
title=f'{title}',
|
||||
description=f"**{time//60}** minutes, **{time%60}** seconds",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.set_footer(text=f'Requested by {ctx.author.name}')
|
||||
message = await ctx.send(embed=embed)
|
||||
await message.add_reaction('⏱️')
|
||||
elif time < 60:
|
||||
embed = discord.Embed(
|
||||
title=f'{title}',
|
||||
description=f'**{time}** seconds',
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.set_footer(text=f'Requested by {ctx.author.name}')
|
||||
message = await ctx.send(embed=embed)
|
||||
await message.add_reaction('⏱️')
|
||||
while True:
|
||||
try:
|
||||
await asyncio.sleep(6)
|
||||
time -= 6
|
||||
if time >= 3600:
|
||||
embed = discord.Embed(
|
||||
title=f'{title}',
|
||||
description=f"**{time//3600}** hours, **{time%3600//60}** minutes, **{time%60}** seconds",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.set_footer(text=f'Requested by {ctx.author.name}')
|
||||
await message.edit(embed=embed)
|
||||
elif time >= 60:
|
||||
embed = discord.Embed(
|
||||
title=f'{title}',
|
||||
description=f"**{time//60}** minutes, **{time%60}** seconds",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.set_footer(text=f'Requested by {ctx.author.name}')
|
||||
await message.edit(embed=embed)
|
||||
elif time < 60:
|
||||
embed = discord.Embed(
|
||||
title=f'{title}',
|
||||
description=f"**{time}** seconds",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.set_footer(text=f'Requested by {ctx.author.name}')
|
||||
await message.edit(embed=embed)
|
||||
if time <= 0:
|
||||
embed = discord.Embed(
|
||||
title=f'{title}',
|
||||
description='Time is up!',
|
||||
color=0xFF0000
|
||||
)
|
||||
content= ctx.author.mention
|
||||
await message.edit(content=content, embed=embed)
|
||||
m = await ctx.channel.get_message(message.id)
|
||||
list_thingy = []
|
||||
output_list_thingy = []
|
||||
reactants = await m.reactions[0].users().flatten()
|
||||
reactants.pop(reactants.index(self.client.user))
|
||||
for user in reactants:
|
||||
list_thingy.append(user.id)
|
||||
x = '<@!' + str(user.id) + '>'
|
||||
output_list_thingy.append(x)
|
||||
if output_list_thingy != []:
|
||||
final = ', '.join(map(str, output_list_thingy))
|
||||
return await ctx.send(f'The timer for **{title}** has ended!\n{final}')
|
||||
else:
|
||||
return await ctx.send(f'The timer for **{title}** has ended!')
|
||||
except:
|
||||
break
|
||||
except ValueError:
|
||||
await ctx.send(f"Invalid time input.", delete_after=5)
|
||||
|
||||
|
||||
207
bot/cogs/commands/tracking.py
Normal file
207
bot/cogs/commands/tracking.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import discord
|
||||
from utils.emoji import ARROWRED
|
||||
from discord.ext import commands
|
||||
import aiosqlite
|
||||
from utils.cv2 import CV2
|
||||
|
||||
INVITE_DB = "db/invite.db"
|
||||
EMOJI_INVITE = ARROWRED
|
||||
|
||||
from utils.config import BotName
|
||||
|
||||
class Tracking(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.invites = {}
|
||||
|
||||
async def ensure_tables(self, guild_id):
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
await db.execute(f'''
|
||||
CREATE TABLE IF NOT EXISTS invites_{guild_id} (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
total INTEGER DEFAULT 0,
|
||||
fake INTEGER DEFAULT 0,
|
||||
left INTEGER DEFAULT 0,
|
||||
rejoin INTEGER DEFAULT 0
|
||||
)
|
||||
''')
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS logging (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
channel_id INTEGER
|
||||
)
|
||||
''')
|
||||
await db.commit()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
import asyncio
|
||||
async def fetch_invites(guild):
|
||||
try:
|
||||
self.invites[guild.id] = await guild.invites()
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
await asyncio.gather(*(fetch_invites(guild) for guild in self.bot.guilds))
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_invite_create(self, invite):
|
||||
try:
|
||||
self.invites[invite.guild.id] = await invite.guild.invites()
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_invite_delete(self, invite):
|
||||
try:
|
||||
self.invites[invite.guild.id] = await invite.guild.invites()
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_join(self, member):
|
||||
guild = member.guild
|
||||
await self.ensure_tables(guild.id)
|
||||
|
||||
invites_before = self.invites.get(guild.id, [])
|
||||
try:
|
||||
invites_after = await guild.invites()
|
||||
except discord.Forbidden:
|
||||
invites_after = []
|
||||
|
||||
inviter = None
|
||||
|
||||
for invite in invites_after:
|
||||
for old_invite in invites_before:
|
||||
if invite.code == old_invite.code and invite.uses > old_invite.uses:
|
||||
inviter = invite.inviter
|
||||
break
|
||||
if inviter:
|
||||
break
|
||||
|
||||
self.invites[guild.id] = invites_after
|
||||
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
if inviter:
|
||||
# Check if user has been in DB before (Rejoin)
|
||||
async with db.execute(f"SELECT user_id FROM invites_{guild.id} WHERE user_id = ?", (member.id,)) as cursor:
|
||||
user_row = await cursor.fetchone()
|
||||
|
||||
if user_row:
|
||||
await db.execute(f"UPDATE invites_{guild.id} SET rejoin = rejoin + 1 WHERE user_id = ?", (inviter.id,))
|
||||
else:
|
||||
await db.execute(f"INSERT OR IGNORE INTO invites_{guild.id} (user_id) VALUES (?)", (inviter.id,))
|
||||
await db.execute(f"UPDATE invites_{guild.id} SET total = total + 1 WHERE user_id = ?", (inviter.id,))
|
||||
await db.commit()
|
||||
|
||||
async with db.execute("SELECT channel_id FROM logging WHERE guild_id = ?", (guild.id,)) as cursor:
|
||||
log_row = await cursor.fetchone()
|
||||
|
||||
log_channel = guild.get_channel(log_row[0]) if log_row else None
|
||||
if log_channel:
|
||||
total = await self.get_total_invites(guild.id, inviter.id) if inviter else 0
|
||||
msg = (
|
||||
f"{member.mention} has joined {guild.name}, invited by "
|
||||
f"{inviter.name if inviter else 'Unknown'}, who now has {total} invites."
|
||||
)
|
||||
await log_channel.send(view=CV2("📥 Member Joined", msg))
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_remove(self, member):
|
||||
guild = member.guild
|
||||
await self.ensure_tables(guild.id)
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
await db.execute(f"UPDATE invites_{guild.id} SET left = left + 1 WHERE user_id = ?", (member.id,))
|
||||
await db.commit()
|
||||
|
||||
async def get_total_invites(self, guild_id, user_id):
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
async with db.execute(f"SELECT total FROM invites_{guild_id} WHERE user_id = ?", (user_id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
return row[0] if row else 0
|
||||
|
||||
@commands.command(aliases=["inv"])
|
||||
async def invites(self, ctx, member: discord.Member = None):
|
||||
member = member or ctx.author
|
||||
await self.ensure_tables(ctx.guild.id)
|
||||
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
async with db.execute(f"SELECT total, fake, left, rejoin FROM invites_{ctx.guild.id} WHERE user_id = ?", (member.id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if row:
|
||||
total, fake, left, rejoin = row
|
||||
real = total - fake - left - rejoin
|
||||
else:
|
||||
total = fake = left = rejoin = real = 0
|
||||
|
||||
desc = (
|
||||
f"{EMOJI_INVITE} **› {member.mention} has `{total}` invites**\n\n"
|
||||
f"**Real:** `{real}`\n"
|
||||
f"**Fake:** `{fake}`\n"
|
||||
f"**Left:** `{left}`\n"
|
||||
f"**Rejoins:** `{rejoin}`\n\n"
|
||||
f"{EMOJI_INVITE} **Get {BotName} Premium Lifetime [Join Support Here](https://discord.gg/codexdev)**"
|
||||
)
|
||||
await ctx.send(view=CV2(f"Invite Log - {member.name}", desc))
|
||||
|
||||
@commands.command(aliases=["addinvs"])
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def addinvites(self, ctx, member: discord.Member, amount: int):
|
||||
await self.ensure_tables(ctx.guild.id)
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
await db.execute(f"INSERT OR IGNORE INTO invites_{ctx.guild.id} (user_id) VALUES (?)", (member.id,))
|
||||
await db.execute(f"UPDATE invites_{ctx.guild.id} SET total = total + ? WHERE user_id = ?", (amount, member.id))
|
||||
await db.commit()
|
||||
await ctx.send(view=CV2("✅ Success", f"Added **{amount}** invites to {member.mention}."))
|
||||
|
||||
@commands.command(aliases=["setinvs"])
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def setinvites(self, ctx, member: discord.Member, amount: int):
|
||||
await self.ensure_tables(ctx.guild.id)
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
await db.execute(f"INSERT OR REPLACE INTO invites_{ctx.guild.id} (user_id, total) VALUES (?, ?)", (member.id, amount))
|
||||
await db.commit()
|
||||
await ctx.send(view=CV2("✅ Success", f"Set invites of {member.mention} to **{amount}**."))
|
||||
|
||||
@commands.command(aliases=["resetinvs"])
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def resetinvites(self, ctx, member: discord.Member):
|
||||
await self.ensure_tables(ctx.guild.id)
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
await db.execute(f"DELETE FROM invites_{ctx.guild.id} WHERE user_id = ?", (member.id,))
|
||||
await db.commit()
|
||||
await ctx.send(view=CV2("✅ Success", f"Reset invites of {member.mention}."))
|
||||
|
||||
@commands.command(aliases=["invlb"])
|
||||
async def invitesleaderboard(self, ctx):
|
||||
await self.ensure_tables(ctx.guild.id)
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
async with db.execute(f"SELECT user_id, total FROM invites_{ctx.guild.id} ORDER BY total DESC LIMIT 10") as cursor:
|
||||
data = await cursor.fetchall()
|
||||
|
||||
if not data:
|
||||
await ctx.send(view=CV2("❌ Error", "No invites found."))
|
||||
return
|
||||
|
||||
leaderboard = ""
|
||||
for idx, (user_id, total) in enumerate(data, start=1):
|
||||
user = ctx.guild.get_member(user_id)
|
||||
name = user.name if user else f"Left User ({user_id})"
|
||||
leaderboard += f"#{idx} {name} — {total} invites\n"
|
||||
|
||||
await ctx.send(view=CV2("📊 Invite Leaderboard", leaderboard))
|
||||
|
||||
@commands.command(aliases=["invlog"])
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def invitelogging(self, ctx, channel: discord.TextChannel):
|
||||
await self.ensure_tables(ctx.guild.id)
|
||||
async with aiosqlite.connect(INVITE_DB) as db:
|
||||
await db.execute("INSERT OR REPLACE INTO logging (guild_id, channel_id) VALUES (?, ?)", (ctx.guild.id, channel.id))
|
||||
await db.commit()
|
||||
await ctx.send(view=CV2("✅ Success", f"Invite logs will now be sent to {channel.mention}"))
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Tracking(bot))
|
||||
66
bot/cogs/commands/translate.py
Normal file
66
bot/cogs/commands/translate.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from deep_translator import GoogleTranslator
|
||||
from discord.ui import LayoutView, TextDisplay, Separator, Container
|
||||
from utils.cv2 import CV2, build_container
|
||||
|
||||
class TranslateSuccess(LayoutView):
|
||||
def __init__(self, original, translated, author):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("🌐 **Translation Complete**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"**Original (Hinglish):**\n{original}"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"**Translated (English):**\n{translated}"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"Requested by **{author.display_name}**")
|
||||
)
|
||||
)
|
||||
|
||||
class TranslateError(LayoutView):
|
||||
def __init__(self, error_msg):
|
||||
super().__init__(timeout=None)
|
||||
self.add_item(
|
||||
build_container(
|
||||
TextDisplay("❌ **Translation Failed**"),
|
||||
Separator(visible=True),
|
||||
TextDisplay(f"`{error_msg}`")
|
||||
)
|
||||
)
|
||||
|
||||
class TranslateCog(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.hybrid_command(
|
||||
name="hinglish",
|
||||
help="Translate informal Hinglish to proper English.",
|
||||
usage="!hinglish chlo udhr chat active krlo idhr nai"
|
||||
)
|
||||
async def hinglish(self, ctx: commands.Context, *, text: str = None):
|
||||
if not text:
|
||||
return await ctx.reply(
|
||||
"⚠️ Please provide some Hinglish text to translate.",
|
||||
ephemeral=True if ctx.interaction else False
|
||||
)
|
||||
|
||||
msg = await ctx.reply(
|
||||
"🔄 Translating Hinglish...",
|
||||
ephemeral=True if ctx.interaction else False
|
||||
)
|
||||
|
||||
try:
|
||||
# Translation using deep-translator (Google)
|
||||
translated = GoogleTranslator(source="auto", target="en").translate(text)
|
||||
|
||||
view = TranslateSuccess(text, translated, ctx.author)
|
||||
await msg.edit(content=None, view=view, embed=None)
|
||||
|
||||
except Exception as e:
|
||||
view = TranslateError(str(e))
|
||||
await msg.edit(content=None, view=view, embed=None)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(TranslateCog(bot))
|
||||
156
bot/cogs/commands/vanityroles.py
Normal file
156
bot/cogs/commands/vanityroles.py
Normal file
@@ -0,0 +1,156 @@
|
||||
import discord
|
||||
from discord.ext import commands, tasks
|
||||
import aiosqlite
|
||||
import aiohttp
|
||||
import os
|
||||
from utils.Tools import *
|
||||
|
||||
DB_PATH = "db/vanity.db"
|
||||
|
||||
class VanityRoles(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self.initialize_db())
|
||||
|
||||
async def initialize_db(self):
|
||||
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS vanity_roles (
|
||||
guild_id INTEGER,
|
||||
vanity TEXT NOT NULL,
|
||||
role_id INTEGER NOT NULL,
|
||||
log_channel_id INTEGER NOT NULL,
|
||||
current_status TEXT,
|
||||
PRIMARY KEY (guild_id, vanity)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
async with db.execute("PRAGMA table_info(vanity_roles)") as cursor:
|
||||
columns = await cursor.fetchall()
|
||||
column_names = [column[1] for column in columns]
|
||||
|
||||
if "current_status" not in column_names:
|
||||
await db.execute("ALTER TABLE vanity_roles ADD COLUMN current_status TEXT")
|
||||
await db.commit()
|
||||
|
||||
@commands.group(name="vanityroles", invoke_without_command=True)
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def vanityroles(self, ctx):
|
||||
await ctx.send("❗ Usage: `vanityroles setup <vanity> <@role> <#channel>`, `vanityroles show`, `vanityroles reset`")
|
||||
|
||||
@vanityroles.command(name="setup")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def setup(self, ctx, vanity: str, role: discord.Role, channel: discord.TextChannel):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
INSERT OR REPLACE INTO vanity_roles (guild_id, vanity, role_id, log_channel_id, current_status)
|
||||
VALUES (?, ?, ?, ?, NULL)
|
||||
""", (ctx.guild.id, vanity.lower(), role.id, channel.id))
|
||||
await db.commit()
|
||||
embed = discord.Embed(
|
||||
title="✅ Vanity Role Setup",
|
||||
description=f"Vanity: `{vanity}`\nRole: {role.mention}\nLog Channel: {channel.mention}",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@vanityroles.command(name="show")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def show(self, ctx):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT vanity, role_id, log_channel_id FROM vanity_roles WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
rows = await cursor.fetchall()
|
||||
|
||||
if not rows:
|
||||
return await ctx.send("❌ No vanity role setups found.")
|
||||
|
||||
embed = discord.Embed(title="🔧 Vanity Role Settings", color=discord.Color.blue())
|
||||
for vanity, role_id, log_channel_id in rows:
|
||||
role = ctx.guild.get_role(role_id)
|
||||
channel = ctx.guild.get_channel(log_channel_id)
|
||||
embed.add_field(
|
||||
name=f"Vanity: `{vanity}`",
|
||||
value=f"Role: {role.mention if role else role_id}\nLog: {channel.mention if channel else log_channel_id}",
|
||||
inline=False
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@vanityroles.command(name="reset")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def reset(self, ctx):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("DELETE FROM vanity_roles WHERE guild_id = ?", (ctx.guild.id,))
|
||||
await db.commit()
|
||||
await ctx.send("✅ All vanity role configurations have been reset.")
|
||||
|
||||
@tasks.loop(seconds=15)
|
||||
async def vanity_checker(self):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("SELECT guild_id, vanity, role_id, log_channel_id, current_status FROM vanity_roles") as cursor:
|
||||
rows = await cursor.fetchall()
|
||||
|
||||
for guild_id, vanity, role_id, log_channel_id, current_status in rows:
|
||||
guild = self.bot.get_guild(guild_id)
|
||||
if not guild:
|
||||
continue
|
||||
|
||||
role = guild.get_role(role_id)
|
||||
log_channel = guild.get_channel(log_channel_id)
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://discord.com/api/v10/invites/{vanity}") as response:
|
||||
is_active = response.status == 200
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error checking vanity {vanity}: {e}")
|
||||
continue
|
||||
|
||||
# Vanity is ACTIVE
|
||||
if is_active and current_status != "active":
|
||||
await self.update_status(guild_id, vanity, "active")
|
||||
assigned = 0
|
||||
for member in guild.members:
|
||||
if role and role not in member.roles:
|
||||
try:
|
||||
await member.add_roles(role, reason="Vanity active")
|
||||
assigned += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Could not add role to {member}: {e}")
|
||||
if log_channel:
|
||||
await log_channel.send(f"✅ Vanity `{vanity}` is now **active**. Role assigned to {assigned} members.")
|
||||
|
||||
# Vanity is INACTIVE
|
||||
elif not is_active and current_status == "active":
|
||||
await self.update_status(guild_id, vanity, None)
|
||||
removed = 0
|
||||
for member in guild.members:
|
||||
if role and role in member.roles:
|
||||
try:
|
||||
await member.remove_roles(role, reason="Vanity inactive")
|
||||
removed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Could not remove role from {member}: {e}")
|
||||
if log_channel:
|
||||
await log_channel.send(f"❌ Vanity `{vanity}` is now **inactive**. Role removed from {removed} members.")
|
||||
|
||||
async def update_status(self, guild_id, vanity, new_status):
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
await db.execute("""
|
||||
UPDATE vanity_roles SET current_status = ? WHERE guild_id = ? AND vanity = ?
|
||||
""", (new_status, guild_id, vanity))
|
||||
await db.commit()
|
||||
|
||||
@vanity_checker.before_loop
|
||||
async def before_checker(self):
|
||||
await self.bot.wait_until_ready()
|
||||
|
||||
async def setup(bot):
|
||||
cog = VanityRoles(bot)
|
||||
await bot.add_cog(cog)
|
||||
cog.vanity_checker.start()
|
||||
1702
bot/cogs/commands/verification.py
Normal file
1702
bot/cogs/commands/verification.py
Normal file
File diff suppressed because it is too large
Load Diff
740
bot/cogs/commands/voice.py
Normal file
740
bot/cogs/commands/voice.py
Normal file
@@ -0,0 +1,740 @@
|
||||
import discord
|
||||
from utils.emoji import CROSS, TICK
|
||||
from discord.ext import commands
|
||||
from discord.utils import get
|
||||
import os
|
||||
from utils.Tools import *
|
||||
from typing import Optional, Union
|
||||
from discord.ext.commands import Context
|
||||
from utils import Paginator, DescriptionEmbedPaginator, FieldPagePaginator, TextPaginator
|
||||
from utils import *
|
||||
|
||||
|
||||
class Voice(commands.Cog):
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.color = 0xFF0000
|
||||
|
||||
@commands.group(name="voice", invoke_without_command=True, aliases=['vc'])
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def vc(self, ctx: commands.Context):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@vc.command(name="kick",
|
||||
help="Removes a user from the voice channel.",
|
||||
usage="voice kick <member>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _kick(self, ctx, *, member: discord.Member):
|
||||
if member.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is not connected to any voice channel",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
ch = member.voice.channel.mention
|
||||
await member.edit(voice_channel=None,
|
||||
reason=f"Disconnected by {str(ctx.author)}")
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{str(member)} has been disconnected from {ch}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="kickall",
|
||||
help="Disconnect all members from the voice channel.",
|
||||
usage="voice kick all")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(move_members=True)
|
||||
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _kickall(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any voice channels.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
count = 0
|
||||
ch = ctx.author.voice.channel.mention
|
||||
for member in ctx.author.voice.channel.members:
|
||||
await member.edit(
|
||||
voice_channel=None,
|
||||
reason=f"Disconnect All Command Executed By: {str(ctx.author)}")
|
||||
count += 1
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"Disconnected {count} members from {ch}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="mute",
|
||||
help="mute a member in voice channel .",
|
||||
usage="voice mute <member>")
|
||||
@commands.has_guild_permissions(mute_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _mute(self, ctx, *, member: discord.Member = None):
|
||||
if member is None:
|
||||
embed = discord.Embed(
|
||||
title=f"{CROSS} Error",
|
||||
description="You need to mention a member to mute.",
|
||||
color=self.color
|
||||
)
|
||||
embed.set_footer(
|
||||
text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url
|
||||
)
|
||||
return await ctx.reply(embed=embed)
|
||||
|
||||
if member.voice is None:
|
||||
embed = discord.Embed(
|
||||
title=f"{CROSS} Error",
|
||||
description=f"{str(member)} is not connected to any voice channels.",
|
||||
color=self.color
|
||||
)
|
||||
embed.set_footer(
|
||||
text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url
|
||||
)
|
||||
return await ctx.reply(embed=embed)
|
||||
|
||||
if member.voice.mute:
|
||||
embed = discord.Embed(
|
||||
title=f"{CROSS} Error",
|
||||
description=f"{str(member)} is already muted in the voice channel.",
|
||||
color=self.color
|
||||
)
|
||||
embed.set_footer(
|
||||
text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url
|
||||
)
|
||||
return await ctx.reply(embed=embed)
|
||||
|
||||
await member.edit(mute=True)
|
||||
embed = discord.Embed(
|
||||
title=f"{TICK}> Success",
|
||||
description=f"{str(member)} has been muted in {member.voice.channel.mention}.",
|
||||
color=self.color
|
||||
)
|
||||
embed.set_footer(
|
||||
text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url
|
||||
)
|
||||
return await ctx.reply(embed=embed)
|
||||
|
||||
@vc.command(name="unmute",
|
||||
help="Unmute a member in the voice channel.",
|
||||
usage="voice unmute <member>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_guild_permissions(mute_members=True)
|
||||
#@commands.bot_has_permissions(mute_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def vcunmute(self, ctx, *, member: discord.Member):
|
||||
if member.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
if member.voice.mute == False:
|
||||
embed2 = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is already unmuted in the voice channel.",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
ch = member.voice.channel.mention
|
||||
embed3 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{str(member)} has been unmuted in {ch}",
|
||||
color=self.color)
|
||||
embed3.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed3.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
await member.edit(mute=False, reason=f"Unmuted by {str(ctx.author)}")
|
||||
return await ctx.reply(embed=embed3)
|
||||
|
||||
@vc.command(name="muteall",
|
||||
help="Mute all members in a voice channel.",
|
||||
usage="voice muteall")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(mute_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _muteall(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
count = 0
|
||||
ch = ctx.author.voice.channel.mention
|
||||
for member in ctx.author.voice.channel.members:
|
||||
if member.voice.mute == False:
|
||||
await member.edit(
|
||||
mute=True,
|
||||
reason=
|
||||
f"voice muteall Command Executed by {str(ctx.author)}")
|
||||
count += 1
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
description=f"Muted {count} members in {ch}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="unmuteall",
|
||||
help="Unmute all members in a voice channel.",
|
||||
usage="voice unmuteall")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(mute_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _unmuteall(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any of the voice channel",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
count = 0
|
||||
ch = ctx.author.voice.channel.mention
|
||||
for member in ctx.author.voice.channel.members:
|
||||
if member.voice.mute == True:
|
||||
await member.edit(
|
||||
mute=False,
|
||||
reason=
|
||||
f"Voice unmuteall Command Executed by: {str(ctx.author)}")
|
||||
count += 1
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
description=f"Unmuted {count} members in {ch}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="deafen",
|
||||
help="Deafen a user in a voice channel.",
|
||||
usage="voice deafen <member>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_guild_permissions(deafen_members=True)
|
||||
#@commands.bot_has_permissions(deafen_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _deafen(self, ctx, *, member: discord.Member):
|
||||
if member.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is not connected to any of the voice channel",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
if member.voice.deaf == True:
|
||||
embed2 = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is already deafened in the voice channel",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
ch = member.voice.channel.mention
|
||||
embed3 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{str(member)} has been Deafened in {ch}",
|
||||
color=self.color)
|
||||
embed3.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed3.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
await member.edit(deafen=True, reason=f"Deafen by {str(ctx.author)}")
|
||||
return await ctx.reply(embed=embed3)
|
||||
|
||||
@vc.command(name="undeafen",
|
||||
help="Undeafen a User in a voice channel .",
|
||||
usage="voice undeafen <member>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_guild_permissions(deafen_members=True)
|
||||
#@commands.bot_has_permissions(deafen_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _undeafen(self, ctx, *, member: discord.Member):
|
||||
if member.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is not connected to any of the voice channel",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
if member.voice.deaf == False:
|
||||
embed2 = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is already undeafened in the voice channel",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
ch = member.voice.channel.mention
|
||||
embed3 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{str(member)} has been undeafened in {ch}",
|
||||
color=self.color)
|
||||
embed3.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed3.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
await member.edit(deafen=False,
|
||||
reason=f"Undeafen by {str(ctx.author)}")
|
||||
return await ctx.reply(embed=embed3)
|
||||
|
||||
@vc.command(name="deafenall",
|
||||
help="Deafen all Ussr in a voice channel.",
|
||||
usage="voice deafenall")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(deafen_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _deafenall(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any of the voice channel",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
count = 0
|
||||
ch = ctx.author.voice.channel.mention
|
||||
for member in ctx.author.voice.channel.members:
|
||||
if member.voice.deaf == False:
|
||||
await member.edit(
|
||||
deafen=True,
|
||||
reason=
|
||||
f"voice deafenall Command Executed by {str(ctx.author)}")
|
||||
count += 1
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
description=f"Deafened {count} members in {ch}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="undeafenall",
|
||||
help="undeafen all member in a voice channel .",
|
||||
usage="voice undeafenall")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(deafen_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _undeafall(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected in any of the voice channel",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
count = 0
|
||||
ch = ctx.author.voice.channel.mention
|
||||
for member in ctx.author.voice.channel.members:
|
||||
if member.voice.deaf == True:
|
||||
await member.edit(
|
||||
deafen=False,
|
||||
reason=
|
||||
f"Voice undeafenall Command Executed by: {str(ctx.author)}")
|
||||
count += 1
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"Undeafened {count} members in {ch}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="moveall",
|
||||
help="Move all members from the voice channel to the specified voice channel.",
|
||||
usage="voice moveall <voice channel>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(move_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _moveall(self, ctx, *, channel: discord.VoiceChannel):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any of the voice channel",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
try:
|
||||
ch = ctx.author.voice.channel.mention
|
||||
nch = channel.mention
|
||||
count = 0
|
||||
for member in ctx.author.voice.channel.members:
|
||||
await member.edit(
|
||||
voice_channel=channel,
|
||||
reason=
|
||||
f"voice moveall Command Executed by: {str(ctx.author)}")
|
||||
count += 1
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{count} Members moved from {ch} to {nch}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
await ctx.reply(embed=embed2)
|
||||
except:
|
||||
embed3 = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=f"Invalid Voice channel provided",
|
||||
color=self.color)
|
||||
embed3.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed3.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
await ctx.reply(embed=embed3)
|
||||
|
||||
|
||||
|
||||
@vc.command(name="pullall",
|
||||
help="Move all members of ALL voice channels to a specified voice channel.",
|
||||
usage="voice pullall <channel>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(move_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _pullall(self, ctx, *, channel: discord.VoiceChannel):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any of the voice channel",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
count = 0
|
||||
for vc in ctx.guild.voice_channels:
|
||||
for member in vc.members:
|
||||
if member != ctx.author:
|
||||
try:
|
||||
await member.edit(
|
||||
voice_channel=channel,
|
||||
reason=f"Pullall Command Executed by: {str(ctx.author)}")
|
||||
count += 1
|
||||
except:
|
||||
pass
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
description=f"Moved {count} members to {channel.mention}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
|
||||
@vc.command(name="move",
|
||||
help="Move a member from one voice channel to another.",
|
||||
usage="voice move <member> <channel>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(move_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _move(self, ctx, member: discord.Member, channel: discord.VoiceChannel):
|
||||
if member.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS}Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
if channel == member.voice.channel:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is already in {channel.mention}.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
await member.edit(voice_channel=channel,
|
||||
reason=f"Moved by {str(ctx.author)}")
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{str(member)} has been moved to {channel.mention}",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
|
||||
@vc.command(name="pull",
|
||||
help="Pull a member from one voice channel to yours.",
|
||||
usage="voice pull <member>")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
#@commands.bot_has_permissions(move_members=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _pull(self, ctx, member: discord.Member):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
if member.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
if member.voice.channel == ctx.author.voice.channel:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
f"{str(member)} is already in your voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
await member.edit(voice_channel=ctx.author.voice.channel,
|
||||
reason=f"Pulled by {str(ctx.author)}")
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{str(member)} has been pulled to your voice channel.",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="lock",
|
||||
help="Locks the voice channel so no one can join.",
|
||||
usage="voice lock")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
@commands.bot_has_permissions(manage_roles=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _lock(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
ch = ctx.author.voice.channel.mention
|
||||
await ctx.author.voice.channel.set_permissions(ctx.guild.default_role,
|
||||
connect=False,
|
||||
reason=f"Locked by {str(ctx.author)}")
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{ch} has been locked.",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="unlock",
|
||||
help="Unlocks the voice channel so anyone can join.",
|
||||
usage="voice unlock")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
@commands.bot_has_permissions(manage_roles=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _unlock(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
ch = ctx.author.voice.channel.mention
|
||||
await ctx.author.voice.channel.set_permissions(ctx.guild.default_role,
|
||||
connect=True,
|
||||
reason=f"Unlocked by {str(ctx.author)}")
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{ch} has been unlocked.",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="private",
|
||||
help="Makes the voice channel private.",
|
||||
usage="voice private")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
@commands.bot_has_permissions(manage_roles=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _private(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
ch = ctx.author.voice.channel.mention
|
||||
await ctx.author.voice.channel.set_permissions(ctx.guild.default_role,
|
||||
connect=False,
|
||||
view_channel=False,
|
||||
reason=f"Made private by {str(ctx.author)}")
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{ch} has been made private.",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
@vc.command(name="unprivate",
|
||||
help="Makes the voice channel public.",
|
||||
usage="voice unprivate")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(manage_roles=True)
|
||||
@commands.bot_has_permissions(manage_roles=True)
|
||||
@commands.cooldown(1, 10, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def _unprivate(self, ctx):
|
||||
if ctx.author.voice is None:
|
||||
embed = discord.Embed(title=f"{CROSS} Error",
|
||||
|
||||
description=
|
||||
"You are not connected to any voice channel.",
|
||||
color=self.color)
|
||||
embed.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed)
|
||||
ch = ctx.author.voice.channel.mention
|
||||
await ctx.author.voice.channel.set_permissions(ctx.guild.default_role,
|
||||
connect=True,
|
||||
view_channel=True,
|
||||
reason=f"Made public by {str(ctx.author)}")
|
||||
embed2 = discord.Embed(title=f"{TICK}> Success",
|
||||
|
||||
description=f"{ch} has been made public.",
|
||||
color=self.color)
|
||||
embed2.set_footer(text=f"Requested by: {ctx.author}",
|
||||
icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
|
||||
embed2.set_thumbnail(url="https://cdn.discordapp.com/emojis/1279464563150032991.png")
|
||||
return await ctx.reply(embed=embed2)
|
||||
|
||||
|
||||
936
bot/cogs/commands/welcome.py
Normal file
936
bot/cogs/commands/welcome.py
Normal file
@@ -0,0 +1,936 @@
|
||||
import discord
|
||||
from utils.emoji import ICONS_CHANNEL, ICONS_PLUS, TICK, ZTICK
|
||||
from discord.ext import commands
|
||||
from discord.ui import View, Select, Button
|
||||
import aiosqlite
|
||||
import asyncio
|
||||
import re
|
||||
import json
|
||||
from utils.Tools import *
|
||||
|
||||
class VariableButton(Button):
|
||||
def __init__(self, author):
|
||||
super().__init__(label="Variables", style=discord.ButtonStyle.secondary)
|
||||
self.author = author
|
||||
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
if interaction.user != self.author:
|
||||
await interaction.response.send_message("Only the command author can use this button.", ephemeral=True)
|
||||
return
|
||||
|
||||
variables = {
|
||||
"{user}": "Mentions the user (e.g., @UserName).",
|
||||
"{user_avatar}": "The user's avatar URL.",
|
||||
"{user_name}": "The user's username.",
|
||||
"{user_id}": "The user's ID number.",
|
||||
"{user_nick}": "The user's nickname in the server.",
|
||||
"{user_joindate}": "The user's join date in the server (formatted as Day, Month Day, Year).",
|
||||
"{user_createdate}": "The user's account creation date (formatted as Day, Month Day, Year).",
|
||||
"{server_name}": "The server's name.",
|
||||
"{server_id}": "The server's ID number.",
|
||||
"{server_membercount}": "The server's total member count.",
|
||||
"{server_icon}": "The server's icon URL."
|
||||
}
|
||||
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Available Placeholders",
|
||||
description="Use these placeholders in your welcome message:",
|
||||
color=discord.Color(0xFF0000)
|
||||
)
|
||||
|
||||
for var, desc in variables.items():
|
||||
embed.add_field(name=var, value=desc, inline=False)
|
||||
|
||||
embed.set_footer(text="Add placeholders directly in the welcome message or embed fields.")
|
||||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||||
|
||||
class Welcomer(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bot.loop.create_task(self._create_table())
|
||||
|
||||
async def _create_table(self):
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS welcome (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
welcome_type TEXT,
|
||||
welcome_message TEXT,
|
||||
channel_id INTEGER,
|
||||
embed_data TEXT,
|
||||
auto_delete_duration INTEGER
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
|
||||
@commands.hybrid_group(invoke_without_command=True, name="greet", help="Shows all the greet commands.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
async def greet(self, ctx: commands.Context):
|
||||
if ctx.subcommand_passed is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@greet.command(name="setup", help="Configures a welcome message for new members joining the server. ")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 6, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def greet_setup(self, ctx):
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
async with db.execute("SELECT * FROM welcome WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if row:
|
||||
error = discord.Embed(description=f"A welcome message has already been set in {ctx.guild.name}. Use `{ctx.prefix}greet reset` to reconfigure.", color=0xFF0000)
|
||||
error.set_author(name="Error", icon_url="https://cdn.discordapp.com/emojis/1294218790082711553.png")
|
||||
return await ctx.send(embed=error)
|
||||
|
||||
options_view = View(timeout=600)
|
||||
|
||||
async def option_callback(interaction: discord.Interaction, button: Button):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You cannot interact with this setup.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
|
||||
if button.custom_id == "simple":
|
||||
await interaction.message.delete()
|
||||
await self.simple_setup(ctx)
|
||||
|
||||
elif button.custom_id == "embed":
|
||||
await interaction.message.delete()
|
||||
await self.embed_setup(ctx)
|
||||
elif button.custom_id == "cancel":
|
||||
await interaction.message.delete()
|
||||
|
||||
button_simple = Button(label="Simple", style=discord.ButtonStyle.success, custom_id="simple")
|
||||
button_simple.callback = lambda interaction: option_callback(interaction, button_simple)
|
||||
options_view.add_item(button_simple)
|
||||
|
||||
button_embed = Button(label="Embed", style=discord.ButtonStyle.success, custom_id="embed")
|
||||
button_embed.callback = lambda interaction: option_callback(interaction, button_embed)
|
||||
options_view.add_item(button_embed)
|
||||
|
||||
button_cancel = Button(label="Cancel", style=discord.ButtonStyle.danger, custom_id="cancel")
|
||||
button_cancel.callback = lambda interaction: option_callback(interaction, button_cancel)
|
||||
options_view.add_item(button_cancel)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Welcome Message Setup",
|
||||
description="Choose the type of welcome message you want to create:",
|
||||
color=0xFF0000
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name=" Simple",
|
||||
value="Send a plain text welcome message. You can use placeholders to personalize it.\n\n",
|
||||
inline=False
|
||||
)
|
||||
embed.add_field(
|
||||
name=" Embed",
|
||||
value="Send a welcome message in an embed format. You can customize the embed with a title, description, image, etc.",
|
||||
inline=False
|
||||
)
|
||||
|
||||
embed.set_footer(text="Click the buttons below to choose the welcome message type.", icon_url=self.bot.user.display_avatar.url)
|
||||
|
||||
|
||||
await ctx.send(embed=embed, view=options_view)
|
||||
|
||||
async def simple_setup(self, ctx):
|
||||
setup_view = View(timeout=600)
|
||||
first = View(timeout=600)
|
||||
message_content = []
|
||||
|
||||
placeholders = {
|
||||
"user": ctx.author.mention,
|
||||
"user_avatar": ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url,
|
||||
"user_name": ctx.author.name,
|
||||
"user_id": ctx.author.id,
|
||||
"user_nick": ctx.author.display_name,
|
||||
"user_joindate": ctx.author.joined_at.strftime("%a, %b %d, %Y"),
|
||||
"user_createdate": ctx.author.created_at.strftime("%a, %b %d, %Y"),
|
||||
"server_name": ctx.guild.name,
|
||||
"server_id": ctx.guild.id,
|
||||
"server_membercount": ctx.guild.member_count,
|
||||
"server_icon": ctx.guild.icon.url if ctx.guild.icon else "https://cdn.discordapp.com/embed/avatars/0.png",
|
||||
"timestamp": discord.utils.format_dt(ctx.message.created_at)
|
||||
}
|
||||
|
||||
def safe_format(text):
|
||||
placeholders_lower = {k.lower(): v for k, v in placeholders.items()}
|
||||
|
||||
def replace_var(match):
|
||||
var_name = match.group(1).lower()
|
||||
return str(placeholders_lower.get(var_name, f"{{{var_name}}}"))
|
||||
|
||||
return re.sub(r"\{(\w+)\}", replace_var, text or "")
|
||||
|
||||
|
||||
async def update_preview(content):
|
||||
preview = safe_format(content)
|
||||
await preview_message.edit(content=f"**Preview:** {preview}", view=setup_view)
|
||||
|
||||
first.add_item(VariableButton(ctx.author))
|
||||
|
||||
preview_message = await ctx.send("__**Simple Message Setup**__ \nEnter your welcome message here:", view=first)
|
||||
|
||||
async def submit_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You cannot interact with this setup.", ephemeral=True)
|
||||
return
|
||||
if message_content:
|
||||
await self._save_welcome_data(ctx.guild.id, "simple", message_content[0])
|
||||
await interaction.response.send_message(f"{TICK}> Welcome message setup completed!")
|
||||
for item in setup_view.children:
|
||||
item.disabled = True
|
||||
await preview_message.edit(view=setup_view)
|
||||
else:
|
||||
await interaction.response.send_message("No message entered to submit.", ephemeral=True)
|
||||
|
||||
async def edit_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You cannot interact with this setup.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer()
|
||||
await ctx.send("Enter the updated welcome message:")
|
||||
try:
|
||||
msg = await self.bot.wait_for("message", timeout=600, check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
message_content.clear()
|
||||
message_content.append(msg.content)
|
||||
await update_preview(msg.content)
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.send("Editing timed out.")
|
||||
|
||||
async def cancel_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You cannot interact with this setup.", ephemeral=True)
|
||||
return
|
||||
await preview_message.delete()
|
||||
|
||||
submit_button = Button(label="Submit", style=discord.ButtonStyle.success)
|
||||
submit_button.callback = submit_callback
|
||||
setup_view.add_item(submit_button)
|
||||
|
||||
edit_button = Button(label="Edit", style=discord.ButtonStyle.primary)
|
||||
edit_button.callback = edit_callback
|
||||
setup_view.add_item(edit_button)
|
||||
setup_view.add_item(VariableButton(ctx.author))
|
||||
|
||||
cancel_button = Button(emoji=ICONS_PLUS, style=discord.ButtonStyle.secondary)
|
||||
cancel_button.callback = cancel_callback
|
||||
setup_view.add_item(cancel_button)
|
||||
|
||||
try:
|
||||
msg = await self.bot.wait_for("message", timeout=600, check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
message_content.append(msg.content)
|
||||
await update_preview(msg.content)
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.send("Setup timed out.")
|
||||
|
||||
|
||||
async def _save_welcome_data(self, guild_id, welcome_type, message, embed_data=None):
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
await db.execute("""
|
||||
INSERT OR REPLACE INTO welcome (guild_id, welcome_type, welcome_message, embed_data)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""", (guild_id, welcome_type, message, json.dumps(embed_data) if embed_data else None))
|
||||
await db.commit()
|
||||
|
||||
|
||||
|
||||
|
||||
async def embed_setup(self, ctx):
|
||||
setup_view = View(timeout=600)
|
||||
embed_data = {
|
||||
"message": None,
|
||||
"title": None,
|
||||
"description": None,
|
||||
"color": None,
|
||||
"footer_text": None,
|
||||
"footer_icon": None,
|
||||
"author_name": None,
|
||||
"author_icon": None,
|
||||
"thumbnail": None,
|
||||
"image": None,
|
||||
}
|
||||
|
||||
placeholders = {
|
||||
"user": ctx.author.mention,
|
||||
"user_avatar": ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url,
|
||||
"user_name": ctx.author.name,
|
||||
"user_id": ctx.author.id,
|
||||
"user_nick": ctx.author.display_name,
|
||||
"user_joindate": ctx.author.joined_at.strftime("%a, %b %d, %Y"),
|
||||
"user_createdate": ctx.author.created_at.strftime("%a, %b %d, %Y"),
|
||||
"server_name": ctx.guild.name,
|
||||
"server_id": ctx.guild.id,
|
||||
"server_membercount": ctx.guild.member_count,
|
||||
"server_icon": ctx.guild.icon.url if ctx.guild.icon else "https://cdn.discordapp.com/embed/avatars/0.png",
|
||||
"timestamp": discord.utils.format_dt(ctx.message.created_at)
|
||||
}
|
||||
|
||||
def safe_format(text):
|
||||
placeholders_lower = {k.lower(): v for k, v in placeholders.items()}
|
||||
|
||||
def replace_var(match):
|
||||
var_name = match.group(1).lower()
|
||||
return str(placeholders_lower.get(var_name, f"{{{var_name}}}"))
|
||||
|
||||
return re.sub(r"\{(\w+)\}", replace_var, text or "")
|
||||
|
||||
|
||||
async def update_preview():
|
||||
content = safe_format(embed_data["message"]) or "Message Content."
|
||||
embed = discord.Embed(
|
||||
title=safe_format(embed_data["title"]) or "",
|
||||
description=safe_format(embed_data["description"]) or "```Customize your welcome embed, take help of variables.```",
|
||||
color=discord.Color(embed_data["color"]) if embed_data["color"] else discord.Color(0x2f3136)
|
||||
)
|
||||
|
||||
|
||||
if embed_data["footer_text"]:
|
||||
embed.set_footer(text=safe_format(embed_data["footer_text"]), icon_url=safe_format(embed_data["footer_icon"]) or None)
|
||||
if embed_data["author_name"]:
|
||||
embed.set_author(name=safe_format(embed_data["author_name"]), icon_url=safe_format(embed_data["author_icon"]) or None)
|
||||
if embed_data["thumbnail"]:
|
||||
embed.set_thumbnail(url=safe_format(embed_data["thumbnail"]))
|
||||
if embed_data["image"]:
|
||||
embed.set_image(url=safe_format(embed_data["image"]))
|
||||
|
||||
await preview_message.edit(content="**Embed Preview:** " + content, embed=embed, view=setup_view)
|
||||
|
||||
preview_message = await ctx.send("Configuring embed welcome message...")
|
||||
|
||||
async def handle_selection(interaction: discord.Interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You cannot interact with this setup.", ephemeral=True)
|
||||
return
|
||||
|
||||
selected_option = select_menu.values[0]
|
||||
await interaction.response.defer()
|
||||
|
||||
try:
|
||||
if selected_option == "message":
|
||||
await ctx.send("Enter the welcome message content:")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
embed_data["message"] = msg.content
|
||||
|
||||
elif selected_option == "title":
|
||||
await ctx.send("Enter the embed title:")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
embed_data["title"] = msg.content
|
||||
|
||||
elif selected_option == "description":
|
||||
await ctx.send("Enter the embed description:")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
embed_data["description"] = msg.content
|
||||
|
||||
elif selected_option == "color":
|
||||
await ctx.send("Enter a hex color (e.g., #3498db or 3498db):")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
color_code = msg.content.lstrip("#")
|
||||
if all(c in "0123456789abcdefABCDEF" for c in color_code) and len(color_code) in {3, 6}:
|
||||
embed_data["color"] = int(color_code.lstrip("#"), 16)
|
||||
else:
|
||||
await ctx.send("Invalid color code. Please enter a valid hex color.")
|
||||
|
||||
elif selected_option in ["footer_text", "footer_icon", "author_name", "author_icon", "thumbnail", "image"]:
|
||||
await ctx.send(f"Enter the URL or text for {selected_option.replace('_', ' ')}:")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
url_or_text = msg.content
|
||||
if selected_option in ["footer_icon", "author_icon", "thumbnail", "image"]:
|
||||
if url_or_text.startswith("http") or url_or_text in ["{user_avatar}", "{server_icon}"]:
|
||||
embed_data[selected_option] = url_or_text
|
||||
else:
|
||||
await ctx.send("Invalid URL. Please enter a valid image URL or a supported placeholder ({user_avatar} or {server_icon}).")
|
||||
else:
|
||||
embed_data[selected_option] = url_or_text
|
||||
|
||||
await update_preview()
|
||||
await interaction.followup.send(f"{selected_option.capitalize()} updated.", ephemeral=True)
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.send("You took too long to respond. Please try again.")
|
||||
except Exception as e:
|
||||
await ctx.send(f"An error occurred: {e}")
|
||||
|
||||
select_menu = Select(
|
||||
placeholder="Choose an option to edit the Embed",
|
||||
options=[
|
||||
discord.SelectOption(label="Message Content", value="message"),
|
||||
discord.SelectOption(label="Title", value="title"),
|
||||
discord.SelectOption(label="Description", value="description"),
|
||||
discord.SelectOption(label="Color", value="color"),
|
||||
discord.SelectOption(label="Footer Text", value="footer_text"),
|
||||
discord.SelectOption(label="Footer Icon", value="footer_icon"),
|
||||
discord.SelectOption(label="Author Name", value="author_name"),
|
||||
discord.SelectOption(label="Author Icon", value="author_icon"),
|
||||
discord.SelectOption(label="Thumbnail", value="thumbnail"),
|
||||
discord.SelectOption(label="Image", value="image")
|
||||
]
|
||||
)
|
||||
select_menu.callback = handle_selection
|
||||
setup_view.add_item(select_menu)
|
||||
|
||||
async def submit_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You cannot interact with this setup.", ephemeral=True)
|
||||
return
|
||||
if not any(embed_data[key] for key in ["title", "description"]):
|
||||
await interaction.response.send_message("Please provide at least a title or an description before submitting.", ephemeral=True)
|
||||
return
|
||||
|
||||
await self._save_welcome_data(ctx.guild.id, "embed", embed_data["message"] or "", embed_data)
|
||||
await interaction.response.send_message(f"{TICK}> Embed welcome message setup completed!")
|
||||
|
||||
for item in setup_view.children:
|
||||
item.disabled = True
|
||||
await preview_message.edit(view=setup_view)
|
||||
|
||||
submit_button = Button(label="Submit", style=discord.ButtonStyle.success)
|
||||
submit_button.callback = submit_callback
|
||||
setup_view.add_item(submit_button)
|
||||
setup_view.add_item(VariableButton(ctx.author))
|
||||
|
||||
async def cancel_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You cannot interact with this setup.", ephemeral=True)
|
||||
return
|
||||
await preview_message.delete()
|
||||
await interaction.response.send_message("Embed setup cancelled.", ephemeral=True)
|
||||
|
||||
cancel_button = Button(label="Cancel", style=discord.ButtonStyle.danger)
|
||||
cancel_button.callback = cancel_callback
|
||||
setup_view.add_item(cancel_button)
|
||||
|
||||
await update_preview()
|
||||
|
||||
|
||||
|
||||
@greet.command(name="reset", aliases=["disable"], help="Resets and deletes the current welcome configuration for the server.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 6, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def greet_reset(self, ctx):
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
cursor = await db.execute("SELECT 1 FROM welcome WHERE guild_id = ?", (ctx.guild.id,))
|
||||
is_set_up = await cursor.fetchone()
|
||||
|
||||
if not is_set_up:
|
||||
error = discord.Embed(description=f"No welcome message has been set for {ctx.guild.name}! Please set a welcome message first using `{ctx.prefix}greet setup`", color=0xFF0000)
|
||||
error.set_author(name="Greet is not configured!", icon_url="https://cdn.discordapp.com/emojis/1294218790082711553.png")
|
||||
return await ctx.send(embed=error)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Are you sure?",
|
||||
description="This will remove all welcome configurations & data related to welcome messages for this server!",
|
||||
color=0xFF0000
|
||||
)
|
||||
|
||||
yes_button = Button(label="Confirm", style=discord.ButtonStyle.danger)
|
||||
no_button = Button(label="Cancel", style=discord.ButtonStyle.secondary)
|
||||
|
||||
async def yes_button_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("Only the command author can confirm this action.", ephemeral=True)
|
||||
return
|
||||
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
await db.execute("DELETE FROM welcome WHERE guild_id = ?", (ctx.guild.id,))
|
||||
await db.commit()
|
||||
|
||||
embed.color = discord.Color(0xFF0000)
|
||||
embed.title = f"{TICK}> Success"
|
||||
embed.description = "Welcome message configuration has been successfully reset."
|
||||
await interaction.message.edit(embed=embed, view=None)
|
||||
|
||||
async def no_button_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("Only the command author can cancel this action.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed.color = discord.Color(0xFF0000)
|
||||
embed.title = "Cancelled"
|
||||
embed.description = "Greet Reset operation has been cancelled."
|
||||
await interaction.message.edit(embed=embed, view=None)
|
||||
|
||||
yes_button.callback = yes_button_callback
|
||||
no_button.callback = no_button_callback
|
||||
|
||||
view = View()
|
||||
view.add_item(yes_button)
|
||||
view.add_item(no_button)
|
||||
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
|
||||
@greet.command(name="channel", help="Sets the channel where welcome messages will be sent.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 6, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def greet_channel(self, ctx):
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
async with db.execute("SELECT welcome_type, channel_id FROM welcome WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
result = await cursor.fetchone()
|
||||
welcome_message = result[0] if result else None
|
||||
welcome_channel = ctx.guild.get_channel(result[1]) if result and result[1] else None
|
||||
|
||||
if not welcome_message:
|
||||
error = discord.Embed(description=f"No welcome message has been set for {ctx.guild.name}! Please set a welcome message first using `{ctx.prefix}greet setup`", color=0xFF0000)
|
||||
error.set_author(name="Greet is not configured!", icon_url="https://cdn.discordapp.com/emojis/1294218790082711553.png")
|
||||
await ctx.send(embed=error)
|
||||
return
|
||||
|
||||
channels = ctx.guild.text_channels
|
||||
chunk_size = 25
|
||||
chunks = [channels[i:i + chunk_size] for i in range(0, len(channels), chunk_size)]
|
||||
current_page = 0
|
||||
|
||||
def generate_view(page):
|
||||
select_menu = Select(
|
||||
placeholder="Select a channel for welcome messages",
|
||||
options=[
|
||||
discord.SelectOption(label=channel.name, emoji=ICONS_CHANNEL, value=str(channel.id))
|
||||
for channel in chunks[page]
|
||||
]
|
||||
)
|
||||
|
||||
async def select_callback(interaction: discord.Interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not authorized to set the welcome channel.", ephemeral=True)
|
||||
return
|
||||
|
||||
selected_channel_id = int(select_menu.values[0])
|
||||
selected_channel = ctx.guild.get_channel(selected_channel_id)
|
||||
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
await db.execute("UPDATE welcome SET channel_id = ? WHERE guild_id = ?", (selected_channel_id, ctx.guild.id))
|
||||
await db.commit()
|
||||
|
||||
embed.description = f"Current Welcome Channel: {selected_channel.mention}"
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
await ctx.send(f"{TICK}> Welcome channel has been set to {selected_channel.mention}")
|
||||
|
||||
select_menu.callback = select_callback
|
||||
|
||||
next_button = Button(label="Next List of Channels", style=discord.ButtonStyle.secondary, disabled=page >= len(chunks) - 1)
|
||||
previous_button = Button(label="Previous", style=discord.ButtonStyle.secondary, disabled=page <= 0)
|
||||
|
||||
async def next_callback(interaction: discord.Interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not authorized to navigate these menus.", ephemeral=True)
|
||||
return
|
||||
nonlocal current_page
|
||||
current_page += 1
|
||||
await interaction.response.edit_message(embed=embed, view=generate_view(current_page))
|
||||
|
||||
async def previous_callback(interaction: discord.Interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not authorized to navigate these menus.", ephemeral=True)
|
||||
return
|
||||
nonlocal current_page
|
||||
current_page -= 1
|
||||
await interaction.response.edit_message(embed=embed, view=generate_view(current_page))
|
||||
|
||||
next_button.callback = next_callback
|
||||
previous_button.callback = previous_callback
|
||||
|
||||
view = View()
|
||||
view.add_item(select_menu)
|
||||
view.add_item(previous_button)
|
||||
view.add_item(next_button)
|
||||
return view
|
||||
|
||||
embed = discord.Embed(
|
||||
title=f"Welcome Channel for {ctx.guild.name}",
|
||||
description=f"Current Welcome Channel: {welcome_channel.mention if welcome_channel else 'None'}",
|
||||
color=0xFF0000
|
||||
)
|
||||
embed.set_footer(text="Use the dropdown menu to select a channel. Navigate pages if needed.")
|
||||
|
||||
await ctx.send(embed=embed, view=generate_view(current_page))
|
||||
|
||||
|
||||
|
||||
@greet.command(name="test", help="Sends a test welcome message to preview the setup.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 6, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def greet_test(self, ctx):
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
async with db.execute("SELECT welcome_type, welcome_message, channel_id, embed_data FROM welcome WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if row is None:
|
||||
error = discord.Embed(description=f"No welcome message has been set for {ctx.guild.name}! Please set a welcome message first using `{ctx.prefix}greet setup`", color=0xFF0000)
|
||||
error.set_author(name="Greet is not configured!", icon_url="https://cdn.discordapp.com/emojis/1294218790082711553.png")
|
||||
await ctx.send(embed=error)
|
||||
return
|
||||
|
||||
welcome_type, welcome_message, channel_id, embed_data = row
|
||||
welcome_channel = self.bot.get_channel(channel_id)
|
||||
|
||||
if not welcome_channel:
|
||||
error2 = discord.Embed(description=f"Welcome channel not set or invalid. Use `{ctx.prefix}greet channel` to set one.", color=0xFF0000)
|
||||
error2.set_author(name="Channel not set", icon_url="https://cdn.discordapp.com/emojis/1294218790082711553.png")
|
||||
await ctx.send(embed=error2)
|
||||
return
|
||||
|
||||
placeholders = {
|
||||
"user": ctx.author.mention,
|
||||
"user_avatar": ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url,
|
||||
"user_name": ctx.author.name,
|
||||
"user_id": ctx.author.id,
|
||||
"user_nick": ctx.author.display_name,
|
||||
"user_joindate": ctx.author.joined_at.strftime("%a, %b %d, %Y"),
|
||||
"user_createdate": ctx.author.created_at.strftime("%a, %b %d, %Y"),
|
||||
"server_name": ctx.guild.name,
|
||||
"server_id": ctx.guild.id,
|
||||
"server_membercount": ctx.guild.member_count,
|
||||
"server_icon": ctx.guild.icon.url if ctx.guild.icon else "https://cdn.discordapp.com/embed/avatars/0.png",
|
||||
"timestamp": discord.utils.format_dt(ctx.message.created_at)
|
||||
}
|
||||
|
||||
def safe_format(text):
|
||||
placeholders_lower = {k.lower(): v for k, v in placeholders.items()}
|
||||
|
||||
def replace_var(match):
|
||||
var_name = match.group(1).lower()
|
||||
return str(placeholders_lower.get(var_name, f"{{{var_name}}}"))
|
||||
|
||||
return re.sub(r"\{(\w+)\}", replace_var, text or "")
|
||||
|
||||
|
||||
if welcome_type == "simple" and welcome_message:
|
||||
await welcome_channel.send(safe_format(welcome_message))
|
||||
|
||||
elif welcome_type == "embed" and embed_data:
|
||||
try:
|
||||
embed_info = json.loads(embed_data)
|
||||
color_value = embed_info.get("color", None)
|
||||
|
||||
|
||||
embed_color = 0x2f3136
|
||||
|
||||
|
||||
if color_value and isinstance(color_value, str) and color_value.startswith("#"):
|
||||
embed_color = discord.Color(int(color_value.lstrip("#"), 16))
|
||||
elif isinstance(color_value, int):
|
||||
embed_color = discord.Color(color_value)
|
||||
|
||||
except (ValueError, SyntaxError, json.JSONDecodeError):
|
||||
await ctx.send("Invalid embed data format. Please reconfigure.")
|
||||
return
|
||||
|
||||
content = safe_format(embed_info.get("message", "")) or None
|
||||
embed = discord.Embed(
|
||||
title=safe_format(embed_info.get("title", "")),
|
||||
description=safe_format(embed_info.get("description", "")),
|
||||
color=embed_color
|
||||
)
|
||||
embed.timestamp = discord.utils.utcnow()
|
||||
|
||||
|
||||
if embed_info.get("footer_text"):
|
||||
embed.set_footer(
|
||||
text=safe_format(embed_info["footer_text"]),
|
||||
icon_url=safe_format(embed_info.get("footer_icon", ""))
|
||||
)
|
||||
if embed_info.get("author_name"):
|
||||
embed.set_author(
|
||||
name=safe_format(embed_info["author_name"]),
|
||||
icon_url=safe_format(embed_info.get("author_icon", ""))
|
||||
)
|
||||
if embed_info.get("thumbnail"):
|
||||
embed.set_thumbnail(url=safe_format(embed_info["thumbnail"]))
|
||||
if embed_info.get("image"):
|
||||
embed.set_image(url=safe_format(embed_info["image"]))
|
||||
|
||||
await welcome_channel.send(content=content, embed=embed)
|
||||
|
||||
|
||||
|
||||
@greet.command(name="config", help="Shows the current welcome configuration.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 6, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def greet_config(self, ctx):
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
async with db.execute("SELECT * FROM welcome WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if row:
|
||||
_, welcome_type, welcome_message, channel_id, embed_data, auto_delete_duration = row
|
||||
response_type = "Simple" if welcome_type == "simple" else "Embed"
|
||||
|
||||
embed = discord.Embed(
|
||||
title=f"Greet Configuration for {ctx.guild.name}",
|
||||
color=0xFF0000
|
||||
)
|
||||
|
||||
embed.add_field(name="Response Type", value=response_type, inline=False)
|
||||
|
||||
if welcome_type == "simple":
|
||||
details = f"Message Content: {welcome_message or 'None'}"
|
||||
embed.add_field(name="Details", value=details[:1024], inline=False)
|
||||
else:
|
||||
embed_details = json.loads(embed_data) if embed_data else {}
|
||||
formatted_embed_data = "\n".join(
|
||||
f"{key.replace('_', ' ').title()}: {value or 'None'}" for key, value in embed_details.items()
|
||||
) or "None"
|
||||
|
||||
for i, chunk in enumerate([formatted_embed_data[i:i+1024] for i in range(0, len(formatted_embed_data), 1024)]):
|
||||
embed.add_field(name=f"Embed Data Part {i+1}", value=chunk, inline=False)
|
||||
|
||||
greet_channel = self.bot.get_channel(channel_id)
|
||||
channel_display = greet_channel.mention if greet_channel else "None"
|
||||
auto_delete_duration = f"{auto_delete_duration} seconds" if auto_delete_duration else "None"
|
||||
|
||||
embed.add_field(name="Greet Channel", value=channel_display, inline=False)
|
||||
embed.add_field(name="Auto Delete Duration", value=auto_delete_duration, inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
error = discord.Embed(
|
||||
description=f"No welcome message has been set for {ctx.guild.name}! Please set a welcome message first using `{ctx.prefix}greet setup`",
|
||||
color=0xFF0000
|
||||
)
|
||||
error.set_author(name="Greet is not configured!", icon_url="https://cdn.discordapp.com/emojis/1294218790082711553.png")
|
||||
await ctx.send(embed=error)
|
||||
|
||||
|
||||
@greet.command(name="autodelete", aliases=["autodel"], help="Sets the auto-delete duration for the welcome message.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 6, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def greet_autodelete(self, ctx, time: str):
|
||||
|
||||
if time.endswith("s"):
|
||||
seconds = int(time[:-1])
|
||||
if 3 <= seconds <= 300:
|
||||
auto_delete_duration = seconds
|
||||
else:
|
||||
await ctx.send("Auto delete time should be between 3 seconds and 300 seconds.")
|
||||
return
|
||||
elif time.endswith("m"):
|
||||
minutes = int(time[:-1])
|
||||
if 1 <= minutes <= 5:
|
||||
auto_delete_duration = minutes * 60
|
||||
else:
|
||||
await ctx.send("Auto delete time should be between 1 minute and 5 minutes.")
|
||||
return
|
||||
else:
|
||||
await ctx.send("Invalid time format. Please use 's' for seconds and 'm' for minutes.")
|
||||
return
|
||||
|
||||
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
await db.execute("""
|
||||
UPDATE welcome
|
||||
SET auto_delete_duration = ?
|
||||
WHERE guild_id = ?
|
||||
""", (auto_delete_duration, ctx.guild.id))
|
||||
await db.commit()
|
||||
|
||||
await ctx.send(f"{ZTICK} Auto delete duration has been set to **{auto_delete_duration}** seconds.")
|
||||
|
||||
|
||||
|
||||
@greet.command(name="edit", help="Edits the current welcome message settings for the server.")
|
||||
@blacklist_check()
|
||||
@ignore_check()
|
||||
@commands.has_permissions(administrator=True)
|
||||
@commands.cooldown(1, 6, commands.BucketType.user)
|
||||
@commands.max_concurrency(1, per=commands.BucketType.default, wait=False)
|
||||
async def greet_edit(self, ctx):
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
async with db.execute("SELECT welcome_type, welcome_message, embed_data FROM welcome WHERE guild_id = ?", (ctx.guild.id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
|
||||
if row is None:
|
||||
error = discord.Embed(description=f"No welcome message has been set for {ctx.guild.name}! Please set a welcome message first using `{ctx.prefix}greet setup`", color=0xFF0000)
|
||||
error.set_author(name="Greet is not configured!", icon_url="https://cdn.discordapp.com/emojis/1294218790082711553.png")
|
||||
await ctx.send(embed=error)
|
||||
return
|
||||
|
||||
welcome_type, welcome_message, embed_data = row
|
||||
|
||||
cancel_flag = False
|
||||
|
||||
if welcome_type == "simple":
|
||||
embed = discord.Embed(
|
||||
title="Edit Welcome Message",
|
||||
description=f"**Response Type:** Simple\n**Message Content:** {welcome_message or 'None'}",
|
||||
color=0xFF0000
|
||||
)
|
||||
edit_button = Button(label="Edit", style=discord.ButtonStyle.primary)
|
||||
cancel_button = Button(label="Cancel", style=discord.ButtonStyle.danger)
|
||||
|
||||
async def cancel_button_callback(interaction):
|
||||
nonlocal cancel_flag
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not authorized to cancel the setup.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.send_message("Setup has been canceled.", ephemeral=True)
|
||||
cancel_flag = True
|
||||
view.clear_items()
|
||||
await interaction.message.edit(embed=embed, view=view)
|
||||
|
||||
cancel_button.callback = cancel_button_callback
|
||||
|
||||
async def edit_button_callback(interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not authorized to edit the welcome message.", ephemeral=True)
|
||||
return
|
||||
|
||||
await interaction.response.send_message("Please provide the new welcome message:", ephemeral=True)
|
||||
try:
|
||||
new_message = await self.bot.wait_for(
|
||||
"message",
|
||||
check=lambda m: m.author == ctx.author and m.channel == ctx.channel,
|
||||
timeout=600
|
||||
)
|
||||
if cancel_flag:
|
||||
await ctx.send("Setup was canceled. No changes were made.")
|
||||
return
|
||||
await new_message.delete()
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
await db.execute("UPDATE welcome SET welcome_message = ? WHERE guild_id = ?", (new_message.content, ctx.guild.id))
|
||||
await db.commit()
|
||||
|
||||
embed.description = f"**Response Type:** Simple\n**Message Content:** {new_message.content}"
|
||||
edit_button.disabled = True
|
||||
cancel_button.disabled = True
|
||||
await interaction.message.edit(embed=embed, view=view)
|
||||
await ctx.send("Welcome message has been successfully updated.")
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.send("You took too long to respond.")
|
||||
except Exception as e:
|
||||
await ctx.send(f"An error occurred: {e}")
|
||||
|
||||
edit_button.callback = edit_button_callback
|
||||
view = View()
|
||||
view.add_item(edit_button)
|
||||
view.add_item(VariableButton(ctx.author))
|
||||
view.add_item(cancel_button)
|
||||
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
elif welcome_type == "embed":
|
||||
embed_data_json = json.loads(embed_data) if embed_data else {}
|
||||
formatted_embed_data = "\n".join(
|
||||
f"{key.replace('_', ' ').title()}: {value or 'None'}" for key, value in embed_data_json.items()
|
||||
) or "None"
|
||||
embed = discord.Embed(
|
||||
title="Edit Welcome Message",
|
||||
description=f"**Response Type:** Embed\n**Embed Data:**\n```{formatted_embed_data}```",
|
||||
color=0xFF0000
|
||||
)
|
||||
|
||||
select_menu = Select(
|
||||
placeholder="Select an embed field to edit",
|
||||
options=[
|
||||
discord.SelectOption(label=field.replace('_', ' ').title(), value=field)
|
||||
for field in embed_data_json.keys()
|
||||
]
|
||||
)
|
||||
|
||||
cancel_button = Button(label="Cancel", style=discord.ButtonStyle.danger)
|
||||
|
||||
async def cancel_button_callback(interaction):
|
||||
nonlocal cancel_flag
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not authorized to cancel the setup.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.send_message("Setup has been canceled.", ephemeral=True)
|
||||
cancel_flag = True
|
||||
view.clear_items()
|
||||
await interaction.message.edit(embed=embed, view=view)
|
||||
|
||||
cancel_button.callback = cancel_button_callback
|
||||
|
||||
async def select_callback(interaction):
|
||||
nonlocal cancel_flag
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You are not authorized to edit this embed.", ephemeral=True)
|
||||
return
|
||||
|
||||
selected_option = select_menu.values[0]
|
||||
await interaction.response.defer()
|
||||
|
||||
while not cancel_flag:
|
||||
try:
|
||||
if selected_option == "message":
|
||||
await ctx.send("Enter the welcome message content:")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
embed_data_json["message"] = msg.content
|
||||
|
||||
elif selected_option == "title":
|
||||
await ctx.send("Enter the embed title:")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
embed_data_json["title"] = msg.content
|
||||
|
||||
elif selected_option == "description":
|
||||
await ctx.send("Enter the embed description:")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
embed_data_json["description"] = msg.content
|
||||
|
||||
elif selected_option == "color":
|
||||
await ctx.send("Enter a hex color (e.g., #3498db or 3498db):")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
color_code = msg.content.lstrip("#")
|
||||
if all(c in "0123456789abcdefABCDEF" for c in color_code) and len(color_code) in {3, 6}:
|
||||
embed_data_json["color"] = int(color_code.lstrip("#"), 16)
|
||||
else:
|
||||
await ctx.send("Invalid color code. Please enter a valid hex color.")
|
||||
continue
|
||||
|
||||
elif selected_option in ["footer_text", "footer_icon", "author_name", "author_icon", "thumbnail", "image"]:
|
||||
await ctx.send(f"Enter the URL or text for {selected_option.replace('_', ' ')}:")
|
||||
msg = await self.bot.wait_for("message", check=lambda m: m.author == ctx.author and m.channel == ctx.channel)
|
||||
url_or_text = msg.content
|
||||
if selected_option in ["footer_icon", "author_icon", "thumbnail", "image"]:
|
||||
if url_or_text.startswith("http") or url_or_text in ["{user_avatar}", "{server_icon}"]:
|
||||
embed_data_json[selected_option] = url_or_text
|
||||
else:
|
||||
await ctx.send("Invalid URL. Please enter a valid image URL or a supported placeholder ({user_avatar} or {server_icon}).")
|
||||
continue
|
||||
else:
|
||||
embed_data_json[selected_option] = url_or_text
|
||||
|
||||
async with aiosqlite.connect("db/welcome.db") as db:
|
||||
await db.execute("UPDATE welcome SET embed_data = ? WHERE guild_id = ?", (json.dumps(embed_data_json), ctx.guild.id))
|
||||
await db.commit()
|
||||
|
||||
embed.description = f"**Response Type:** Embed\n**Embed Data:**\n```{json.dumps(embed_data_json, indent=4)}```"
|
||||
await interaction.message.edit(embed=embed, view=None)
|
||||
await ctx.send("Embed data has been successfully updated.")
|
||||
break
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.send("You took too long to respond.")
|
||||
break
|
||||
except Exception as e:
|
||||
await ctx.send(f"An error occurred: {e}")
|
||||
break
|
||||
|
||||
select_menu.callback = select_callback
|
||||
view = View()
|
||||
view.add_item(select_menu)
|
||||
view.add_item(VariableButton(ctx.author))
|
||||
view.add_item(cancel_button)
|
||||
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
|
||||
21
bot/cogs/commands/youtube.py
Normal file
21
bot/cogs/commands/youtube.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import re
|
||||
|
||||
class Youtube(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
|
||||
@commands.command(name='yt', aliases=['youtube'])
|
||||
async def search_youtube(self, ctx, *, search_query):
|
||||
query_string = urllib.parse.urlencode({'search_query': search_query})
|
||||
html_content = urllib.request.urlopen('http://www.youtube.com/results?' + query_string)
|
||||
search_results = re.findall(r"watch\?v=(\S{11})", html_content.read().decode())
|
||||
if len(search_results) > 0:
|
||||
result_message = f"**▶ | Here are your search results:- https://www.youtube.com/watch?v={search_results[0]} **"
|
||||
await ctx.send(result_message)
|
||||
else:
|
||||
await ctx.send('No search results found.')
|
||||
Reference in New Issue
Block a user