Compare commits

..

34 commits

Author SHA1 Message Date
029ed943c8 Update README.md 2026-06-08 10:45:11 +00:00
chersbobers
e69064eaaa Sad 2026-05-08 18:17:29 +12:00
chersbobers
2ff854476c Updated readme for gitpush test and now that im on sr.ht 2026-04-18 22:26:45 +12:00
chersbobers
44067f29e9 Update LICENSE 2026-03-03 04:33:14 +01:00
chersbobers
8c94c8140c Merge pull request 'Adding a license' (#4) from nightly into stable
Reviewed-on: https://codeberg.org/chersbobers/booly/pulls/4
2026-03-02 09:25:40 +01:00
Charlie
26994f4377 Adding a license 2026-03-02 21:20:50 +13:00
chersbobers
18d3676e72 Merge pull request 'More pull requsers' (#3) from nightly into stable
Reviewed-on: https://codeberg.org/chersbobers/booly/pulls/3
2026-03-02 08:21:54 +01:00
Charlie
d0287e2b79 More pull requsers 2026-03-02 20:21:19 +13:00
chersbobers
f691ca5817 Merge pull request 'Fix' (#2) from nightly into stable
Reviewed-on: https://codeberg.org/chersbobers/booly/pulls/2
2026-03-02 08:16:29 +01:00
Charlie
292bde7491 Fix 2026-03-02 20:15:55 +13:00
chersbobers
282561daae Merge pull request 'nightly - stable' (#1) from nightly into stable
Reviewed-on: https://codeberg.org/chersbobers/booly/pulls/1
2026-03-02 08:14:59 +01:00
Charlie
da70f61de7 Update 2026-03-02 20:13:57 +13:00
Charlie
51e5844cee Big mirror update. 2026-03-02 20:12:20 +13:00
Charlie
8ae5c62493 Mirroring to codeberg test 2026-02-07 22:19:15 +13:00
chersbobers
6c378578b8
Merge pull request #6 from chersbobers/nightly
Minor Update
2026-02-07 22:13:53 +13:00
Charlie
9ccdfbf57b Minor Update 2026-02-07 22:12:01 +13:00
chersbobers
59fb76ff83
Merge pull request #5 from chersbobers/nightly
Huge Update | Switched too Digital Ocean
2026-02-07 18:54:47 +13:00
Charlie
bf68c8bcad Huge Update | Switched too Digital Ocean
Signed-off-by: Charlie <chazington7@gmail.com>
2026-02-07 18:53:23 +13:00
chersbobers
755f0f0484
Merge pull request #4 from chersbobers/nightly
URGENT README UPDATES
2026-02-06 16:29:47 +13:00
Charlie
04de7c3d57 Server updates - Broken servers
Signed-off-by: Charlie <chazington7@gmail.com>
2026-02-06 16:28:56 +13:00
Charlie
53075167ae Fixed outdated info before the release of nightly branch. 2026-02-06 16:18:37 +13:00
chersbobers
b75a2d765c
New stable update from 1.0.0 to 1.1.0
New stable update. from 1.0.0 to 1.1.0
2026-02-01 21:40:05 +13:00
Charlie
a2dcda769c Minor reformat 2026-02-01 21:37:08 +13:00
Charlie
44798612f5 Added COPY config.toml . 2026-02-01 21:20:36 +13:00
Charlie
64b1d812cd Add test to see if the config is loading at all in the right dir 2026-02-01 21:15:04 +13:00
Charlie
9438b63c40 Im adding a config file this is part of the new cogly or what ever I call it 2026-02-01 21:10:56 +13:00
Charlie
e70d126a95 Update with pick your own shortcode 2026-01-31 19:21:50 +13:00
Charlie
7578a5be6d Bugfixs 2026-01-31 18:04:52 +13:00
chersbobers
0f369873ac added url shortner to u.chers.moe may change url to u.booly.me when i get it 2026-01-31 18:03:16 +13:00
chersbobers
603dedf344
Merge pull request #1 from chersbobers/nightly
Readme update with new bot link.
2026-01-30 07:23:34 +13:00
Charlie
dde2628570 Readme update with new bot link. 2026-01-30 07:19:52 +13:00
Charlie
8793fc93f3 Readme update moving to nightly branch 2026-01-29 23:15:48 +13:00
Charlie
a4255335df Bugfix in render.yaml 2026-01-28 16:37:28 +13:00
Charlie
cd3621ec88 Hotfix | Removed emojis 2026-01-28 16:30:24 +13:00
13 changed files with 427 additions and 221 deletions

9
.gitignore vendored
View file

@ -1,13 +1,4 @@
# Data files
data.json
timeouts.json
# Environment files
.env
# Luvit files
deps/
lit-*
# Logs
*.log

View file

@ -7,5 +7,6 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY bot.py .
COPY cogs/ ./cogs/
COPY config.toml .
CMD ["python", "bot.py"]

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 chersbobers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,8 +1,4 @@
# booly
Revamped tooly designed for ease of use.
two use modes:
1. premade *servers may lag
2. host your self it comes with the whole bot all you need is a token and a host
you can fork expand pull request host just but this in the readme or site ![Powered by Booly](https://img.shields.io/github/v/release/chersbobers/booly?label=powered%20by%20booly&color=5865F2&logo=discord&logoColor=white)
# Booly is changing maintainers!
or i might come back
----------------------------------------------------------------------------------------------------
[use at your own risk](oldreadme.md)

118
bot.py
View file

@ -3,21 +3,25 @@ from discord.ext import commands
import os
import json
import logging
import tomllib
from aiohttp import web
import asyncio
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('bot')
CONFIG = {
'xp_min': 15,
'xp_max': 25,
'xp_cooldown': 60,
'xp_per_level': 100,
'level_up_multiplier': 10,
'data_file': 'data.json',
'video_check_interval': 300
}
def load_config():
base_path = os.path.dirname(__file__)
config_path = os.path.join(base_path, "config.toml")
try:
print(f"Loading config from: {config_path}")
with open(config_path, "rb") as f:
return tomllib.load(f)
except FileNotFoundError:
logger.error("config.toml missing.")
exit(1)
CONFIG = load_config()
class SimpleDB:
def __init__(self, filename):
@ -41,13 +45,8 @@ class SimpleDB:
key = f"{guild_id}_{user_id}"
if key not in self.data['users']:
self.data['users'][key] = {
'coins': 0,
'bank': 0,
'level': 1,
'xp': 0,
'last_message': 0,
'last_daily': 0,
'last_work': 0
'coins': 0, 'bank': 0, 'level': 1, 'xp': 0,
'last_message': 0, 'last_daily': 0, 'last_work': 0
}
return self.data['users'][key]
@ -56,15 +55,6 @@ class SimpleDB:
self.data['users'][key] = data
self.save()
def get_all_guild_users(self, guild_id):
users = []
for key, data in self.data['users'].items():
if key.startswith(f"{guild_id}_"):
user_id = key.split('_')[1]
users.append({'user_id': user_id, 'data': data})
users.sort(key=lambda x: (x['data']['level'], x['data']['xp']), reverse=True)
return users
class MyBot(commands.Bot):
def __init__(self):
intents = discord.Intents.default()
@ -72,85 +62,57 @@ class MyBot(commands.Bot):
intents.members = True
super().__init__(command_prefix='/', intents=intents)
self.db = SimpleDB(CONFIG['data_file'])
self.db = SimpleDB(CONFIG['bot']['data_file'])
self.config = CONFIG
async def setup_hook(self):
await self.load_extension('cogs.leveling')
await self.load_extension('cogs.system')
await self.load_extension('cogs.economy')
await self.load_extension('cogs.fun')
for extension in self.config['bot']['enabled_cogs']:
try:
await self.load_extension(extension)
logger.info(f'Loaded: {extension}')
except Exception as e:
logger.error(f'Error {extension}: {e}')
await self.tree.sync()
logger.info('✅ All cogs loaded and commands synced!')
bot = MyBot()
async def health_check(request):
return web.Response(text="Bot is running! ✅")
return web.Response(text="Bot is running!")
async def redirect_handler(request):
code = request.match_info.get('code', '')
if os.path.exists(CONFIG['bot']['data_file']):
try:
with open(CONFIG['bot']['data_file'], 'r') as f:
data = json.load(f)
for guild_id, guild_data in data.get('guilds', {}).items():
if 'urls' in guild_data and code in guild_data['urls']:
return web.Response(status=301, headers={'Location': guild_data['urls'][code]})
except Exception as e:
logger.error(f'Error: {e}')
return web.Response(text='Not Found', status=404)
async def start_web_server():
app = web.Application()
app.router.add_get('/', health_check)
app.router.add_get('/health', health_check)
app.router.add_get('/{code}', redirect_handler)
runner = web.AppRunner(app)
await runner.setup()
port = int(os.getenv('PORT', 8080))
site = web.TCPSite(runner, '0.0.0.0', port)
await site.start()
logger.info(f'🌐 Health check server running on port {port}')
@bot.event
async def on_ready():
asyncio.create_task(start_web_server())
logger.info(f'✅ Logged in as {bot.user}')
logger.info(f'📊 Connected to {len(bot.guilds)} guilds')
await bot.change_presence(activity=discord.Game(name="/help"))
logger.info('🚀 All systems operational!')
@bot.tree.command(name='help', description='Show all available commands')
async def help_command(interaction: discord.Interaction):
embed = discord.Embed(
title='🤖 Bot Commands',
description='Here are all the slash commands you can use!',
color=0x5865F2
)
embed.add_field(
name='📊 Leveling & Economy',
value='`/rank` - View your rank\n`/leaderboard` - Top 10 users\n`/balance` - Check balance\n`/daily` - Daily reward\n`/work` - Work for coins',
inline=False
)
embed.add_field(
name='🎮 Fun',
value='`/8ball` - Magic 8ball\n`/roll` - Roll dice\n`/flip` - Flip coin\n`/cat` - Random cat\n`/dog` - Random dog',
inline=False
)
embed.add_field(
name='🛡️ System & Moderation',
value='`/kick` - Kick member\n`/ban` - Ban member\n`/unban` - Unban user\n`/timeout` - Timeout member\n`/warn` - Warn member\n`/warnings` - View warnings\n`/clearwarnings` - Clear warnings\n`/purge` - Delete messages\n`/lock` - Lock channel\n`/unlock` - Unlock channel',
inline=False
)
embed.add_field(
name='🎭 Reaction Roles & YouTube',
value='`/reactionrole` - Create reaction role\n`/removereactionrole` - Remove reaction role\n`/listreactionroles` - List reaction roles\n`/createreactionpanel` - Create panel\n`/setupyoutube` - Setup YouTube\n`/toggleyoutube` - Toggle YouTube\n`/youtubestatus` - YouTube status\n`/testlastvideo` - Test video',
inline=False
)
embed.add_field(
name=' Info',
value='`/ping` - Bot latency\n`/serverinfo` - Server info\n`/userinfo` - User info',
inline=False
)
await interaction.response.send_message(embed=embed)
logger.info(f'Logged in as {bot.user}')
await bot.change_presence(activity=discord.Game(name="Commands"))
if __name__ == '__main__':
token = os.getenv('DISCORD_TOKEN')
if not token:
logger.error('DISCORD_TOKEN not set!')
logger.error('DISCORD_TOKEN not set')
exit(1)
logger.info("everythings working")
logger.info('hello from chersbobers and booly co :3')
bot.run(token)

View file

@ -14,7 +14,7 @@ class Economy(commands.Cog):
user_data = self.bot.db.get_user(str(interaction.guild.id), str(target.id))
embed = discord.Embed(
title='💰 Balance',
title='Balance',
description=f'{target.mention} has **{user_data["coins"]:,}** coins in wallet and **{user_data["bank"]:,}** in bank!\n**Total:** {user_data["coins"] + user_data["bank"]:,} coins',
color=0xFFD700
)
@ -28,7 +28,7 @@ class Economy(commands.Cog):
if now - user_data['last_daily'] < 86400:
time_left = 86400 - (now - user_data['last_daily'])
hours = int(time_left / 3600)
await interaction.response.send_message(f'You already claimed your daily! Come back in {hours} hours.', ephemeral=True)
await interaction.response.send_message(f'You already claimed your daily! Come back in {hours} hours.', ephemeral=True)
return
reward = 100
@ -36,7 +36,7 @@ class Economy(commands.Cog):
user_data['last_daily'] = now
self.bot.db.set_user(str(interaction.guild.id), str(interaction.user.id), user_data)
await interaction.response.send_message(f'You claimed your daily reward of **{reward:,}** coins!\n💰 New balance: **{user_data["coins"]:,}** coins')
await interaction.response.send_message(f'You claimed your daily reward of **{reward:,}** coins!\n💰 New balance: **{user_data["coins"]:,}** coins')
@app_commands.command(name='work', description='Work to earn coins')
async def work(self, interaction: discord.Interaction):
@ -46,7 +46,7 @@ class Economy(commands.Cog):
if now - user_data['last_work'] < 3600:
time_left = 3600 - (now - user_data['last_work'])
minutes = int(time_left / 60)
await interaction.response.send_message(f'You need to rest! Come back in {minutes} minutes.', ephemeral=True)
await interaction.response.send_message(f'You need to rest! Come back in {minutes} minutes.', ephemeral=True)
return
earnings = random.randint(10, 50)
@ -63,7 +63,7 @@ class Economy(commands.Cog):
'You tutored students and earned'
]
await interaction.response.send_message(f'💼 {random.choice(jobs)} **{earnings:,}** coins!\n💰 New balance: **{user_data["coins"]:,}** coins')
await interaction.response.send_message(f'{random.choice(jobs)} **{earnings:,}** coins!\n💰 New balance: **{user_data["coins"]:,}** coins')
async def setup(bot):
await bot.add_cog(Economy(bot))

View file

@ -16,20 +16,20 @@ class Fun(commands.Cog):
'Reply hazy, try again.', 'Ask again later.', 'Better not tell you now.',
'Cannot predict now.', "Don't count on it.", 'My reply is no.', 'Very doubtful.'
]
await interaction.response.send_message(f'🔮 **{question}**\n{random.choice(responses)}')
await interaction.response.send_message(f' **{question}**\n{random.choice(responses)}')
@app_commands.command(name='roll', description='Roll a dice')
async def roll(self, interaction: discord.Interaction, sides: int = 6):
if sides < 2 or sides > 100:
await interaction.response.send_message('Dice must have between 2 and 100 sides!', ephemeral=True)
await interaction.response.send_message('Dice must have between 2 and 100 sides!', ephemeral=True)
return
result = random.randint(1, sides)
await interaction.response.send_message(f'🎲 You rolled a **{result}** (1-{sides})')
await interaction.response.send_message(f' You rolled a **{result}** (1-{sides})')
@app_commands.command(name='flip', description='Flip a coin')
async def flip(self, interaction: discord.Interaction):
result = random.choice(['Heads', 'Tails'])
await interaction.response.send_message(f'🪙 The coin landed on **{result}**!')
await interaction.response.send_message(f'The coin landed on **{result}**!')
@app_commands.command(name='cat', description='Get a random cat picture')
async def cat(self, interaction: discord.Interaction):
@ -38,12 +38,12 @@ class Fun(commands.Cog):
try:
async with session.get('https://api.thecatapi.com/v1/images/search') as resp:
data = await resp.json()
embed = discord.Embed(title='🐱 Random Kitty!', color=0xFF69B4)
embed = discord.Embed(title='Random Kitty!', color=0xFF69B4)
embed.set_image(url=data[0]['url'])
embed.set_footer(text=f'Requested by {interaction.user.name}')
await interaction.followup.send(embed=embed)
except:
await interaction.followup.send('Failed to fetch a cat picture 😿')
await interaction.followup.send('Failed to fetch a cat picture ')
@app_commands.command(name='dog', description='Get a random dog picture')
async def dog(self, interaction: discord.Interaction):
@ -52,12 +52,12 @@ class Fun(commands.Cog):
try:
async with session.get('https://api.thedogapi.com/v1/images/search') as resp:
data = await resp.json()
embed = discord.Embed(title='🐶 Random Doggy!', color=0xFF69B4)
embed = discord.Embed(title='Random Doggy!', color=0xFF69B4)
embed.set_image(url=data[0]['url'])
embed.set_footer(text=f'Requested by {interaction.user.name}')
await interaction.followup.send(embed=embed)
except:
await interaction.followup.send('Failed to fetch a dog picture 😥')
await interaction.followup.send('Failed to fetch a dog picture ')
async def setup(bot):
await bot.add_cog(Fun(bot))

View file

@ -30,13 +30,13 @@ class Leveling(commands.Cog):
user_data['coins'] += coin_reward
messages = [
f'🎉 GG {message.author.mention}! You leveled up to **Level {user_data["level"]}**!',
f'Congrats {message.author.mention}! You\'re now **Level {user_data["level"]}**!',
f'🚀 Level up! {message.author.mention} reached **Level {user_data["level"]}**!'
f'gg {message.author.mention}! You leveled up to **Level {user_data["level"]}**!',
f'Congrats {message.author.mention}! You\'re now **Level {user_data["level"]}**!',
f'Level up! {message.author.mention} reached **Level {user_data["level"]}**!'
]
await message.channel.send(
f'{random.choice(messages)} You earned **{coin_reward:,} coins**! 💰'
f'{random.choice(messages)} You earned **{coin_reward:,} coins**! '
)
self.bot.db.set_user(str(message.guild.id), str(message.author.id), user_data)
@ -64,7 +64,7 @@ class Leveling(commands.Cog):
`{bar}`
**💰 BALANCE** {user_data['coins']:,} coins
**BALANCE** {user_data['coins']:,} coins
"""
embed.set_thumbnail(url=target.display_avatar.url)
await interaction.response.send_message(embed=embed)
@ -82,7 +82,7 @@ class Leveling(commands.Cog):
)
embed = discord.Embed(
title='🏆 Server Leaderboard',
title='Server Leaderboard',
description='\n'.join(description) if description else 'No users yet!',
color=0x9B59B6,
timestamp=datetime.utcnow()

View file

@ -45,7 +45,7 @@ class System(commands.Cog):
continue
embed = discord.Embed(
title='🎬 New YouTube Video!',
title='New YouTube Video!',
description=f'**{latest.title}**',
url=latest.link,
color=0xFF0000,
@ -60,14 +60,14 @@ class System(commands.Cog):
if hasattr(latest, 'published'):
embed.add_field(name='Published', value=latest.published, inline=True)
await channel.send('📺 New video alert! @everyone', embed=embed)
logger.info(f'📺 Sent notification for: {latest.title}')
await channel.send('New video alert! @everyone', embed=embed)
logger.info(f'Sent notification for: {latest.title}')
settings['last_video_id'] = video_id
self.bot.db.save()
except Exception as e:
logger.error(f'Error checking YouTube: {e}')
logger.error(f'Error checking YouTube: {e}')
@check_youtube.before_loop
async def before_check_youtube(self):
@ -76,14 +76,14 @@ class System(commands.Cog):
@app_commands.command(name='ping', description='Check bot latency')
async def ping(self, interaction: discord.Interaction):
latency = round(self.bot.latency * 1000)
await interaction.response.send_message(f'🏓 Pong! Latency: `{latency}ms`')
await interaction.response.send_message(f'Pong! Latency: `{latency}ms`')
@app_commands.command(name='serverinfo', description='Display server information')
async def serverinfo(self, interaction: discord.Interaction):
guild = interaction.guild
embed = discord.Embed(
title=f'📊 {guild.name}',
title=f'{guild.name}',
color=0x5865F2,
timestamp=datetime.utcnow()
)
@ -91,12 +91,12 @@ class System(commands.Cog):
if guild.icon:
embed.set_thumbnail(url=guild.icon.url)
embed.add_field(name='👑 Owner', value=guild.owner.mention, inline=True)
embed.add_field(name='👥 Members', value=guild.member_count, inline=True)
embed.add_field(name='📅 Created', value=guild.created_at.strftime('%Y-%m-%d'), inline=True)
embed.add_field(name='💬 Channels', value=len(guild.channels), inline=True)
embed.add_field(name='😀 Emojis', value=len(guild.emojis), inline=True)
embed.add_field(name='🎭 Roles', value=len(guild.roles), inline=True)
embed.add_field(name='Owner', value=guild.owner.mention, inline=True)
embed.add_field(name='Members', value=guild.member_count, inline=True)
embed.add_field(name='Created', value=guild.created_at.strftime('%Y-%m-%d'), inline=True)
embed.add_field(name='Channels', value=len(guild.channels), inline=True)
embed.add_field(name='Emojis', value=len(guild.emojis), inline=True)
embed.add_field(name='Roles', value=len(guild.roles), inline=True)
await interaction.response.send_message(embed=embed)
@ -105,21 +105,21 @@ class System(commands.Cog):
target = member or interaction.user
embed = discord.Embed(
title=f'👤 {target.name}',
title=f'{target.name}',
color=target.color if target.color != discord.Color.default() else 0x5865F2,
timestamp=datetime.utcnow()
)
embed.set_thumbnail(url=target.display_avatar.url)
embed.add_field(name='🆔 ID', value=target.id, inline=True)
embed.add_field(name='📛 Nickname', value=target.nick or 'None', inline=True)
embed.add_field(name='🤖 Bot', value='Yes' if target.bot else 'No', inline=True)
embed.add_field(name='📅 Joined Server', value=target.joined_at.strftime('%Y-%m-%d %H:%M') if target.joined_at else 'Unknown', inline=True)
embed.add_field(name='📅 Account Created', value=target.created_at.strftime('%Y-%m-%d %H:%M'), inline=True)
embed.add_field(name='ID', value=target.id, inline=True)
embed.add_field(name='Nickname', value=target.nick or 'None', inline=True)
embed.add_field(name='Bot', value='Yes' if target.bot else 'No', inline=True)
embed.add_field(name='Joined Server', value=target.joined_at.strftime('%Y-%m-%d %H:%M') if target.joined_at else 'Unknown', inline=True)
embed.add_field(name='Account Created', value=target.created_at.strftime('%Y-%m-%d %H:%M'), inline=True)
roles = [role.mention for role in target.roles if role.name != '@everyone']
embed.add_field(name=f'🎭 Roles ({len(roles)})', value=' '.join(roles) if roles else 'None', inline=False)
embed.add_field(name=f'Roles ({len(roles)})', value=' '.join(roles) if roles else 'None', inline=False)
await interaction.response.send_message(embed=embed)
@ -128,18 +128,18 @@ class System(commands.Cog):
@app_commands.default_permissions(kick_members=True)
async def kick(self, interaction: discord.Interaction, member: discord.Member, reason: str = 'No reason provided'):
if member.top_role >= interaction.user.top_role and interaction.user != interaction.guild.owner:
await interaction.response.send_message('You cannot kick this member!', ephemeral=True)
await interaction.response.send_message('You cannot kick this member!', ephemeral=True)
return
if member == interaction.guild.owner:
await interaction.response.send_message('You cannot kick the server owner!', ephemeral=True)
await interaction.response.send_message('You cannot kick the server owner!', ephemeral=True)
return
try:
await member.kick(reason=f'{reason} | Kicked by {interaction.user}')
embed = discord.Embed(
title='👢 Member Kicked',
title='Member Kicked',
description=f'{member.mention} has been kicked from the server',
color=0xFF9500,
timestamp=datetime.utcnow()
@ -148,33 +148,33 @@ class System(commands.Cog):
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
logger.info(f'👢 {member} kicked by {interaction.user} - Reason: {reason}')
logger.info(f'{member} kicked by {interaction.user} - Reason: {reason}')
except discord.Forbidden:
await interaction.response.send_message('I do not have permission to kick this member!', ephemeral=True)
await interaction.response.send_message('I do not have permission to kick this member!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
@app_commands.command(name='ban', description='[MOD] Ban a member from the server')
@app_commands.describe(member='Member to ban', reason='Reason for ban', delete_days='Days of messages to delete (0-7)')
@app_commands.default_permissions(ban_members=True)
async def ban(self, interaction: discord.Interaction, member: discord.Member, reason: str = 'No reason provided', delete_days: int = 0):
if member.top_role >= interaction.user.top_role and interaction.user != interaction.guild.owner:
await interaction.response.send_message('You cannot ban this member!', ephemeral=True)
await interaction.response.send_message('You cannot ban this member!', ephemeral=True)
return
if member == interaction.guild.owner:
await interaction.response.send_message('You cannot ban the server owner!', ephemeral=True)
await interaction.response.send_message('You cannot ban the server owner!', ephemeral=True)
return
if delete_days < 0 or delete_days > 7:
await interaction.response.send_message('Delete days must be between 0 and 7!', ephemeral=True)
await interaction.response.send_message('Delete days must be between 0 and 7!', ephemeral=True)
return
try:
await member.ban(reason=f'{reason} | Banned by {interaction.user}', delete_message_days=delete_days)
embed = discord.Embed(
title='🔨 Member Banned',
title='Member Banned',
description=f'{member.mention} has been banned from the server',
color=0xFF0000,
timestamp=datetime.utcnow()
@ -183,11 +183,11 @@ class System(commands.Cog):
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
logger.info(f'🔨 {member} banned by {interaction.user} - Reason: {reason}')
logger.info(f'{member} banned by {interaction.user} - Reason: {reason}')
except discord.Forbidden:
await interaction.response.send_message('I do not have permission to ban this member!', ephemeral=True)
await interaction.response.send_message('I do not have permission to ban this member!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
@app_commands.command(name='unban', description='[MOD] Unban a user from the server')
@app_commands.describe(user_id='User ID to unban', reason='Reason for unban')
@ -196,14 +196,14 @@ class System(commands.Cog):
try:
user = await self.bot.fetch_user(int(user_id))
except:
await interaction.response.send_message('Invalid user ID!', ephemeral=True)
await interaction.response.send_message('Invalid user ID!', ephemeral=True)
return
try:
await interaction.guild.unban(user, reason=f'{reason} | Unbanned by {interaction.user}')
embed = discord.Embed(
title='User Unbanned',
title='User Unbanned',
description=f'{user.mention} has been unbanned from the server',
color=0x00FF00,
timestamp=datetime.utcnow()
@ -212,28 +212,28 @@ class System(commands.Cog):
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
logger.info(f'{user} unbanned by {interaction.user} - Reason: {reason}')
logger.info(f'{user} unbanned by {interaction.user} - Reason: {reason}')
except discord.NotFound:
await interaction.response.send_message('This user is not banned!', ephemeral=True)
await interaction.response.send_message('This user is not banned!', ephemeral=True)
except discord.Forbidden:
await interaction.response.send_message('I do not have permission to unban users!', ephemeral=True)
await interaction.response.send_message('I do not have permission to unban users!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
@app_commands.command(name='timeout', description='[MOD] Timeout a member')
@app_commands.describe(member='Member to timeout', duration='Duration in minutes', reason='Reason for timeout')
@app_commands.default_permissions(moderate_members=True)
async def timeout(self, interaction: discord.Interaction, member: discord.Member, duration: int, reason: str = 'No reason provided'):
if member.top_role >= interaction.user.top_role and interaction.user != interaction.guild.owner:
await interaction.response.send_message('You cannot timeout this member!', ephemeral=True)
await interaction.response.send_message('You cannot timeout this member!', ephemeral=True)
return
if member == interaction.guild.owner:
await interaction.response.send_message('You cannot timeout the server owner!', ephemeral=True)
await interaction.response.send_message('You cannot timeout the server owner!', ephemeral=True)
return
if duration < 1 or duration > 40320:
await interaction.response.send_message('Duration must be between 1 minute and 28 days!', ephemeral=True)
await interaction.response.send_message('Duration must be between 1 minute and 28 days!', ephemeral=True)
return
try:
@ -241,7 +241,7 @@ class System(commands.Cog):
await member.timeout(timeout_until, reason=f'{reason} | Timed out by {interaction.user}')
embed = discord.Embed(
title='Member Timed Out',
title='Member Timed Out',
description=f'{member.mention} has been timed out',
color=0xFFA500,
timestamp=datetime.utcnow()
@ -251,11 +251,11 @@ class System(commands.Cog):
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
logger.info(f'{member} timed out by {interaction.user} for {duration}m - Reason: {reason}')
logger.info(f'{member} timed out by {interaction.user} for {duration}m - Reason: {reason}')
except discord.Forbidden:
await interaction.response.send_message('I do not have permission to timeout this member!', ephemeral=True)
await interaction.response.send_message('I do not have permission to timeout this member!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
@app_commands.command(name='warn', description='[MOD] Warn a member')
@app_commands.describe(member='Member to warn', reason='Reason for warning')
@ -285,7 +285,7 @@ class System(commands.Cog):
warning_count = len(self.bot.db.data['warnings'][guild_id][user_id])
embed = discord.Embed(
title='⚠️ Member Warned',
title='Member Warned',
description=f'{member.mention} has been warned',
color=0xFFFF00,
timestamp=datetime.utcnow()
@ -297,11 +297,11 @@ class System(commands.Cog):
await interaction.response.send_message(embed=embed)
try:
await member.send(f'⚠️ You have been warned in **{interaction.guild.name}**\n**Reason:** {reason}\n**Total Warnings:** {warning_count}')
await member.send(f'You have been warned in **{interaction.guild.name}**\n**Reason:** {reason}\n**Total Warnings:** {warning_count}')
except:
pass
logger.info(f'⚠️ {member} warned by {interaction.user} - Reason: {reason}')
logger.info(f'{member} warned by {interaction.user} - Reason: {reason}')
@app_commands.command(name='warnings', description='View warnings for a member')
@app_commands.describe(member='Member to check warnings for')
@ -320,7 +320,7 @@ class System(commands.Cog):
return
embed = discord.Embed(
title=f'⚠️ Warnings for {target.display_name}',
title=f'Warnings for {target.display_name}',
color=0xFFFF00,
timestamp=datetime.utcnow()
)
@ -359,7 +359,7 @@ class System(commands.Cog):
self.bot.db.save()
embed = discord.Embed(
title='Warnings Cleared',
title='Warnings Cleared',
description=f'Cleared {warning_count} warning(s) for {member.mention}',
color=0x00FF00,
timestamp=datetime.utcnow()
@ -367,14 +367,14 @@ class System(commands.Cog):
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'Cleared {warning_count} warnings for {member} by {interaction.user}')
logger.info(f'Cleared {warning_count} warnings for {member} by {interaction.user}')
@app_commands.command(name='purge', description='[MOD] Delete multiple messages')
@app_commands.describe(amount='Number of messages to delete (1-100)', member='Only delete messages from this member (optional)')
@app_commands.default_permissions(manage_messages=True)
async def purge(self, interaction: discord.Interaction, amount: int, member: discord.Member = None):
if amount < 1 or amount > 100:
await interaction.response.send_message('Amount must be between 1 and 100!', ephemeral=True)
await interaction.response.send_message('Amount must be between 1 and 100!', ephemeral=True)
return
await interaction.response.defer(ephemeral=True)
@ -385,12 +385,12 @@ class System(commands.Cog):
else:
deleted = await interaction.channel.purge(limit=amount)
await interaction.followup.send(f'Deleted {len(deleted)} message(s)!', ephemeral=True)
logger.info(f'🗑️ {interaction.user} purged {len(deleted)} messages in #{interaction.channel.name}')
await interaction.followup.send(f'Deleted {len(deleted)} message(s)!', ephemeral=True)
logger.info(f'{interaction.user} purged {len(deleted)} messages in #{interaction.channel.name}')
except discord.Forbidden:
await interaction.followup.send('I do not have permission to delete messages!', ephemeral=True)
await interaction.followup.send('I do not have permission to delete messages!', ephemeral=True)
except Exception as e:
await interaction.followup.send(f'An error occurred: {e}', ephemeral=True)
await interaction.followup.send(f'An error occurred: {e}', ephemeral=True)
@app_commands.command(name='lock', description='[MOD] Lock a channel')
@app_commands.describe(channel='Channel to lock (defaults to current channel)')
@ -402,7 +402,7 @@ class System(commands.Cog):
await target_channel.set_permissions(interaction.guild.default_role, send_messages=False)
embed = discord.Embed(
title='🔒 Channel Locked',
title='Channel Locked',
description=f'{target_channel.mention} has been locked',
color=0xFF0000,
timestamp=datetime.utcnow()
@ -410,11 +410,11 @@ class System(commands.Cog):
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'🔒 {target_channel.name} locked by {interaction.user}')
logger.info(f'{target_channel.name} locked by {interaction.user}')
except discord.Forbidden:
await interaction.response.send_message('I do not have permission to lock this channel!', ephemeral=True)
await interaction.response.send_message('I do not have permission to lock this channel!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
@app_commands.command(name='unlock', description='[MOD] Unlock a channel')
@app_commands.describe(channel='Channel to unlock (defaults to current channel)')
@ -426,7 +426,7 @@ class System(commands.Cog):
await target_channel.set_permissions(interaction.guild.default_role, send_messages=None)
embed = discord.Embed(
title='🔓 Channel Unlocked',
title='Channel Unlocked',
description=f'{target_channel.mention} has been unlocked',
color=0x00FF00,
timestamp=datetime.utcnow()
@ -434,11 +434,11 @@ class System(commands.Cog):
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'🔓 {target_channel.name} unlocked by {interaction.user}')
logger.info(f'{target_channel.name} unlocked by {interaction.user}')
except discord.Forbidden:
await interaction.response.send_message('I do not have permission to unlock this channel!', ephemeral=True)
await interaction.response.send_message('I do not have permission to unlock this channel!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
await interaction.response.send_message(f'An error occurred: {e}', ephemeral=True)
@commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
@ -477,9 +477,9 @@ class System(commands.Cog):
try:
await member.add_roles(role)
logger.info(f'Added role {role.name} to {member.name}')
logger.info(f'Added role {role.name} to {member.name}')
except Exception as e:
logger.error(f'Error adding role: {e}')
logger.error(f'Error adding role: {e}')
@commands.Cog.listener()
async def on_raw_reaction_remove(self, payload):
@ -518,9 +518,9 @@ class System(commands.Cog):
try:
await member.remove_roles(role)
logger.info(f' Removed role {role.name} from {member.name}')
logger.info(f'Removed role {role.name} from {member.name}')
except Exception as e:
logger.error(f'Error removing role: {e}')
logger.error(f'Error removing role: {e}')
@app_commands.command(name='reactionrole', description='[ADMIN] Create a reaction role')
@app_commands.describe(message_id='Message ID to add reactions to', emoji='Emoji to use (e.g., 🎮)', role='Role to assign')
@ -529,16 +529,16 @@ class System(commands.Cog):
try:
message = await interaction.channel.fetch_message(int(message_id))
except discord.NotFound:
await interaction.response.send_message('Message not found in this channel!', ephemeral=True)
await interaction.response.send_message('Message not found in this channel!', ephemeral=True)
return
except ValueError:
await interaction.response.send_message('Invalid message ID!', ephemeral=True)
await interaction.response.send_message('Invalid message ID!', ephemeral=True)
return
try:
await message.add_reaction(emoji)
except discord.HTTPException:
await interaction.response.send_message('Invalid emoji or unable to add reaction!', ephemeral=True)
await interaction.response.send_message('Invalid emoji or unable to add reaction!', ephemeral=True)
return
guild_id = str(interaction.guild.id)
@ -556,7 +556,7 @@ class System(commands.Cog):
self.bot.db.save()
embed = discord.Embed(
title='Reaction Role Created',
title='Reaction Role Created',
description=f'React with {emoji} on the message to get {role.mention}',
color=0x00FF00
)
@ -565,7 +565,7 @@ class System(commands.Cog):
embed.add_field(name='Role', value=role.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'Created reaction role: {emoji} -> {role.name}')
logger.info(f'Created reaction role: {emoji} -> {role.name}')
@app_commands.command(name='removereactionrole', description='[ADMIN] Remove a reaction role')
@app_commands.describe(message_id='Message ID', emoji='Emoji to remove (leave empty to remove all)')
@ -577,21 +577,21 @@ class System(commands.Cog):
self.bot.db.data['reaction_roles'] = {}
if guild_id not in self.bot.db.data['reaction_roles'] or message_id not in self.bot.db.data['reaction_roles'].get(guild_id, {}):
await interaction.response.send_message('No reaction roles found for that message!', ephemeral=True)
await interaction.response.send_message('No reaction roles found for that message!', ephemeral=True)
return
if emoji:
if emoji not in self.bot.db.data['reaction_roles'][guild_id][message_id]:
await interaction.response.send_message('That emoji is not set up for reaction roles!', ephemeral=True)
await interaction.response.send_message('That emoji is not set up for reaction roles!', ephemeral=True)
return
del self.bot.db.data['reaction_roles'][guild_id][message_id][emoji]
self.bot.db.save()
await interaction.response.send_message(f'Removed reaction role for {emoji}')
await interaction.response.send_message(f'Removed reaction role for {emoji}')
else:
del self.bot.db.data['reaction_roles'][guild_id][message_id]
self.bot.db.save()
await interaction.response.send_message(f'Removed all reaction roles from message {message_id}')
await interaction.response.send_message(f'Removed all reaction roles from message {message_id}')
@app_commands.command(name='listreactionroles', description='List all reaction roles')
async def listreactionroles(self, interaction: discord.Interaction):
@ -607,7 +607,7 @@ class System(commands.Cog):
return
embed = discord.Embed(
title='🎭 Reaction Roles',
title='Reaction Roles',
color=0x9B59B6
)
@ -631,7 +631,7 @@ class System(commands.Cog):
@app_commands.default_permissions(administrator=True)
async def createreactionpanel(self, interaction: discord.Interaction, title: str, description: str):
embed = discord.Embed(
title=f'🎭 {title}',
title=f'{title}',
description=description,
color=0x9B59B6
)
@ -640,7 +640,7 @@ class System(commands.Cog):
message = await interaction.channel.send(embed=embed)
await interaction.response.send_message(
f'Panel created! Message ID: `{message.id}`\n'
f'Panel created! Message ID: `{message.id}`\n'
f'Use `/reactionrole {message.id} <emoji> <role>` to add roles to it.',
ephemeral=True
)
@ -664,16 +664,16 @@ class System(commands.Cog):
self.bot.db.save()
embed = discord.Embed(
title='YouTube Notifications Configured',
title='YouTube Notifications Configured',
description=f'New video notifications will be posted in {channel.mention}',
color=0xFF0000
)
embed.add_field(name='YouTube Channel ID', value=f'`{youtube_channel_id}`', inline=False)
embed.add_field(name='Check Interval', value='Every 5 minutes', inline=True)
embed.add_field(name='Status', value='🟢 Active', inline=True)
embed.add_field(name='Status', value='Active', inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'YouTube notifications enabled in {interaction.guild.name} → #{channel.name}')
logger.info(f'YouTube notifications enabled in {interaction.guild.name} → #{channel.name}')
@app_commands.command(name='toggleyoutube', description='[ADMIN] Toggle YouTube notifications on/off')
@app_commands.default_permissions(administrator=True)
@ -693,11 +693,11 @@ class System(commands.Cog):
self.bot.db.data['youtube'][guild_id]['enabled'] = not self.bot.db.data['youtube'][guild_id].get('enabled', False)
self.bot.db.save()
status = 'enabled' if self.bot.db.data['youtube'][guild_id]['enabled'] else 'disabled'
status = 'enabled' if self.bot.db.data['youtube'][guild_id]['enabled'] else 'disabled'
color = 0x00FF00 if self.bot.db.data['youtube'][guild_id]['enabled'] else 0x808080
embed = discord.Embed(
title='🔔 YouTube Notifications',
title='YouTube Notifications',
description=f'YouTube notifications are now **{status}**',
color=color,
timestamp=datetime.utcnow()
@ -719,11 +719,11 @@ class System(commands.Cog):
})
embed = discord.Embed(
title='📺 YouTube Notification Status',
title='YouTube Notification Status',
color=0xFF0000
)
status = '🟢 Enabled' if settings.get('enabled') else '🔴 Disabled'
status = 'Enabled' if settings.get('enabled') else 'Disabled'
embed.add_field(name='Status', value=status, inline=True)
if settings.get('channel_id'):
@ -736,7 +736,7 @@ class System(commands.Cog):
if settings.get('youtube_channel_id'):
embed.add_field(name='YouTube Channel ID', value=f'`{settings["youtube_channel_id"]}`', inline=False)
else:
embed.add_field(name='⚠️ Warning', value='YouTube Channel ID not set', inline=False)
embed.add_field(name='Warning', value='YouTube Channel ID not set', inline=False)
if settings.get('last_video_id'):
embed.add_field(name='Last Video ID', value=f'`{settings["last_video_id"]}`', inline=False)
@ -756,7 +756,7 @@ class System(commands.Cog):
settings = self.bot.db.data['youtube'].get(guild_id, {})
if not settings.get('youtube_channel_id'):
await interaction.response.send_message('YouTube Channel ID not configured! Use `/setupyoutube` first.', ephemeral=True)
await interaction.response.send_message('YouTube Channel ID not configured! Use `/setupyoutube` first.', ephemeral=True)
return
await interaction.response.defer()
@ -766,14 +766,14 @@ class System(commands.Cog):
feed = await asyncio.to_thread(feedparser.parse, feed_url)
if not feed.entries:
await interaction.followup.send('No videos found for this channel!')
await interaction.followup.send('No videos found for this channel!')
return
latest = feed.entries[0]
video_id = latest.yt_videoid if hasattr(latest, 'yt_videoid') else latest.id.split(':')[-1]
embed = discord.Embed(
title='🎬 Latest YouTube Video (Test)',
title='Latest YouTube Video (Test)',
description=f'**{latest.title}**',
url=latest.link,
color=0xFF0000,
@ -791,11 +791,11 @@ class System(commands.Cog):
embed.set_footer(text='This is a test notification')
await interaction.followup.send(embed=embed)
logger.info(f'📺 Test notification sent for: {latest.title}')
logger.info(f'Test notification sent for: {latest.title}')
except Exception as e:
await interaction.followup.send(f'Error fetching video: {e}')
logger.error(f'Error in testlastvideo: {e}')
await interaction.followup.send(f'Error fetching video: {e}')
logger.error(f'Error in testlastvideo: {e}')
async def setup(bot):
await bot.add_cog(System(bot))

143
cogs/utility.py Normal file
View file

@ -0,0 +1,143 @@
import discord
from discord.ext import commands
from discord import app_commands
import re
import random
import string
class Utility(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.domain = "u.chers.moe"
def is_valid_url(self, url):
url_pattern = re.compile(
r'^https?://'
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|'
r'localhost|'
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
r'(?::\d+)?'
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
return url_pattern.match(url) is not None
def generate_short_code(self, length=6):
chars = string.ascii_letters + string.digits
return ''.join(random.choice(chars) for _ in range(length))
def get_guild_data(self, guild_id):
if 'guilds' not in self.bot.db.data:
self.bot.db.data['guilds'] = {}
if str(guild_id) not in self.bot.db.data['guilds']:
self.bot.db.data['guilds'][str(guild_id)] = {'urls': {}}
if 'urls' not in self.bot.db.data['guilds'][str(guild_id)]:
self.bot.db.data['guilds'][str(guild_id)]['urls'] = {}
return self.bot.db.data['guilds'][str(guild_id)]
@app_commands.command(name='shorten', description='Shorten a long URL')
@app_commands.describe(
url='The URL you want to shorten',
code='Custom short code (optional)'
)
async def shorten_url(self, interaction: discord.Interaction, url: str, code: str = None):
if not self.is_valid_url(url):
await interaction.response.send_message(
'Please provide a valid URL',
ephemeral=True
)
return
guild_data = self.get_guild_data(interaction.guild_id)
for existing_code, stored_url in guild_data['urls'].items():
if stored_url == url:
shortened = f"https://{self.domain}/{existing_code}"
embed = discord.Embed(title='URL Already Shortened', color=0x5865F2)
embed.add_field(name='Original', value=url[:100] + ('...' if len(url) > 100 else ''), inline=False)
embed.add_field(name='Shortened', value=shortened, inline=False)
await interaction.response.send_message(embed=embed)
return
if code:
if code in guild_data['urls']:
await interaction.response.send_message(
'This short code is already taken',
ephemeral=True
)
return
else:
code = self.generate_short_code()
while code in guild_data['urls']:
code = self.generate_short_code()
guild_data['urls'][code] = url
self.bot.db.save()
shortened = f"https://{self.domain}/{code}"
embed = discord.Embed(title='URL Shortened', color=0x5865F2)
embed.add_field(name='Original', value=url[:100] + ('...' if len(url) > 100 else ''), inline=False)
embed.add_field(name='Shortened', value=shortened, inline=False)
embed.add_field(name='Code', value=code, inline=False)
await interaction.response.send_message(embed=embed)
@app_commands.command(name='expand', description='Get the original URL from a short code')
@app_commands.describe(code='The short code to expand')
async def expand_url(self, interaction: discord.Interaction, code: str):
guild_data = self.get_guild_data(interaction.guild_id)
if code in guild_data['urls']:
original_url = guild_data['urls'][code]
embed = discord.Embed(title='URL Expanded', color=0x5865F2)
embed.add_field(name='Code', value=code, inline=False)
embed.add_field(name='Original URL', value=original_url, inline=False)
await interaction.response.send_message(embed=embed)
else:
await interaction.response.send_message('Short code not found', ephemeral=True)
@app_commands.command(name='listshort', description='List all shortened URLs')
async def list_short(self, interaction: discord.Interaction):
guild_data = self.get_guild_data(interaction.guild_id)
if not guild_data['urls']:
await interaction.response.send_message('No shortened URLs found', ephemeral=True)
return
embed = discord.Embed(title='Shortened URLs', color=0x5865F2)
count = 0
for code, url in guild_data['urls'].items():
if count >= 25:
break
shortened = f"https://{self.domain}/{code}"
embed.add_field(
name=code,
value=f"{url[:50]}{'...' if len(url) > 50 else ''}\n{shortened}",
inline=False
)
count += 1
if len(guild_data['urls']) > 25:
embed.set_footer(text=f'Showing 25 of {len(guild_data["urls"])} URLs')
await interaction.response.send_message(embed=embed)
@app_commands.command(name='deleteshort', description='Delete a shortened URL')
@app_commands.describe(code='The short code to delete')
async def delete_short(self, interaction: discord.Interaction, code: str):
guild_data = self.get_guild_data(interaction.guild_id)
if code in guild_data['urls']:
url = guild_data['urls'][code]
del guild_data['urls'][code]
self.bot.db.save()
embed = discord.Embed(title='URL Deleted', color=0x5865F2)
embed.add_field(name='Code', value=code, inline=False)
embed.add_field(name='Original URL', value=url, inline=False)
await interaction.response.send_message(embed=embed)
else:
await interaction.response.send_message('Short code not found', ephemeral=True)
async def setup(bot):
await bot.add_cog(Utility(bot))

23
config.toml Normal file
View file

@ -0,0 +1,23 @@
# this is the deafult config file
[bot]
data_file = "data.json"
# this allows you to pick your cogs if installing multiple
enabled_cogs = [
"cogs.leveling",
"cogs.system",
"cogs.economy",
"cogs.fun",
"cogs.utility"
]
# this might not even need a comment but xp
[xp]
min = 15
max = 25
cooldown = 60
per_level = 100
level_up_multiplier = 10
# port and how many times it checks for videos
[web]
video_check_interval = 300
port = 3000

81
oldreadme.md Normal file
View file

@ -0,0 +1,81 @@
# NOT UPDATED!
<div align="center">
<img src="https://files.catbox.moe/zuj0ob.png" alt="Booly" width="100%">
<img src="https://img.shields.io/badge/discord.py-2.0+-5865F2?style=flat&logo=discord&logoColor=white" alt="discord.py">
<img src="https://img.shields.io/badge/Python-3.8+-3776AB?style=flat&logo=python&logoColor=white" alt="python">
</div>
# Overview
Booly the bot your server deserves.
# Installation
there is 2 methods of install:
1. <s>prehosted 100 servers limit running on digital ocean (1 GB Memory / 1 Intel vCPU / 35 GB Disk) </s> Prehosted is down I can't be bothered to pay tax on it.
2. self host it
## Prehosted
[__Click me for invite__](__https://discord.com/oauth2/authorize?client_id=1466398693383995558&permissions=8&integration_type=0&scope=bot+applications.commands__)
notes: until I give discord my id 100 servers can have this more may be added.
## Self hosting
I recommended using the main stable repo (https://github.com/chersbobers/booly) for yours but the nightly branch is usable too (https://github.com/chersbobers/booly/tree/nightly)
### what you need
A server I reccomend render with uptimerobot (note: Oregen and singapore servers are ip banned for me they might not be for you)
Also a discord bot with Presence Intent, Server Members Intent and Message Content Intent
Envs:
DISCORD_TOKEN (your bot token)
PORT 3000
For the invite link it just needs bot and applications.commands
# Commands
## Leveling & Economy
- `/rank` - View your rank and progress
- `/leaderboard` - See the top 10 users
- `/balance` - Check your balance
- `/daily` - Claim your daily reward
- `/work` - Work for coins
## Fun Commands
- `/8ball` - Ask the magic 8ball a question
- `/roll` - Roll dice
- `/flip` - Flip a coin
- `/cat` - Get a random cat picture
- `/dog` - Get a random dog picture
## System & Moderation
- `/kick` - Kick a member from the server
- `/ban` - Ban a member from the server
- `/unban` - Unban a user
- `/timeout` - Timeout a member
- `/warn` - Warn a member
- `/warnings` - View warnings for a user
- `/clearwarnings` - Clear warnings for a user
- `/purge` - Delete multiple messages
- `/lock` - Lock a channel
- `/unlock` - Unlock a channel
## Reaction Roles & YouTube
- `/reactionrole` - Create a reaction role
- `/removereactionrole` - Remove a reaction role
- `/listreactionroles` - List all reaction roles
- `/createreactionpanel` - Create a reaction role panel
- `/setupyoutube` - Setup YouTube notifications
- `/toggleyoutube` - Toggle YouTube notifications on/off
- `/youtubestatus` - Check YouTube notification status
- `/testlastvideo` - Test the last video notification
## Utility
- `/shorten` - Shorten a long URL with optional custom code
- `/expand` - Get the original URL from a short code
- `/listshort` - List all shortened URLs in the server
- `/deleteshort` - Delete a shortened URL by code
## Info Commands
- `/ping` - Check bot latency
- `/serverinfo` - Get information about the server
- `/userinfo` - Get information about a user

View file

@ -1,12 +0,0 @@
services:
- type: worker
name: tooly-bot
runtime: docker
plan: free
region: oregon
branch: main
dockerfilePath: ./Dockerfile
envVars:
- key: DISCORD_TOKEN
sync: false
autoDeploy: true