Update configuration and README for improved setup and security; disable default API and emoji sync, and enhance Discord permission checks in API routes.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
208
bot/cogs/commands/counting.py
Normal file
208
bot/cogs/commands/counting.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# ╔══════════════════════════════════════════════════════════════════╗
|
||||
# ║ ║
|
||||
# ║ ░█▀▀░█▀█░█▀▄░█▀▀░█░█ ░█▀▄░█▀▀░█░█░█▀▀ ║
|
||||
# ║ ░█░░░█░█░█░█░█▀▀░▄▀▄ ░█░█░█▀▀░▀▄▀░▀▀█ ║
|
||||
# ║ ░▀▀▀░▀▀▀░▀▀░░▀▀▀░▀░▀ ░▀▀░░▀▀▀░░▀░░▀▀▀ ║
|
||||
# ║ ║
|
||||
# ║ © 2026 CodeX Devs — All Rights Reserved ║
|
||||
# ║ ║
|
||||
# ║ discord ── https://discord.gg/codexdev ║
|
||||
# ║ youtube ── https://youtube.com/@CodeXDevs ║
|
||||
# ║ github ── https://github.com/RayExo ║
|
||||
# ║ ║
|
||||
# ╚══════════════════════════════════════════════════════════════════╝
|
||||
|
||||
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))
|
||||
Reference in New Issue
Block a user