diff --git a/BotUpdate/cogs/admin.py b/BotUpdate/cogs/admin.py deleted file mode 100644 index 8d994bd..0000000 --- a/BotUpdate/cogs/admin.py +++ /dev/null @@ -1,69 +0,0 @@ -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history -import sys -import os - -class Admin(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - @commands.has_permissions(administrator=True) - async def shutdown(self, ctx): - add_history(ctx.author.id, "shutdown") - embed = discord.Embed( - title=f"{self.config['bot_name']} Shutting Down", - description=f"{self.config['bot_name']} is now offline.", - color=self.color_manager.get_color("Red"), - timestamp=ctx.message.created_at - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - await self.bot.close() - - @commands.command() - @commands.has_permissions(administrator=True) - async def start(self, ctx): - add_history(ctx.author.id, "start") - await self.bot.change_presence( - status=discord.Status.online, - activity=discord.Game(name=f"Run {self.config['defaults']['prefix']}help for help") - ) - embed = discord.Embed( - title=f"{self.config['bot_name']} Starting Up", - description=f"{self.config['bot_name']} is now online.", - color=self.color_manager.get_color("Blue"), - timestamp=ctx.message.created_at - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - @commands.has_permissions(administrator=True) - async def restart(self, ctx): - add_history(ctx.author.id, "restart") - embed = discord.Embed( - title="Restarting", - description="The restart will take approximately 10 to 30 seconds on average.", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - await self.bot.close() - os.execv(sys.executable, ['python'] + sys.argv) - -async def setup(bot): - await bot.add_cog(Admin(bot)) \ No newline at end of file diff --git a/BotUpdate/cogs/events.py b/BotUpdate/cogs/events.py deleted file mode 100644 index 1959122..0000000 --- a/BotUpdate/cogs/events.py +++ /dev/null @@ -1,95 +0,0 @@ -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -import datetime - -class Events(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.Cog.listener() - async def on_message_delete(self, message): - if message.author == self.bot.user: - return - - log_channel_id = self.config.get("log_channel_id") - if not log_channel_id: - return - - channel = self.bot.get_channel(int(log_channel_id)) - if not channel: - return - - embed = discord.Embed( - title="Message Deleted", - color=self.color_manager.get_color("Red"), - timestamp=datetime.datetime.utcnow() - ) - embed.add_field(name="Author", value=message.author.mention) - embed.add_field(name="Channel", value=message.channel.mention) - embed.add_field(name="Content", value=message.content or "No content") - - if message.attachments: - embed.add_field( - name="Attachments", - value="\n".join([a.url for a in message.attachments]) - ) - - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await channel.send(embed=embed) - - @commands.Cog.listener() - async def on_message_edit(self, before, after): - if before.author == self.bot.user: - return - - if before.content == after.content: - return - - log_channel_id = self.config.get("log_channel_id") - if not log_channel_id: - return - - channel = self.bot.get_channel(int(log_channel_id)) - if not channel: - return - - embed = discord.Embed( - title="Message Edited", - color=self.color_manager.get_color("Orange"), - timestamp=datetime.datetime.utcnow() - ) - embed.add_field(name="Author", value=before.author.mention) - embed.add_field(name="Channel", value=before.channel.mention) - embed.add_field(name="Before", value=before.content or "No content") - embed.add_field(name="After", value=after.content or "No content") - - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await channel.send(embed=embed) - - @commands.Cog.listener() - async def on_command_error(self, ctx, error): - if isinstance(error, commands.CommandNotFound): - return - - if isinstance(error, commands.MissingPermissions): - await ctx.send("You don't have the required permissions to use this command.") - return - - if isinstance(error, commands.MissingRequiredArgument): - await ctx.send(f"Missing required argument: {error.param}") - return - - # For other types of errors, you might want to log them - print(f"An error occurred: {error}") - -async def setup(bot): - await bot.add_cog(Events(bot)) \ No newline at end of file diff --git a/BotUpdate/cogs/fun.py b/BotUpdate/cogs/fun.py deleted file mode 100644 index 7cecc23..0000000 --- a/BotUpdate/cogs/fun.py +++ /dev/null @@ -1,61 +0,0 @@ -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history -from utils.helper import get_char_image -import unicodedata -import os - -class Fun(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - async def charinfo(self, ctx, *, characters: str): - add_history(ctx.author.id, "charinfo", [characters]) - if len(characters) > 1: - await ctx.send("Please provide only one character for information.") - return - - char = characters[0] - unicode_value = ord(char) - char_name = unicodedata.name(char, "Could not find name!") - char_category = unicodedata.category(char) - - unicode_escape = f"\\u{unicode_value:04x}" - unicode_escape_full = f"\\U{unicode_value:08x}" - python_escape = repr(char) - - embed = discord.Embed( - color=self.color_manager.get_color("Blue"), - title="Character info", - description=f"Information on character: {char}" - ) - - embed.add_field(name="Original character", value=char, inline=True) - embed.add_field(name="Character name", value=char_name, inline=True) - embed.add_field(name="Character category", value=char_category, inline=True) - embed.add_field(name="Unicode value", value=f"U+{unicode_value:04X}", inline=True) - embed.add_field(name="Unicode escape", value=unicode_escape, inline=True) - embed.add_field(name="Full Unicode escape", value=unicode_escape_full, inline=True) - embed.add_field(name="Python escape", value=python_escape, inline=True) - - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - - image_path = get_char_image(char) - - if image_path: - file = discord.File(image_path, filename="character.png") - embed.set_thumbnail(url="attachment://character.png") - await ctx.send(embed=embed, file=file) - os.remove(image_path) - else: - await ctx.send(embed=embed) - -async def setup(bot): - await bot.add_cog(Fun(bot)) \ No newline at end of file diff --git a/BotUpdate/cogs/md.md b/BotUpdate/cogs/md.md deleted file mode 100644 index 0222207..0000000 --- a/BotUpdate/cogs/md.md +++ /dev/null @@ -1,70 +0,0 @@ -### Approach to Developing a 2FA Email Sending Project Using Custom Domain Email - -#### **Objective:** -You aim to develop a test project that sends Two-Factor Authentication (2FA) emails using Python, leveraging a custom email address with a custom domain (`nerd-bear.org`). The current setup involves GoDaddy for domain registration, Cloudflare for DNS management, and free email forwarding to a Gmail account. However, this setup does not support sending emails directly from the custom domain email address. - -#### **Requirements:** -- **Primary Requirement:** Send emails from the custom domain email address (`nerd-bear.org`) for the 2FA project. -- **Constraints:** The current setup does not support sending emails directly from the custom domain email address. - -#### **Proposed Solution:** - -1. **Set Up a Custom Email Service:** - - **Option 1: Use a Third-Party Email Service Provider (ESP):** - - **Service Providers:** Consider using services like **SendGrid**, **Mailgun**, or **Amazon SES** (Simple Email Service). - - **Steps:** - 1. **Sign Up:** Create an account with the chosen ESP. - 2. **Domain Verification:** Verify your domain (`nerd-bear.org`) with the ESP to ensure you can send emails from it. This usually involves adding DNS records provided by the ESP to your Cloudflare DNS settings. - 3. **API Integration:** Use the ESP's API to send emails from your Python application. Most ESPs provide Python libraries or SDKs for easy integration. - 4. **Custom Email Address:** Configure the ESP to send emails from your custom domain email address (e.g., `noreply@nerd-bear.org`). - - **Example with SendGrid:** - ```python - from sendgrid import SendGridAPIClient - from sendgrid.helpers.mail import Mail - - message = Mail( - from_email='noreply@nerd-bear.org', - to_emails='user@example.com', - subject='Your 2FA Code', - html_content='Your code is: 123456') - - try: - sg = SendGridAPIClient('YOUR_SENDGRID_API_KEY') - response = sg.send(message) - print(response.status_code, response.body, response.headers) - except Exception as e: - print(e.message) - ``` - - - **Option 2: Set Up a Mail Server:** - - **Service Providers:** Consider using **Mailcow**, **Mailu**, or **Postfix/Dovecot** to set up your own mail server. - - **Steps:** - 1. **Server Setup:** Deploy a mail server on a VPS (Virtual Private Server) or a dedicated server. - 2. **Domain Configuration:** Configure the mail server to use your custom domain (`nerd-bear.org`). This involves setting up DNS records (MX, SPF, DKIM, DMARC) in Cloudflare. - 3. **Email Sending:** Use Python's `smtplib` library to send emails from your custom domain email address. - - **Example with SMTP:** - ```python - import smtplib - from email.mime.text import MIMEText - - msg = MIMEText('Your code is: 123456') - msg['Subject'] = 'Your 2FA Code' - msg['From'] = 'noreply@nerd-bear.org' - msg['To'] = 'user@example.com' - - with smtplib.SMTP('your-mail-server.com', 587) as server: - server.starttls() - server.login('noreply@nerd-bear.org', 'your-email-password') - server.sendmail('noreply@nerd-bear.org', 'user@example.com', msg.as_string()) - ``` - -2. **Leverage Existing Infrastructure:** - - **Cloudflare:** Ensure that Cloudflare is configured to allow outbound SMTP traffic if you choose to set up your own mail server. - - **GoDaddy:** Verify that GoDaddy is correctly pointing to Cloudflare for DNS management. - -3. **Security Considerations:** - - **API Keys:** If using an ESP, store API keys securely (e.g., environment variables, encrypted storage). - - **Email Authentication:** Ensure that your emails are authenticated using SPF, DKIM, and DMARC to prevent them from being marked as spam. - -#### **Conclusion:** -The best approach depends on your specific needs, such as the volume of emails, budget, and technical expertise. Using a third-party ESP like SendGrid or Mailgun is generally easier and more scalable, while setting up your own mail server offers more control but requires more technical effort. diff --git a/BotUpdate/cogs/moderation.py b/BotUpdate/cogs/moderation.py deleted file mode 100644 index 85e8db6..0000000 --- a/BotUpdate/cogs/moderation.py +++ /dev/null @@ -1,127 +0,0 @@ -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history -import datetime - -class Moderation(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - @commands.has_permissions(kick_members=True) - async def kick(self, ctx, member: discord.Member, *, reason="No reason provided"): - add_history(ctx.author.id, "kick", [str(member.id), reason]) - try: - await member.send( - embed=discord.Embed( - title="You've Been Kicked", - description=f"You were kicked from {ctx.guild.name}.\nReason: {reason}", - color=self.color_manager.get_color("Red") - ) - ) - except: - pass # Unable to DM the user - - await member.kick(reason=reason) - embed = discord.Embed( - title="User Kicked", - description=f"{member.mention} has been kicked.\nReason: {reason}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - @commands.has_permissions(ban_members=True) - async def ban(self, ctx, member: discord.Member, *, reason="No reason provided"): - add_history(ctx.author.id, "ban", [str(member.id), reason]) - try: - await member.send( - embed=discord.Embed( - title="You've Been Banned", - description=f"You were banned from {ctx.guild.name}.\nReason: {reason}", - color=self.color_manager.get_color("Red") - ) - ) - except: - pass # Unable to DM the user - - await member.ban(reason=reason) - embed = discord.Embed( - title="User Banned", - description=f"{member.mention} has been banned.\nReason: {reason}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - @commands.has_permissions(ban_members=True) - async def unban(self, ctx, *, member): - add_history(ctx.author.id, "unban", [member]) - banned_users = await ctx.guild.bans() - member_name, member_discriminator = member.split('#') - - for ban_entry in banned_users: - user = ban_entry.user - if (user.name, user.discriminator) == (member_name, member_discriminator): - await ctx.guild.unban(user) - embed = discord.Embed( - title="User Unbanned", - description=f"{user.mention} has been unbanned.", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - return - - await ctx.send(f"User {member} not found in ban list.") - - @commands.command() - @commands.has_permissions(manage_messages=True) - async def timeout(self, ctx, member: discord.Member, duration: int, unit, *, reason="No reason provided"): - add_history(ctx.author.id, "timeout", [str(member.id), str(duration), unit, reason]) - time_units = {"s": "seconds", "m": "minutes", "h": "hours", "d": "days"} - if unit not in time_units: - await ctx.send("Invalid time unit. Use 's' for seconds, 'm' for minutes, 'h' for hours, or 'd' for days.") - return - - time_delta = datetime.timedelta(**{time_units[unit]: duration}) - await member.timeout(time_delta, reason=reason) - - embed = discord.Embed( - title="User Timed Out", - description=f"{member.mention} has been timed out for {duration}{unit}.\nReason: {reason}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - try: - await member.send( - embed=discord.Embed( - title="You were timed out", - description=f"You have been timed out in {ctx.guild.name} for {duration}{unit}.\nReason: {reason}", - color=self.color_manager.get_color("Red") - ) - ) - except: - pass # Unable to DM the user - -async def setup(bot): - await bot.add_cog(Moderation(bot)) \ No newline at end of file diff --git a/BotUpdate/cogs/music.py b/BotUpdate/cogs/music.py deleted file mode 100644 index d3b5470..0000000 --- a/BotUpdate/cogs/music.py +++ /dev/null @@ -1,76 +0,0 @@ -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history -import yt_dlp - -class Music(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - async def join(self, ctx): - add_history(ctx.author.id, "join") - if ctx.author.voice is None: - await ctx.send("You need to be in a voice channel to use this command.") - return - - channel = ctx.author.voice.channel - await channel.connect() - await ctx.send(f"Joined {channel.name}") - - @commands.command() - async def leave(self, ctx): - add_history(ctx.author.id, "leave") - if ctx.voice_client is None: - await ctx.send("I'm not in a voice channel.") - return - - await ctx.voice_client.disconnect() - await ctx.send("Left the voice channel") - - @commands.command() - async def play(self, ctx, *, query): - add_history(ctx.author.id, "play", [query]) - if ctx.voice_client is None: - if ctx.author.voice: - await ctx.author.voice.channel.connect() - else: - await ctx.send("You are not connected to a voice channel.") - return - - async with ctx.typing(): - try: - with yt_dlp.YoutubeDL({"format": "bestaudio", "noplaylist": "True"}) as ydl: - info = ydl.extract_info(query, download=False) - URL = info["url"] - title = info["title"] - except Exception as e: - await ctx.send(f"An error occurred: {str(e)}") - return - - if ctx.voice_client.is_playing(): - ctx.voice_client.stop() - - FFMPEG_OPTIONS = { - 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', - 'options': '-vn' - } - audio_source = discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS) - ctx.voice_client.play(audio_source) - - embed = discord.Embed( - title="Now Playing", - description=f"Now playing: {title}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - -async def setup(bot): - await bot.add_cog(Music(bot)) \ No newline at end of file diff --git a/BotUpdate/cogs/utility.py b/BotUpdate/cogs/utility.py deleted file mode 100644 index f11dbdc..0000000 --- a/BotUpdate/cogs/utility.py +++ /dev/null @@ -1,142 +0,0 @@ -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history, add_feedback -from utils.helper import fetch_latency, text_to_speech -import os -import asyncio -from deep_translator import GoogleTranslator - -class Utility(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - async def ping(self, ctx): - add_history(ctx.author.id, "ping") - latency = fetch_latency(self.bot) - embed = discord.Embed( - title="Bot Latency", - description=f"The current bot latency is approximately `{latency}ms`", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - async def profile(self, ctx, member: discord.Member = None): - member = member or ctx.author - add_history(ctx.author.id, "profile", [str(member.id)]) - - embed = discord.Embed( - title=f"{member.name}'s Profile", - description="User's public Discord information", - color=self.color_manager.get_color("Blue") - ) - embed.add_field(name="Display Name", value=member.display_name, inline=True) - embed.add_field(name="User ID", value=member.id, inline=True) - embed.add_field(name="Account Created", value=member.created_at.strftime("%d/%m/%y %H:%M:%S"), inline=True) - embed.add_field(name="Joined Server", value=member.joined_at.strftime("%d/%m/%y %H:%M:%S"), inline=True) - embed.add_field(name="Top Role", value=member.top_role.mention, inline=True) - embed.add_field(name="Status", value=str(member.status).title(), inline=True) - - if member.avatar: - embed.set_thumbnail(url=member.avatar.url) - - await ctx.send(embed=embed) - - @commands.command() - async def feedback(self, ctx, *, message): - add_history(ctx.author.id, "feedback", [message]) - if add_feedback(str(ctx.author.id), message): - embed = discord.Embed( - title="Feedback Received", - description="Thank you for your feedback!", - color=self.color_manager.get_color("Green") - ) - else: - embed = discord.Embed( - title="Feedback Error", - description="There was an error submitting your feedback. Please try again later.", - color=self.color_manager.get_color("Red") - ) - - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - async def tts(self, ctx, *, text): - add_history(ctx.author.id, "tts", [text]) - if not ctx.author.voice: - await ctx.send("You need to be in a voice channel to use this command.") - return - - output_file = f"./temp/audio/tts_{ctx.message.id}.mp3" - - try: - text_to_speech(text, output_file, self.config.get("tts_mode", "normal")) - except Exception as e: - await ctx.send(f"An error occurred while generating the TTS: {str(e)}") - return - - voice_channel = ctx.author.voice.channel - if ctx.voice_client is None: - vc = await voice_channel.connect() - else: - vc = ctx.voice_client - - if vc.is_playing(): - vc.stop() - - vc.play( - discord.FFmpegPCMAudio(source=output_file), - after=lambda e: asyncio.run_coroutine_threadsafe(vc.disconnect(), self.bot.loop) - ) - - while vc.is_playing(): - await asyncio.sleep(1) - - if os.path.exists(output_file): - os.remove(output_file) - - embed = discord.Embed( - title="TTS Completed", - description=f"Successfully played TTS in {voice_channel.mention}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - async def translate(self, ctx, *, text): - add_history(ctx.author.id, "translate", [text]) - try: - translator = GoogleTranslator(source="auto", target="en") - translated_text = translator.translate(text) - - embed = discord.Embed( - title="Translation", - description=translated_text, - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - except Exception as e: - await ctx.send(f"An error occurred during translation: {str(e)}") - -async def setup(bot): - await bot.add_cog(Utility(bot)) \ No newline at end of file diff --git a/BotUpdate/config.json b/BotUpdate/config.json deleted file mode 100644 index a2c15ac..0000000 --- a/BotUpdate/config.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "defaults": { - "prefix": "?", - "footer_text": "This bot is created and hosted by Nerd bear", - "footer_icon": "https://as2.ftcdn.net/v2/jpg/01/17/00/87/1000_F_117008730_0Dg5yniuxPQLz3shrJvLIeBsPfPRBSE1.jpg" - }, - - "bot_version": "0.4.6", - "bot_name": "Memo", - "tts_mode":"fast", - "log_channel_id": "1290060885485948950", - "tts_detector_factory_seed": "0", - "token": "token", - - "colors": { - "Red": "#FFB3BA", - "Coral": "#FFCCB6", - "Orange": "#FFE5B4", - "Gold": "#FFF1B5", - "Yellow": "#FFFFD1", - "Lime": "#DCFFB8", - "Green": "#BAFFC9", - "Teal": "#B5EAD7", - "Cyan": "#C7F2FF", - "Blue": "#B5DEFF", - "Navy": "#C5CAE9", - "Purple": "#D0B8FF", - "Magenta": "#F2B5D4", - "Pink": "#FFCCE5", - "Gray": "#E0E0E0", - "Lavender": "#E6E6FA" - }, - - "guilds": { - "1288144110880030795": { - "prefix": "*" - } - } -} \ No newline at end of file diff --git a/BotUpdate/data/crac.db b/BotUpdate/data/crac.db deleted file mode 100644 index e69de29..0000000 diff --git a/BotUpdate/logs/bot.log b/BotUpdate/logs/bot.log deleted file mode 100644 index e69de29..0000000 diff --git a/BotUpdate/main.py b/BotUpdate/main.py deleted file mode 100644 index 0bca7dc..0000000 --- a/BotUpdate/main.py +++ /dev/null @@ -1,36 +0,0 @@ -import os -import discord -from discord.ext import commands -import json -import logging -from utils.config_manager import load_config - -intents = discord.Intents.all() -config = load_config() - -bot = commands.Bot(command_prefix=config['defaults']['prefix'], intents=intents) - -@bot.event -async def on_ready(): - print(f'Logged in as {bot.user.name} (ID: {bot.user.id})') - print('------') - -async def load_cogs(): - for filename in os.listdir('./cogs'): - if filename.endswith('.py'): - try: - await bot.load_extension(f'cogs.{filename[:-3]}') - print(f'Loaded cog: {filename[:-3]}') - except Exception as e: - print(f'Failed to load cog {filename[:-3]}: {str(e)}') - -async def main(): - logging.basicConfig(filename='logs/bot.log', level=logging.INFO, - format='%(asctime)s:%(levelname)s:%(message)s') - - await load_cogs() - await bot.start(config['token']) - -if __name__ == '__main__': - import asyncio - asyncio.run(main()) \ No newline at end of file diff --git a/BotUpdate/requirements.txt b/BotUpdate/requirements.txt deleted file mode 100644 index 233d67b..0000000 --- a/BotUpdate/requirements.txt +++ /dev/null @@ -1,118 +0,0 @@ -aiohappyeyeballs == 2.4.3 -aiohttp == 3.10.8 -aiosignal == 1.3.1 -aiosqlite == 0.20.0 -annotated-types == 0.7.0 -anyio == 4.6.0 -async-timeout == 4.0.3 -attrs == 24.2.0 -beautifulsoup4 == 4.12.3 -blinker == 1.8.2 -Brotli == 1.1.0 -certifi == 2024.8.30 -cffi == 1.17.1 -charset-normalizer == 3.3.2 -click == 8.1.7 -colorama == 0.4.6 -comtypes == 1.4.7 -cryptography == 43.0.1 -dataclasses-json == 0.6.7 -deep-translator == 1.11.4 -Deprecated == 1.2.14 -discord == 2.3.2 -discord.py == 2.4.0 -disnake == 2.9.2 -distro == 1.9.0 -duckduckgo_search == 6.2.13 -edge-tts == 6.1.12 -fastapi == 0.115.0 -ffmpeg-python == 0.2.0 -Flask == 3.0.3 -Flask-Cors == 5.0.0 -frozenlist == 1.4.1 -future == 1.0.0 -gevent == 24.2.1 -greenlet == 3.1.1 -groq == 0.11.0 -gTTS == 2.5.3 -h11 == 0.14.0 -httpcore == 1.0.5 -httpx == 0.27.2 -idna == 3.10 -itsdangerous == 2.2.0 -Jinja2 == 3.1.4 -joblib == 1.4.2 -jsonpatch == 1.33 -jsonpointer == 3.0.0 -langchain == 0.3.1 -langchain-community == 0.3.1 -langchain-core == 0.3.6 -langchain-groq == 0.2.0 -langchain-ollama == 0.2.0 -langchain-text-splitter == 0.3.0 -langdetect == 1.0.9 -langsmith == 0.1.129 -lxml == 5.3.0 -lyrics-extractor == 3.0.1 -lyricsgenius == 3.0.1 -markdown-it-py == 3.0.0 -MarkupSafe == 2.1.5 -marshmallow == 3.22.0 -mdurl == 0.1.2 -multidict == 6.1.0 -mutagen == 1.47.0 -mypy-extensions == 1.0.0 -nltk == 3.9.1 -numpy == 1.26.4 -ollama == 0.3.3 -orjson == 3.10.7 -packaging == 24.1 -pillow == 10.4.0 -primp == 0.6.3 -pycparser == 2.22 -pycryptodomex == 3.20.0 -pydantic == 2.9.2 -pydantic-settings == 2.5.2 -pydantic_core == 2.23.4 -PyGithub == 2.4.0 -Pygments == 2.18.0 -PyJWT == 2.9.0 -PyNaCl == 1.5.0 -pypiwin32 == 223 -PySide6 == 6.7.3 -PySide6_Addons == 6.7.3 -PySide6_Essentials == 6.7.3 -python-dotenv == 1.0.1 -pyttsx3 == 2.98 -pytube == 15.0.0 -pywin32 == 306 -PyYAML == 6.0.2 -redis == 5.1.0 -regex == 2024.9.11 -requests == 2.32.3 -rich == 13.8.1 -shiboken6 == 6.7.3 -six == 1.16.0 -sniffio == 1.3.1 -soupsieve == 2.6 -spotipy == 2.24.0 -SQLAlchemy == 2.0.35 -starlette == 0.38.6 -tenacity == 8.5.0 -timeout == 0.1.2 -tqdm == 4.66.5 -typing-inspect == 0.9.0 -typing_extensions == 4.12.2 -urllib3 == 2.2.3 -uvicorn == 0.31.0 -websocket == 0.2.1 -websocket-client == 1.8.0 -websockets == 13.1 -Werkzeug == 3.0.4 -wikipedia == 1.4.0 -wrapt == 1.16.0 -yarl == 1.13.1 -youtube-transcript-api == 0.6.2 -yt-dlp == 2024.9.27 -zope.event == 5.0 -zope.interface == 7.0.3 \ No newline at end of file diff --git a/BotUpdate/utils/__init__.py b/BotUpdate/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/BotUpdate/utils/__pycache__/__init__.cpython-312.pyc b/BotUpdate/utils/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index b9442b5..0000000 Binary files a/BotUpdate/utils/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/BotUpdate/utils/__pycache__/config_manager.cpython-312.pyc b/BotUpdate/utils/__pycache__/config_manager.cpython-312.pyc deleted file mode 100644 index fc67717..0000000 Binary files a/BotUpdate/utils/__pycache__/config_manager.cpython-312.pyc and /dev/null differ diff --git a/BotUpdate/utils/config_manager.py b/BotUpdate/utils/config_manager.py deleted file mode 100644 index 43c7b54..0000000 --- a/BotUpdate/utils/config_manager.py +++ /dev/null @@ -1,41 +0,0 @@ -import discord -from src.utils.helper import * - - -def load_config(config_path: str = "config.json") -> dict: - try: - with open(config_path, "r") as f: - return json.load(f) - except FileNotFoundError: - print(f"WARNING: Config file not found at {config_path}. Using default configuration.") - return {"default_prefix": "?", "guilds": {}} - except json.JSONDecodeError: - print(f"ERROR: Invalid JSON in config file {config_path}. Using default configuration.") - return {"default_prefix": "?", "guilds": {}} - -def save_config(config: dict, config_path: str = "config.json") -> None: - try: - with open(config_path, "w") as f: - json.dump(config, f, indent=4) - except IOError as e: - print(f"ERROR: Failed to save config to {config_path}: {e}") - -class ColorManager: - def __init__(self, config: dict): - self.colors = config.get("colors", {}) - - def get_color(self, color_name: str) -> int: - if color_name not in self.colors: - raise ValueError(f"Color '{color_name}' not found in configuration") - return int(self.colors[color_name].lstrip("#"), 16) - - def list_colors(self) -> list: - return list(self.colors.keys()) - - def create_color_embed(self, title: str, description: str, color_name: str) -> discord.Embed: - try: - color = self.get_color(color_name) - return discord.Embed(title=title, description=description, color=color) - except ValueError as e: - print(f"ERROR: Failed to create color embed: {e}") - return discord.Embed(title=title, description=description) \ No newline at end of file diff --git a/BotUpdate/utils/db_manager.py b/BotUpdate/utils/db_manager.py deleted file mode 100644 index 5afced8..0000000 --- a/BotUpdate/utils/db_manager.py +++ /dev/null @@ -1,55 +0,0 @@ -import sqlite3 -import datetime -import json - -DB_PATH = "./data/memo.db" - -def get_db_connection(): - return sqlite3.connect(DB_PATH) - -def execute_query(query: str, params: tuple = (), fetch: bool = False): - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute(query, params) - if fetch: - return cursor.fetchall() - conn.commit() - return True - -def add_feedback(user_id: str, message: str) -> bool: - datetime_value = datetime.datetime.now().strftime("%S:%M:%H %d/%m/%y") - query = "INSERT INTO feedback VALUES (?, ?, ?)" - return execute_query(query, (user_id, message, datetime_value)) - -def add_history(user_id: int, command: str, arguments: list[str] = ["none"]) -> bool: - args_json = json.dumps(arguments) - datetime_value = datetime.datetime.now().strftime("%S:%M:%H %d/%m/%y") - query = "INSERT INTO history (user_id, command, arguments, datetime) VALUES (?, ?, ?, ?)" - return execute_query(query, (user_id, command, args_json, datetime_value)) - -def get_usage(command_name: str) -> tuple[str, str, str]: - query = "SELECT command_name, arguments, level FROM usage WHERE command_name = ?" - result = execute_query(query, (command_name,), fetch=True) - return result[0] if result else None - -def get_all_usages() -> list[tuple[str, str, str]]: - query = "SELECT command_name, arguments, level FROM usage" - return execute_query(query, fetch=True) - -def add_usage(command_name: str, arguments: list[str], level: int) -> bool: - if not 0 <= level <= 3: - raise ValueError("Level must be between 0 and 3") - query = "INSERT INTO usage (command_name, arguments, level) VALUES (?, ?, ?)" - args_json = json.dumps(arguments) - return execute_query(query, (command_name, args_json, str(level))) - -def create_tables(): - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute("CREATE TABLE IF NOT EXISTS feedback (user_id TEXT, message TEXT, datetime TEXT)") - cursor.execute("CREATE TABLE IF NOT EXISTS history (user_id INTEGER, command TEXT, arguments TEXT, datetime TEXT)") - cursor.execute("CREATE TABLE IF NOT EXISTS usage (command_name TEXT, arguments TEXT, level TEXT)") - conn.commit() - -# Call this function when initializing the bot to ensure tables exist -create_tables() \ No newline at end of file diff --git a/BotUpdate/utils/helper.py b/BotUpdate/utils/helper.py deleted file mode 100644 index 89eaf91..0000000 --- a/BotUpdate/utils/helper.py +++ /dev/null @@ -1,54 +0,0 @@ -import datetime -import tempfile -from PIL import Image, ImageDraw, ImageFont -from langdetect import detect, DetectorFactory -from langdetect.lang_detect_exception import LangDetectException -from collections import Counter -from gtts import gTTS -import discord - -def set_langdetect_seed(seed: int = 0): - DetectorFactory.seed = seed - -def text_to_speech(text: str, output_file: str, tts_mode: str) -> None: - try: - slow = tts_mode.lower() == "slow" - language = detect(text) - tts = gTTS(text=text, lang=language, slow=slow) - tts.save(output_file) - except Exception as e: - print(f"ERROR_LOG ~ Text-to-speech conversion failed: {e}") - -def log_info(value: str = "None") -> None: - timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - print(f"{timestamp} [INFO] {value}") - -def fetch_latency(client: discord.Client, shouldRound: bool = True) -> float: - latency = client.latency * 1000 - return round(latency) if shouldRound else latency - -def get_char_image(char: str, bg: str = "white", fg: str = "black", format: str = "png") -> str: - try: - img = Image.new("RGB", (200, 200), color=bg) - d = ImageDraw.Draw(img) - try: - font = ImageFont.truetype("arial.ttf", 120) - except IOError: - font = ImageFont.load_default() - d.text((100, 100), char, font=font, fill=fg, anchor="mm") - with tempfile.NamedTemporaryFile(delete=False, suffix=f".{format}") as temp_file: - img.save(temp_file, format=format.upper()) - temp_file_path = temp_file.name - return temp_file_path - except Exception as e: - print(f"ERROR: Failed to generate character image: {e}") - return None - -def detect_language(text: str) -> str: - try: - detections = [detect(text) for _ in range(5)] - most_common = Counter(detections).most_common(1)[0][0] - return most_common - except LangDetectException as e: - print(f"ERROR: Language detection failed: {e}") - return "unknown" \ No newline at end of file diff --git a/README.md b/README.md index e22bb66..5f726d3 100644 --- a/README.md +++ b/README.md @@ -1,228 +1,175 @@ -# Memo Bot - -## Table of Contents -1. [Introduction](#introduction) -2. [Features](#features) -3. [Commands](#commands) -4. [Installation](#installation) -5. [Usage](#usage) -6. [Configuration](#configuration) -7. [Project Structure](#project-structure) -8. [Dependencies](#dependencies) -9. [Development](#development) -10. [Contributing](#contributing) -11. [License](#license) -12. [Support](#support) -13. [Creator](#creator) - -## Introduction - -Memo Bot is a versatile, all-purpose Discord bot designed to enhance server management and user interaction. Currently in active development, Memo Bot offers a wide range of features from moderation tools to fun commands, making it a valuable addition to any Discord server. - -## Features - -1. **Server Moderation** - - Kick users - - Ban and unban users - - Timeout users - - Nickname management - -2. **User Interaction** - - Character information lookup - - Text-to-speech functionality - - Music playback from YouTube - - User profile display - -3. **Bot Management** - - Customizable bot status - - Start/shutdown commands - - Feedback system - -4. **Message Handling** - - Logging of message edits and deletions - - Inappropriate word detection and filtering - -5. **Voice Channel Integration** - - Join and leave voice channels - - Play audio in voice channels - -6. **Customization** - - Configurable command prefix - - Guild-specific settings - -## Commands - -Here's a detailed list of available commands: - -1. `?help`: Displays a help message with all available commands and their usage. - -2. `?kick @user [reason]`: Kicks the mentioned user from the server. Requires kick permissions. - -3. `?ban @user [reason]`: Bans the mentioned user from the server. Requires ban permissions. - -4. `?unban @user`: Unbans the specified user from the server. Requires ban permissions. - -5. `?timeout @user [reason]`: Timeouts the mentioned user for the specified duration. Requires moderate members permission. - -6. `?shutdown`: Shuts down the bot. Requires administrator permissions. - -7. `?start`: Starts the bot if it's offline. Requires administrator permissions. - -8. `?charinfo [character]`: Provides detailed information about the specified character, including Unicode data. - -9. `?tts [message]`: Converts the given text to speech and plays it in the user's current voice channel. - -10. `?play [youtube_url]`: Plays audio from the specified YouTube video in the user's current voice channel. - -11. `?join`: Makes the bot join the user's current voice channel. - -12. `?leave`: Makes the bot leave the current voice channel. - -13. `?profile @user`: Displays detailed profile information about the mentioned user. - -14. `?nick @user [new_nickname]`: Changes the nickname of the mentioned user. Requires manage nicknames permission. - -15. `?feedback [message]`: Allows users to submit feedback about the bot, which is stored in a database. - -## Installation - -1. Clone the repository: - ``` +# Memo Discord Bot + +
+ Memo Bot Logo + + [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) + [![Version](https://img.shields.io/badge/version-0.4.6-brightgreen.svg)](https://github.com/your-username/Memo-bot/releases) + [![Python](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) + [![Discord.py](https://img.shields.io/badge/disnake-2.8+-blue.svg)](https://github.com/DisnakeDev/disnake) +
+ +## Overview + +Memo Bot is a versatile Discord bot designed to enhance server management and user interaction. From moderation tools to fun commands, Memo Bot provides a comprehensive suite of features to improve your Discord server experience. + +### Key Features + +- ๐Ÿ›ก๏ธ **Server Moderation** + - Kick/Ban management + - User timeouts + - Voice channel controls + - Nickname management + +- ๐ŸŽต **Voice Features** + - YouTube music playback + - Text-to-speech (TTS) + - Voice channel controls + +- ๐Ÿ”ง **Utility Commands** + - User profiles + - Server statistics + - Character information lookup + - Language translation + +- ๐ŸŽฎ **Fun Commands** + - Random quotes + - Dad jokes + - Coin flips + - And more! + +## Quick Start + +1. **Installation** + ```bash + # Clone the repository git clone https://github.com/your-username/Memo-bot.git - ``` - -2. Navigate to the project directory: - ``` cd Memo-bot - ``` -3. Install the required dependencies: - ``` + # Install dependencies pip install -r requirements.txt - ``` -4. Set up your Discord bot token: - - Create a new application in the [Discord Developer Portal](https://discord.com/developers/applications) - - Create a bot for your application and copy the bot token - - Create a `config.json` file in the project root and add your token: - ```json - { - "token": "YOUR_BOT_TOKEN_HERE" - } - ``` - -5. Set up the SQLite database: - ``` + # Set up the database python setup/create_feedback_table.py python setup/create_history_table.py python setup/create_usage_table.py ``` -## Usage - -1. Run the bot: +2. **Configuration** + - Create a `config.json` file in the project root: + ```json + { + "defaults": { + "prefix": "?", + "footer_text": "Your footer text", + "footer_icon": "Your footer icon URL" + }, + "bot_version": "0.4.6", + "bot_name": "Memo", + "tts_mode": "fast", + "log_channel_id": "YOUR_LOG_CHANNEL_ID" + } ``` - python launcher.py - ``` - -2. Invite the bot to your Discord server: - - Go to the OAuth2 URL generator in your Discord Developer Portal - - Select the "bot" scope and the necessary permissions - - Use the generated URL to invite the bot to your server - -3. Once the bot is in your server, use `?help` to see all available commands. - -## Configuration - -The `config.json` file contains important settings for the bot: - -```json -{ - "defaults": { - "prefix": "?" - }, - "bot_version": "0.4.5", - "bot_name": "Memo", - "tts_mode": "fast", - "guilds": { - "1288144110880030795": { - "prefix": "*" - } - } -} -``` -- `defaults.prefix`: The default command prefix for the bot. -- `bot_version`: The current version of the bot. -- `bot_name`: The name of the bot. -- `tts_mode`: The mode for text-to-speech functionality ("fast" or "slow"). -- `guilds`: Guild-specific settings, including custom prefixes. - -## Project Structure - -- `main/bot.py`: The main bot file containing command implementations and event handlers. -- `launcher.py`: The entry point for running the bot. -- `db_manager/`: Directory containing database management modules. -- `setup/`: Directory containing database setup scripts. -- `temp/audio/`: Directory for temporary audio files used by the TTS feature. -- `website/`: Directory containing HTML files for the bot's website. +3. **Launch** + ```bash + python launcher.py --token YOUR_BOT_TOKEN + ``` -## Dependencies +## Commands -Memo Bot relies on several Python libraries: +Here's a quick overview of the main commands: -- discord.py: The core library for interacting with the Discord API. -- Pillow: Used for image manipulation in the `charinfo` command. -- gTTS: Google Text-to-Speech library for the TTS feature. -- yt_dlp: YouTube downloader used for the music playback feature. -- rich: Used for console output formatting and logging. -- langdetect: Used for language detection in the TTS feature. +### Moderation +- `?kick @user [reason]` - Kick a user +- `?ban @user [reason]` - Ban a user +- `?timeout @user [reason]` - Timeout a user +- `?mute @user [reason]` - Server mute a user +- `?deafen @user [reason]` - Server deafen a user -For a complete list of dependencies, refer to the `requirements.txt` file. +### Voice & Music +- `?play [youtube_url]` - Play music from YouTube +- `?tts [text]` - Convert text to speech +- `?join` - Join voice channel +- `?leave` - Leave voice channel -## Development +### Utility +- `?profile @user` - View user profile +- `?server` - View server info +- `?translate [text]` - Translate text to English +- `?charinfo [character]` - Get character information -Memo Bot is under active development with nearly daily updates. The development process includes: +### Fun +- `?joke` - Get a random dad joke +- `?quote` - Get quote of the day +- `?coin` - Flip a coin -- Regular feature additions and improvements -- Bug fixes and performance optimizations -- Refactoring for better code organization and maintainability -- Implementation of user feedback and feature requests +For a complete list of commands, use `?help` in Discord. -Future development plans include: -- Implementing a music queue system -- Adding more fun and interactive commands -- Improving error handling and user feedback -- Enhancing the configuration system for more granular control +## Project Structure -## Contributing +``` +Memo-bot/ +โ”œโ”€โ”€ assets/ # Bot assets (logos, emojis) +โ”œโ”€โ”€ db_manager/ # Database management modules +โ”œโ”€โ”€ setup/ # Database setup scripts +โ”œโ”€โ”€ src/ # Main bot source code +โ”‚ โ”œโ”€โ”€ bot.py # Core bot implementation +โ”‚ โ””โ”€โ”€ utils/ # Utility functions +โ”œโ”€โ”€ temp/ # Temporary files +โ”œโ”€โ”€ website/ # Bot website files +โ”œโ”€โ”€ config.json # Bot configuration +โ”œโ”€โ”€ launcher.py # Bot launcher +โ”œโ”€โ”€ LICENSE # Apache 2.0 license +โ””โ”€โ”€ README.md # Project documentation +``` -We welcome contributions to Memo Bot! Here's how you can contribute: +## Development +### Requirements +- Python 3.8 or higher +- Discord Developer Account +- Required Python packages (see requirements.txt) + +### Core Dependencies +- disnake +- gTTS +- yt_dlp +- rich +- click +- deep-translator +- pillow + +### Contributing 1. Fork the repository -2. Create a new branch for your feature or bug fix -3. Make your changes and commit them with clear, descriptive commit messages -4. Push your changes to your fork -5. Submit a pull request to the main repository +2. Create your feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request -Please ensure your code adheres to the existing style conventions and includes appropriate tests. +## Support -## License +- [Documentation](https://Memo.nerd-bear.org/docs) +- [Issue Tracker](https://github.com/your-username/Memo-bot/issues) +- [Discord Support Server](https://discord.gg/your-invite) +- Email: support@nerd-bear.org -This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for the full license text. +## License -## Support +This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. -If you need help or want to report a bug, you can: +## Acknowledgments -1. Open an issue on this GitHub repository -2. Join our support Discord server (link to be added) -3. Contact us via email at Memo@nerd-bear.org +- [DisnakeDev](https://github.com/DisnakeDev/disnake) for the Discord API wrapper +- All contributors who have helped improve Memo Bot -## Creator +## Authors -Memo Bot is created and maintained by Nerd Bear. For more information about the creator and other projects, visit [nerd-bear.org](https://nerd-bear.org). +- **Nerd Bear** - *Initial work and maintenance* - [nerd-bear](https://github.com/nerd-bear) --- -Memo Bot ยฉ 2024 by Nerd Bear. All rights reserved. \ No newline at end of file +
+ Made with โค๏ธ by Nerd Bear
+ ยฉ 2024 Memo Bot. All rights reserved. +
\ No newline at end of file diff --git a/assets/emojis/webp/codebase.md b/assets/emojis/webp/codebase.md deleted file mode 100644 index 3274c74..0000000 --- a/assets/emojis/webp/codebase.md +++ /dev/null @@ -1,40 +0,0 @@ -# 0_percent_battery.webp - -This is a binary file of the type: Image - -# 25_percent_battery_blinking.webp - -This is a binary file of the type: Image - -# 50_percent_battery.webp - -This is a binary file of the type: Image - -# 75_percent_battery_blinking.webp - -This is a binary file of the type: Image - -# 75_percent_battery.webp - -This is a binary file of the type: Image - -# 100_percent_battery.webp - -This is a binary file of the type: Image - -# active_developer.webp - -This is a binary file of the type: Image - -# help_heart.webp - -This is a binary file of the type: Image - -# linked.webp - -This is a binary file of the type: Image - -# verified_developer.webp - -This is a binary file of the type: Image - diff --git a/changes.md b/changes.md deleted file mode 100644 index 53ca75f..0000000 --- a/changes.md +++ /dev/null @@ -1,7 +0,0 @@ -# Memo Bot Changelog - -## Latest Updates - -+ Added restart command -+ Updated shutdown command to disconect from all voice channels -+ Updated start command to set RPC to the help info \ No newline at end of file diff --git a/codebase.md b/codebase.md deleted file mode 100644 index 49cda20..0000000 --- a/codebase.md +++ /dev/null @@ -1,10196 +0,0 @@ -# .gitignore - -``` -.vscode -``` - -# assets\emojis\gif\0_percent_battery.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\25_percent_battery_blinking.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\50_percent_battery.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\75_percent_battery_blinking.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\75_percent_battery.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\100_percent_battery.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\active_developer.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\help_heart.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\linked.gif - -This is a binary file of the type: Image - -# assets\emojis\gif\verified_developer.gif - -This is a binary file of the type: Image - -# assets\emojis\webp\0_percent_battery.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\25_percent_battery_blinking.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\50_percent_battery.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\75_percent_battery_blinking.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\75_percent_battery.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\100_percent_battery.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\active_developer.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\help_heart.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\linked.webp - -This is a binary file of the type: Image - -# assets\emojis\webp\verified_developer.webp - -This is a binary file of the type: Image - -# assets\logo\editable\variation-1.ai - -This is a binary file of the type: Binary - -# assets\logo\editable\variation-2.ai - -This is a binary file of the type: Binary - -# assets\logo\editable\variation-3.ai - -This is a binary file of the type: Binary - -# assets\logo\editable\variation-4.ai - -This is a binary file of the type: Binary - -# assets\logo\png\bear.png - -This is a binary file of the type: Image - -# assets\logo\png\padded_bear.png - -This is a binary file of the type: Image - -# assets\logo\svg\variation-1.svg - -This is a file of the type: SVG Image - -# assets\logo\svg\variation-2.svg - -This is a file of the type: SVG Image - -# assets\logo\svg\variation-3.svg - -This is a file of the type: SVG Image - -# assets\logo\svg\variation-4.svg - -This is a file of the type: SVG Image - -# BotUpdate\cogs\__init__.py - -```py - -``` - -# BotUpdate\cogs\admin.py - -```py -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history -import sys -import os - -class Admin(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - @commands.has_permissions(administrator=True) - async def shutdown(self, ctx): - add_history(ctx.author.id, "shutdown") - embed = discord.Embed( - title=f"{self.config['bot_name']} Shutting Down", - description=f"{self.config['bot_name']} is now offline.", - color=self.color_manager.get_color("Red"), - timestamp=ctx.message.created_at - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - await self.bot.close() - - @commands.command() - @commands.has_permissions(administrator=True) - async def start(self, ctx): - add_history(ctx.author.id, "start") - await self.bot.change_presence( - status=discord.Status.online, - activity=discord.Game(name=f"Run {self.config['defaults']['prefix']}help for help") - ) - embed = discord.Embed( - title=f"{self.config['bot_name']} Starting Up", - description=f"{self.config['bot_name']} is now online.", - color=self.color_manager.get_color("Blue"), - timestamp=ctx.message.created_at - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - @commands.has_permissions(administrator=True) - async def restart(self, ctx): - add_history(ctx.author.id, "restart") - embed = discord.Embed( - title="Restarting", - description="The restart will take approximately 10 to 30 seconds on average.", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - await self.bot.close() - os.execv(sys.executable, ['python'] + sys.argv) - -async def setup(bot): - await bot.add_cog(Admin(bot)) -``` - -# BotUpdate\cogs\events.py - -```py -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -import datetime - -class Events(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.Cog.listener() - async def on_message_delete(self, message): - if message.author == self.bot.user: - return - - log_channel_id = self.config.get("log_channel_id") - if not log_channel_id: - return - - channel = self.bot.get_channel(int(log_channel_id)) - if not channel: - return - - embed = discord.Embed( - title="Message Deleted", - color=self.color_manager.get_color("Red"), - timestamp=datetime.datetime.utcnow() - ) - embed.add_field(name="Author", value=message.author.mention) - embed.add_field(name="Channel", value=message.channel.mention) - embed.add_field(name="Content", value=message.content or "No content") - - if message.attachments: - embed.add_field( - name="Attachments", - value="\n".join([a.url for a in message.attachments]) - ) - - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await channel.send(embed=embed) - - @commands.Cog.listener() - async def on_message_edit(self, before, after): - if before.author == self.bot.user: - return - - if before.content == after.content: - return - - log_channel_id = self.config.get("log_channel_id") - if not log_channel_id: - return - - channel = self.bot.get_channel(int(log_channel_id)) - if not channel: - return - - embed = discord.Embed( - title="Message Edited", - color=self.color_manager.get_color("Orange"), - timestamp=datetime.datetime.utcnow() - ) - embed.add_field(name="Author", value=before.author.mention) - embed.add_field(name="Channel", value=before.channel.mention) - embed.add_field(name="Before", value=before.content or "No content") - embed.add_field(name="After", value=after.content or "No content") - - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await channel.send(embed=embed) - - @commands.Cog.listener() - async def on_command_error(self, ctx, error): - if isinstance(error, commands.CommandNotFound): - return - - if isinstance(error, commands.MissingPermissions): - await ctx.send("You don't have the required permissions to use this command.") - return - - if isinstance(error, commands.MissingRequiredArgument): - await ctx.send(f"Missing required argument: {error.param}") - return - - # For other types of errors, you might want to log them - print(f"An error occurred: {error}") - -async def setup(bot): - await bot.add_cog(Events(bot)) -``` - -# BotUpdate\cogs\fun.py - -```py -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history -from utils.helper import get_char_image -import unicodedata -import os - -class Fun(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - async def charinfo(self, ctx, *, characters: str): - add_history(ctx.author.id, "charinfo", [characters]) - if len(characters) > 1: - await ctx.send("Please provide only one character for information.") - return - - char = characters[0] - unicode_value = ord(char) - char_name = unicodedata.name(char, "Could not find name!") - char_category = unicodedata.category(char) - - unicode_escape = f"\\u{unicode_value:04x}" - unicode_escape_full = f"\\U{unicode_value:08x}" - python_escape = repr(char) - - embed = discord.Embed( - color=self.color_manager.get_color("Blue"), - title="Character info", - description=f"Information on character: {char}" - ) - - embed.add_field(name="Original character", value=char, inline=True) - embed.add_field(name="Character name", value=char_name, inline=True) - embed.add_field(name="Character category", value=char_category, inline=True) - embed.add_field(name="Unicode value", value=f"U+{unicode_value:04X}", inline=True) - embed.add_field(name="Unicode escape", value=unicode_escape, inline=True) - embed.add_field(name="Full Unicode escape", value=unicode_escape_full, inline=True) - embed.add_field(name="Python escape", value=python_escape, inline=True) - - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - - image_path = get_char_image(char) - - if image_path: - file = discord.File(image_path, filename="character.png") - embed.set_thumbnail(url="attachment://character.png") - await ctx.send(embed=embed, file=file) - os.remove(image_path) - else: - await ctx.send(embed=embed) - -async def setup(bot): - await bot.add_cog(Fun(bot)) -``` - -# BotUpdate\cogs\md.md - -```md -### Approach to Developing a 2FA Email Sending Project Using Custom Domain Email - -#### **Objective:** -You aim to develop a test project that sends Two-Factor Authentication (2FA) emails using Python, leveraging a custom email address with a custom domain (`nerd-bear.org`). The current setup involves GoDaddy for domain registration, Cloudflare for DNS management, and free email forwarding to a Gmail account. However, this setup does not support sending emails directly from the custom domain email address. - -#### **Requirements:** -- **Primary Requirement:** Send emails from the custom domain email address (`nerd-bear.org`) for the 2FA project. -- **Constraints:** The current setup does not support sending emails directly from the custom domain email address. - -#### **Proposed Solution:** - -1. **Set Up a Custom Email Service:** - - **Option 1: Use a Third-Party Email Service Provider (ESP):** - - **Service Providers:** Consider using services like **SendGrid**, **Mailgun**, or **Amazon SES** (Simple Email Service). - - **Steps:** - 1. **Sign Up:** Create an account with the chosen ESP. - 2. **Domain Verification:** Verify your domain (`nerd-bear.org`) with the ESP to ensure you can send emails from it. This usually involves adding DNS records provided by the ESP to your Cloudflare DNS settings. - 3. **API Integration:** Use the ESP's API to send emails from your Python application. Most ESPs provide Python libraries or SDKs for easy integration. - 4. **Custom Email Address:** Configure the ESP to send emails from your custom domain email address (e.g., `noreply@nerd-bear.org`). - - **Example with SendGrid:** - \`\`\`python - from sendgrid import SendGridAPIClient - from sendgrid.helpers.mail import Mail - - message = Mail( - from_email='noreply@nerd-bear.org', - to_emails='user@example.com', - subject='Your 2FA Code', - html_content='Your code is: 123456') - - try: - sg = SendGridAPIClient('YOUR_SENDGRID_API_KEY') - response = sg.send(message) - print(response.status_code, response.body, response.headers) - except Exception as e: - print(e.message) - \`\`\` - - - **Option 2: Set Up a Mail Server:** - - **Service Providers:** Consider using **Mailcow**, **Mailu**, or **Postfix/Dovecot** to set up your own mail server. - - **Steps:** - 1. **Server Setup:** Deploy a mail server on a VPS (Virtual Private Server) or a dedicated server. - 2. **Domain Configuration:** Configure the mail server to use your custom domain (`nerd-bear.org`). This involves setting up DNS records (MX, SPF, DKIM, DMARC) in Cloudflare. - 3. **Email Sending:** Use Python's `smtplib` library to send emails from your custom domain email address. - - **Example with SMTP:** - \`\`\`python - import smtplib - from email.mime.text import MIMEText - - msg = MIMEText('Your code is: 123456') - msg['Subject'] = 'Your 2FA Code' - msg['From'] = 'noreply@nerd-bear.org' - msg['To'] = 'user@example.com' - - with smtplib.SMTP('your-mail-server.com', 587) as server: - server.starttls() - server.login('noreply@nerd-bear.org', 'your-email-password') - server.sendmail('noreply@nerd-bear.org', 'user@example.com', msg.as_string()) - \`\`\` - -2. **Leverage Existing Infrastructure:** - - **Cloudflare:** Ensure that Cloudflare is configured to allow outbound SMTP traffic if you choose to set up your own mail server. - - **GoDaddy:** Verify that GoDaddy is correctly pointing to Cloudflare for DNS management. - -3. **Security Considerations:** - - **API Keys:** If using an ESP, store API keys securely (e.g., environment variables, encrypted storage). - - **Email Authentication:** Ensure that your emails are authenticated using SPF, DKIM, and DMARC to prevent them from being marked as spam. - -#### **Conclusion:** -The best approach depends on your specific needs, such as the volume of emails, budget, and technical expertise. Using a third-party ESP like SendGrid or Mailgun is generally easier and more scalable, while setting up your own mail server offers more control but requires more technical effort. - -``` - -# BotUpdate\cogs\moderation.py - -```py -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history -import datetime - -class Moderation(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - @commands.has_permissions(kick_members=True) - async def kick(self, ctx, member: discord.Member, *, reason="No reason provided"): - add_history(ctx.author.id, "kick", [str(member.id), reason]) - try: - await member.send( - embed=discord.Embed( - title="You've Been Kicked", - description=f"You were kicked from {ctx.guild.name}.\nReason: {reason}", - color=self.color_manager.get_color("Red") - ) - ) - except: - pass # Unable to DM the user - - await member.kick(reason=reason) - embed = discord.Embed( - title="User Kicked", - description=f"{member.mention} has been kicked.\nReason: {reason}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - @commands.has_permissions(ban_members=True) - async def ban(self, ctx, member: discord.Member, *, reason="No reason provided"): - add_history(ctx.author.id, "ban", [str(member.id), reason]) - try: - await member.send( - embed=discord.Embed( - title="You've Been Banned", - description=f"You were banned from {ctx.guild.name}.\nReason: {reason}", - color=self.color_manager.get_color("Red") - ) - ) - except: - pass # Unable to DM the user - - await member.ban(reason=reason) - embed = discord.Embed( - title="User Banned", - description=f"{member.mention} has been banned.\nReason: {reason}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - @commands.has_permissions(ban_members=True) - async def unban(self, ctx, *, member): - add_history(ctx.author.id, "unban", [member]) - banned_users = await ctx.guild.bans() - member_name, member_discriminator = member.split('#') - - for ban_entry in banned_users: - user = ban_entry.user - if (user.name, user.discriminator) == (member_name, member_discriminator): - await ctx.guild.unban(user) - embed = discord.Embed( - title="User Unbanned", - description=f"{user.mention} has been unbanned.", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - return - - await ctx.send(f"User {member} not found in ban list.") - - @commands.command() - @commands.has_permissions(manage_messages=True) - async def timeout(self, ctx, member: discord.Member, duration: int, unit, *, reason="No reason provided"): - add_history(ctx.author.id, "timeout", [str(member.id), str(duration), unit, reason]) - time_units = {"s": "seconds", "m": "minutes", "h": "hours", "d": "days"} - if unit not in time_units: - await ctx.send("Invalid time unit. Use 's' for seconds, 'm' for minutes, 'h' for hours, or 'd' for days.") - return - - time_delta = datetime.timedelta(**{time_units[unit]: duration}) - await member.timeout(time_delta, reason=reason) - - embed = discord.Embed( - title="User Timed Out", - description=f"{member.mention} has been timed out for {duration}{unit}.\nReason: {reason}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - try: - await member.send( - embed=discord.Embed( - title="You were timed out", - description=f"You have been timed out in {ctx.guild.name} for {duration}{unit}.\nReason: {reason}", - color=self.color_manager.get_color("Red") - ) - ) - except: - pass # Unable to DM the user - -async def setup(bot): - await bot.add_cog(Moderation(bot)) -``` - -# BotUpdate\cogs\music.py - -```py -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history -import yt_dlp - -class Music(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - async def join(self, ctx): - add_history(ctx.author.id, "join") - if ctx.author.voice is None: - await ctx.send("You need to be in a voice channel to use this command.") - return - - channel = ctx.author.voice.channel - await channel.connect() - await ctx.send(f"Joined {channel.name}") - - @commands.command() - async def leave(self, ctx): - add_history(ctx.author.id, "leave") - if ctx.voice_client is None: - await ctx.send("I'm not in a voice channel.") - return - - await ctx.voice_client.disconnect() - await ctx.send("Left the voice channel") - - @commands.command() - async def play(self, ctx, *, query): - add_history(ctx.author.id, "play", [query]) - if ctx.voice_client is None: - if ctx.author.voice: - await ctx.author.voice.channel.connect() - else: - await ctx.send("You are not connected to a voice channel.") - return - - async with ctx.typing(): - try: - with yt_dlp.YoutubeDL({"format": "bestaudio", "noplaylist": "True"}) as ydl: - info = ydl.extract_info(query, download=False) - URL = info["url"] - title = info["title"] - except Exception as e: - await ctx.send(f"An error occurred: {str(e)}") - return - - if ctx.voice_client.is_playing(): - ctx.voice_client.stop() - - FFMPEG_OPTIONS = { - 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', - 'options': '-vn' - } - audio_source = discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS) - ctx.voice_client.play(audio_source) - - embed = discord.Embed( - title="Now Playing", - description=f"Now playing: {title}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - -async def setup(bot): - await bot.add_cog(Music(bot)) -``` - -# BotUpdate\cogs\utility.py - -```py -import discord -from discord.ext import commands -from utils.config_manager import ColorManager, load_config -from utils.db_manager import add_history, add_feedback -from utils.helper import fetch_latency, text_to_speech -import os -import asyncio -from deep_translator import GoogleTranslator - -class Utility(commands.Cog): - def __init__(self, bot): - self.bot = bot - self.config = load_config() - self.color_manager = ColorManager(self.config) - - @commands.command() - async def ping(self, ctx): - add_history(ctx.author.id, "ping") - latency = fetch_latency(self.bot) - embed = discord.Embed( - title="Bot Latency", - description=f"The current bot latency is approximately `{latency}ms`", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - async def profile(self, ctx, member: discord.Member = None): - member = member or ctx.author - add_history(ctx.author.id, "profile", [str(member.id)]) - - embed = discord.Embed( - title=f"{member.name}'s Profile", - description="User's public Discord information", - color=self.color_manager.get_color("Blue") - ) - embed.add_field(name="Display Name", value=member.display_name, inline=True) - embed.add_field(name="User ID", value=member.id, inline=True) - embed.add_field(name="Account Created", value=member.created_at.strftime("%d/%m/%y %H:%M:%S"), inline=True) - embed.add_field(name="Joined Server", value=member.joined_at.strftime("%d/%m/%y %H:%M:%S"), inline=True) - embed.add_field(name="Top Role", value=member.top_role.mention, inline=True) - embed.add_field(name="Status", value=str(member.status).title(), inline=True) - - if member.avatar: - embed.set_thumbnail(url=member.avatar.url) - - await ctx.send(embed=embed) - - @commands.command() - async def feedback(self, ctx, *, message): - add_history(ctx.author.id, "feedback", [message]) - if add_feedback(str(ctx.author.id), message): - embed = discord.Embed( - title="Feedback Received", - description="Thank you for your feedback!", - color=self.color_manager.get_color("Green") - ) - else: - embed = discord.Embed( - title="Feedback Error", - description="There was an error submitting your feedback. Please try again later.", - color=self.color_manager.get_color("Red") - ) - - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - async def tts(self, ctx, *, text): - add_history(ctx.author.id, "tts", [text]) - if not ctx.author.voice: - await ctx.send("You need to be in a voice channel to use this command.") - return - - output_file = f"./temp/audio/tts_{ctx.message.id}.mp3" - - try: - text_to_speech(text, output_file, self.config.get("tts_mode", "normal")) - except Exception as e: - await ctx.send(f"An error occurred while generating the TTS: {str(e)}") - return - - voice_channel = ctx.author.voice.channel - if ctx.voice_client is None: - vc = await voice_channel.connect() - else: - vc = ctx.voice_client - - if vc.is_playing(): - vc.stop() - - vc.play( - discord.FFmpegPCMAudio(source=output_file), - after=lambda e: asyncio.run_coroutine_threadsafe(vc.disconnect(), self.bot.loop) - ) - - while vc.is_playing(): - await asyncio.sleep(1) - - if os.path.exists(output_file): - os.remove(output_file) - - embed = discord.Embed( - title="TTS Completed", - description=f"Successfully played TTS in {voice_channel.mention}", - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - - @commands.command() - async def translate(self, ctx, *, text): - add_history(ctx.author.id, "translate", [text]) - try: - translator = GoogleTranslator(source="auto", target="en") - translated_text = translator.translate(text) - - embed = discord.Embed( - title="Translation", - description=translated_text, - color=self.color_manager.get_color("Blue") - ) - embed.set_footer( - text=self.config["defaults"]["footer_text"], - icon_url=self.config["defaults"]["footer_icon"] - ) - await ctx.send(embed=embed) - except Exception as e: - await ctx.send(f"An error occurred during translation: {str(e)}") - -async def setup(bot): - await bot.add_cog(Utility(bot)) -``` - -# BotUpdate\config.json - -```json -{ - "defaults": { - "prefix": "?", - "footer_text": "This bot is created and hosted by Nerd bear", - "footer_icon": "https://as2.ftcdn.net/v2/jpg/01/17/00/87/1000_F_117008730_0Dg5yniuxPQLz3shrJvLIeBsPfPRBSE1.jpg" - }, - - "bot_version": "0.4.6", - "bot_name": "Memo", - "tts_mode":"fast", - "log_channel_id": "1290060885485948950", - "tts_detector_factory_seed": "0", - "token": "token", - - "colors": { - "Red": "#FFB3BA", - "Coral": "#FFCCB6", - "Orange": "#FFE5B4", - "Gold": "#FFF1B5", - "Yellow": "#FFFFD1", - "Lime": "#DCFFB8", - "Green": "#BAFFC9", - "Teal": "#B5EAD7", - "Cyan": "#C7F2FF", - "Blue": "#B5DEFF", - "Navy": "#C5CAE9", - "Purple": "#D0B8FF", - "Magenta": "#F2B5D4", - "Pink": "#FFCCE5", - "Gray": "#E0E0E0", - "Lavender": "#E6E6FA" - }, - - "guilds": { - "1288144110880030795": { - "prefix": "*" - } - } -} -``` - -# BotUpdate\data\crac.db - -```db - -``` - -# BotUpdate\logs\bot.log - -```log - -``` - -# BotUpdate\main.py - -```py -import os -import discord -from discord.ext import commands -import json -import logging -from utils.config_manager import load_config - -intents = discord.Intents.all() -config = load_config() - -bot = commands.Bot(command_prefix=config['defaults']['prefix'], intents=intents) - -@bot.event -async def on_ready(): - print(f'Logged in as {bot.user.name} (ID: {bot.user.id})') - print('------') - -async def load_cogs(): - for filename in os.listdir('./cogs'): - if filename.endswith('.py'): - try: - await bot.load_extension(f'cogs.{filename[:-3]}') - print(f'Loaded cog: {filename[:-3]}') - except Exception as e: - print(f'Failed to load cog {filename[:-3]}: {str(e)}') - -async def main(): - logging.basicConfig(filename='logs/bot.log', level=logging.INFO, - format='%(asctime)s:%(levelname)s:%(message)s') - - await load_cogs() - await bot.start(config['token']) - -if __name__ == '__main__': - import asyncio - asyncio.run(main()) -``` - -# BotUpdate\requirements.txt - -```txt -aiohappyeyeballs == 2.4.3 -aiohttp == 3.10.8 -aiosignal == 1.3.1 -aiosqlite == 0.20.0 -annotated-types == 0.7.0 -anyio == 4.6.0 -async-timeout == 4.0.3 -attrs == 24.2.0 -beautifulsoup4 == 4.12.3 -blinker == 1.8.2 -Brotli == 1.1.0 -certifi == 2024.8.30 -cffi == 1.17.1 -charset-normalizer == 3.3.2 -click == 8.1.7 -colorama == 0.4.6 -comtypes == 1.4.7 -cryptography == 43.0.1 -dataclasses-json == 0.6.7 -deep-translator == 1.11.4 -Deprecated == 1.2.14 -discord == 2.3.2 -discord.py == 2.4.0 -disnake == 2.9.2 -distro == 1.9.0 -duckduckgo_search == 6.2.13 -edge-tts == 6.1.12 -fastapi == 0.115.0 -ffmpeg-python == 0.2.0 -Flask == 3.0.3 -Flask-Cors == 5.0.0 -frozenlist == 1.4.1 -future == 1.0.0 -gevent == 24.2.1 -greenlet == 3.1.1 -groq == 0.11.0 -gTTS == 2.5.3 -h11 == 0.14.0 -httpcore == 1.0.5 -httpx == 0.27.2 -idna == 3.10 -itsdangerous == 2.2.0 -Jinja2 == 3.1.4 -joblib == 1.4.2 -jsonpatch == 1.33 -jsonpointer == 3.0.0 -langchain == 0.3.1 -langchain-community == 0.3.1 -langchain-core == 0.3.6 -langchain-groq == 0.2.0 -langchain-ollama == 0.2.0 -langchain-text-splitter == 0.3.0 -langdetect == 1.0.9 -langsmith == 0.1.129 -lxml == 5.3.0 -lyrics-extractor == 3.0.1 -lyricsgenius == 3.0.1 -markdown-it-py == 3.0.0 -MarkupSafe == 2.1.5 -marshmallow == 3.22.0 -mdurl == 0.1.2 -multidict == 6.1.0 -mutagen == 1.47.0 -mypy-extensions == 1.0.0 -nltk == 3.9.1 -numpy == 1.26.4 -ollama == 0.3.3 -orjson == 3.10.7 -packaging == 24.1 -pillow == 10.4.0 -primp == 0.6.3 -pycparser == 2.22 -pycryptodomex == 3.20.0 -pydantic == 2.9.2 -pydantic-settings == 2.5.2 -pydantic_core == 2.23.4 -PyGithub == 2.4.0 -Pygments == 2.18.0 -PyJWT == 2.9.0 -PyNaCl == 1.5.0 -pypiwin32 == 223 -PySide6 == 6.7.3 -PySide6_Addons == 6.7.3 -PySide6_Essentials == 6.7.3 -python-dotenv == 1.0.1 -pyttsx3 == 2.98 -pytube == 15.0.0 -pywin32 == 306 -PyYAML == 6.0.2 -redis == 5.1.0 -regex == 2024.9.11 -requests == 2.32.3 -rich == 13.8.1 -shiboken6 == 6.7.3 -six == 1.16.0 -sniffio == 1.3.1 -soupsieve == 2.6 -spotipy == 2.24.0 -SQLAlchemy == 2.0.35 -starlette == 0.38.6 -tenacity == 8.5.0 -timeout == 0.1.2 -tqdm == 4.66.5 -typing-inspect == 0.9.0 -typing_extensions == 4.12.2 -urllib3 == 2.2.3 -uvicorn == 0.31.0 -websocket == 0.2.1 -websocket-client == 1.8.0 -websockets == 13.1 -Werkzeug == 3.0.4 -wikipedia == 1.4.0 -wrapt == 1.16.0 -yarl == 1.13.1 -youtube-transcript-api == 0.6.2 -yt-dlp == 2024.9.27 -zope.event == 5.0 -zope.interface == 7.0.3 -``` - -# BotUpdate\utils\__init__.py - -```py - -``` - -# BotUpdate\utils\config_manager.py - -```py -import discord -from src.utils.helper import * - - -def load_config(config_path: str = "config.json") -> dict: - try: - with open(config_path, "r") as f: - return json.load(f) - except FileNotFoundError: - print(f"WARNING: Config file not found at {config_path}. Using default configuration.") - return {"default_prefix": "?", "guilds": {}} - except json.JSONDecodeError: - print(f"ERROR: Invalid JSON in config file {config_path}. Using default configuration.") - return {"default_prefix": "?", "guilds": {}} - -def save_config(config: dict, config_path: str = "config.json") -> None: - try: - with open(config_path, "w") as f: - json.dump(config, f, indent=4) - except IOError as e: - print(f"ERROR: Failed to save config to {config_path}: {e}") - -class ColorManager: - def __init__(self, config: dict): - self.colors = config.get("colors", {}) - - def get_color(self, color_name: str) -> int: - if color_name not in self.colors: - raise ValueError(f"Color '{color_name}' not found in configuration") - return int(self.colors[color_name].lstrip("#"), 16) - - def list_colors(self) -> list: - return list(self.colors.keys()) - - def create_color_embed(self, title: str, description: str, color_name: str) -> discord.Embed: - try: - color = self.get_color(color_name) - return discord.Embed(title=title, description=description, color=color) - except ValueError as e: - print(f"ERROR: Failed to create color embed: {e}") - return discord.Embed(title=title, description=description) -``` - -# BotUpdate\utils\db_manager.py - -```py -import sqlite3 -import datetime -import json - -DB_PATH = "./data/memo.db" - -def get_db_connection(): - return sqlite3.connect(DB_PATH) - -def execute_query(query: str, params: tuple = (), fetch: bool = False): - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute(query, params) - if fetch: - return cursor.fetchall() - conn.commit() - return True - -def add_feedback(user_id: str, message: str) -> bool: - datetime_value = datetime.datetime.now().strftime("%S:%M:%H %d/%m/%y") - query = "INSERT INTO feedback VALUES (?, ?, ?)" - return execute_query(query, (user_id, message, datetime_value)) - -def add_history(user_id: int, command: str, arguments: list[str] = ["none"]) -> bool: - args_json = json.dumps(arguments) - datetime_value = datetime.datetime.now().strftime("%S:%M:%H %d/%m/%y") - query = "INSERT INTO history (user_id, command, arguments, datetime) VALUES (?, ?, ?, ?)" - return execute_query(query, (user_id, command, args_json, datetime_value)) - -def get_usage(command_name: str) -> tuple[str, str, str]: - query = "SELECT command_name, arguments, level FROM usage WHERE command_name = ?" - result = execute_query(query, (command_name,), fetch=True) - return result[0] if result else None - -def get_all_usages() -> list[tuple[str, str, str]]: - query = "SELECT command_name, arguments, level FROM usage" - return execute_query(query, fetch=True) - -def add_usage(command_name: str, arguments: list[str], level: int) -> bool: - if not 0 <= level <= 3: - raise ValueError("Level must be between 0 and 3") - query = "INSERT INTO usage (command_name, arguments, level) VALUES (?, ?, ?)" - args_json = json.dumps(arguments) - return execute_query(query, (command_name, args_json, str(level))) - -def create_tables(): - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute("CREATE TABLE IF NOT EXISTS feedback (user_id TEXT, message TEXT, datetime TEXT)") - cursor.execute("CREATE TABLE IF NOT EXISTS history (user_id INTEGER, command TEXT, arguments TEXT, datetime TEXT)") - cursor.execute("CREATE TABLE IF NOT EXISTS usage (command_name TEXT, arguments TEXT, level TEXT)") - conn.commit() - -# Call this function when initializing the bot to ensure tables exist -create_tables() -``` - -# BotUpdate\utils\helper.py - -```py -import datetime -import tempfile -from PIL import Image, ImageDraw, ImageFont -from langdetect import detect, DetectorFactory -from langdetect.lang_detect_exception import LangDetectException -from collections import Counter -from gtts import gTTS -import discord - -def set_langdetect_seed(seed: int = 0): - DetectorFactory.seed = seed - -def text_to_speech(text: str, output_file: str, tts_mode: str) -> None: - try: - slow = tts_mode.lower() == "slow" - language = detect(text) - tts = gTTS(text=text, lang=language, slow=slow) - tts.save(output_file) - except Exception as e: - print(f"ERROR_LOG ~ Text-to-speech conversion failed: {e}") - -def log_info(value: str = "None") -> None: - timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - print(f"{timestamp} [INFO] {value}") - -def fetch_latency(client: discord.Client, shouldRound: bool = True) -> float: - latency = client.latency * 1000 - return round(latency) if shouldRound else latency - -def get_char_image(char: str, bg: str = "white", fg: str = "black", format: str = "png") -> str: - try: - img = Image.new("RGB", (200, 200), color=bg) - d = ImageDraw.Draw(img) - try: - font = ImageFont.truetype("arial.ttf", 120) - except IOError: - font = ImageFont.load_default() - d.text((100, 100), char, font=font, fill=fg, anchor="mm") - with tempfile.NamedTemporaryFile(delete=False, suffix=f".{format}") as temp_file: - img.save(temp_file, format=format.upper()) - temp_file_path = temp_file.name - return temp_file_path - except Exception as e: - print(f"ERROR: Failed to generate character image: {e}") - return None - -def detect_language(text: str) -> str: - try: - detections = [detect(text) for _ in range(5)] - most_common = Counter(detections).most_common(1)[0][0] - return most_common - except LangDetectException as e: - print(f"ERROR: Language detection failed: {e}") - return "unknown" -``` - -# changes.md - -```md -# Memo Bot Changelog - -## Latest Updates - -+ Added restart command -+ Updated shutdown command to disconect from all voice channels -+ Updated start command to set RPC to the help info -``` - -# config.json - -```json -{ - "defaults": { - "prefix": "?", - "footer_text": "This bot is created and hosted by Nerd bear", - "footer_icon": "https://as2.ftcdn.net/v2/jpg/01/17/00/87/1000_F_117008730_0Dg5yniuxPQLz3shrJvLIeBsPfPRBSE1.jpg" - }, - - "bot_version": "0.4.6", - "bot_name": "Memo", - "tts_mode":"fast", - "log_channel_id": "1290060885485948950", - "tts_detector_factory_seed": "0", - - "colors": { - "Red": "#FFB3BA", - "Coral": "#FFCCB6", - "Orange": "#FFE5B4", - "Gold": "#FFF1B5", - "Yellow": "#FFFFD1", - "Lime": "#DCFFB8", - "Green": "#BAFFC9", - "Teal": "#B5EAD7", - "Cyan": "#C7F2FF", - "Blue": "#B5DEFF", - "Navy": "#C5CAE9", - "Purple": "#D0B8FF", - "Magenta": "#F2B5D4", - "Pink": "#FFCCE5", - "Gray": "#E0E0E0", - "Lavender": "#E6E6FA" - }, - - "guilds": { - "1288144110880030795": { - "prefix": "*" - } - } -} -``` - -# crac.db - -```db - -``` - -# db_manager\__intit__.py - -```py - -``` - -# db_manager\feedback.py - -```py -import sqlite3 -import datetime - - -def add_feedback(user_id: str, message: str) -> bool: - """Uses SQLite to add user's feedback to feedback db table - - ### Params: - `used_id` `str` The user id of the person who submitted the feedback. - `message` `str` The name of the command that the user ran. - - ### Return: - Returns a bool (True on success) - """ - - datetime_value = datetime.datetime.today().now().__format__("%S:%M:%H %d/%m/%y") - - db_connection = sqlite3.connect("./memo.db") - db_cursor = db_connection.cursor() - db_cursor.execute( - f'INSERT INTO feedback VALUES (\'{user_id}\', "{message}", "{datetime_value}")' - ) - db_connection.commit() - - return True - -``` - -# db_manager\history.py - -```py -import sqlite3 -import datetime -import json - -def add_history(user_id: int, guild_id: int, command: str, arguments: list[str] = ["none"]) -> bool: - """Uses SQLite to add user command to history of commands ran - - ### Params: - `user_id` `int` The user id of the person who ran the command. - `guild_id` `int` The guild id of the server that the user ran the command in. - `command` `str` The name of the command that the user ran. - `arguments` `list[str]` The arguments passed in to the command, defaults to ["none"]. - - ### Return: - Returns a bool (True on success) - """ - args_json = json.dumps(arguments) - datetime_value = datetime.datetime.now().strftime("%S:%M:%H %d/%m/%y") - - db_connection = sqlite3.connect("./memo.db") - db_cursor = db_connection.cursor() - - try: - db_cursor.execute( - "INSERT INTO history (user_id, guild_id, command, arguments, datetime) VALUES (?, ?, ?, ?, ?)", - (user_id, guild_id, command, args_json, datetime_value) - ) - db_connection.commit() - return True - except sqlite3.Error as e: - print(f"ERROR_LOG: SQLite error: {e}") - return False - finally: - db_connection.close() -``` - -# db_manager\usage.py - -```py -import sqlite3 -from typing import List, Tuple -import json - -DB_PATH = "./memo.db" - - -def get_db_connection(): - """Create and return a database connection.""" - return sqlite3.connect(DB_PATH) - - -def execute_query(query: str, params: Tuple = (), fetch: bool = False): - """Execute a SQL query and optionally fetch results.""" - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute(query, params) - if fetch: - return cursor.fetchall() - conn.commit() - return True - - -def get_usage(command_name: str) -> Tuple[str, str, str]: - """Get usage information for a specific command. - - Args: - command_name (str): The name of the command to retrieve. - - Returns: - Tuple[str, str, str]: A tuple containing (command_name, arguments, level). - Returns None if the command is not found. - """ - query = "SELECT command_name, arguments, level FROM usage WHERE command_name = ?" - result = execute_query(query, (command_name,), fetch=True) - return result[0] if result else None - - -def get_all_usages() -> List[Tuple[str, str, str]]: - """Get usage information for all commands. - - Returns: - List[Tuple[str, str, str]]: A list of tuples, each containing (command_name, arguments, level). - """ - query = "SELECT command_name, arguments, level FROM usage" - return execute_query(query, fetch=True) - - -def add_usage(command_name: str, arguments: List[str], level: int) -> bool: - """Add usage information for a command. - - Args: - command_name (str): The name of the command to add. - arguments (List[str]): The list of arguments the command accepts. - level (int): The permission level of the command (0-3). - - Returns: - bool: True if the operation was successful, False otherwise. - """ - if not 0 <= level <= 3: - raise ValueError("Level must be between 0 and 3") - - query = "INSERT INTO usage (command_name, arguments, level) VALUES (?, ?, ?)" - args_json = json.dumps(arguments) - return execute_query(query, (command_name, args_json, str(level))) -``` - -# help_heart.webp - -This is a binary file of the type: Image - -# launcher.py - -```py -import click -from rich import print as rich_print - - -@click.command() -@click.option("--token", help="Discord bot token", required=True) -def main(token: str): - if not token.strip(): - rich_print("[bold red]ERROR:[/bold red] Invalid token provided.") - rich_print("Usage: python3 ./launcher.py --token ") - raise click.Abort() - - from src.bot import Memo - - try: - Memo.run(token) - except Exception as e: - rich_print(f"[red]{e}[/red]") - click.Abort() - - -if __name__ == "__main__": - main() - -# python ./launcher.py --token "MTI4OTkyMTQ3NjYxNDU1MzY3Mg.GNo3VX.kjVPN-1ri34TtfuWZ-ADqhSeW56fARaLu7pMnk" -``` - -# LICENSE - -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2024 Iona M. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -``` - -# memo.db - -This is a binary file of the type: Binary - -# README.md - -```md -# Memo Bot - -## Table of Contents -1. [Introduction](#introduction) -2. [Features](#features) -3. [Commands](#commands) -4. [Installation](#installation) -5. [Usage](#usage) -6. [Configuration](#configuration) -7. [Project Structure](#project-structure) -8. [Dependencies](#dependencies) -9. [Development](#development) -10. [Contributing](#contributing) -11. [License](#license) -12. [Support](#support) -13. [Creator](#creator) - -## Introduction - -Memo Bot is a versatile, all-purpose Discord bot designed to enhance server management and user interaction. Currently in active development, Memo Bot offers a wide range of features from moderation tools to fun commands, making it a valuable addition to any Discord server. - -## Features - -1. **Server Moderation** - - Kick users - - Ban and unban users - - Timeout users - - Nickname management - -2. **User Interaction** - - Character information lookup - - Text-to-speech functionality - - Music playback from YouTube - - User profile display - -3. **Bot Management** - - Customizable bot status - - Start/shutdown commands - - Feedback system - -4. **Message Handling** - - Logging of message edits and deletions - - Inappropriate word detection and filtering - -5. **Voice Channel Integration** - - Join and leave voice channels - - Play audio in voice channels - -6. **Customization** - - Configurable command prefix - - Guild-specific settings - -## Commands - -Here's a detailed list of available commands: - -1. `?help`: Displays a help message with all available commands and their usage. - -2. `?kick @user [reason]`: Kicks the mentioned user from the server. Requires kick permissions. - -3. `?ban @user [reason]`: Bans the mentioned user from the server. Requires ban permissions. - -4. `?unban @user`: Unbans the specified user from the server. Requires ban permissions. - -5. `?timeout @user [reason]`: Timeouts the mentioned user for the specified duration. Requires moderate members permission. - -6. `?shutdown`: Shuts down the bot. Requires administrator permissions. - -7. `?start`: Starts the bot if it's offline. Requires administrator permissions. - -8. `?charinfo [character]`: Provides detailed information about the specified character, including Unicode data. - -9. `?tts [message]`: Converts the given text to speech and plays it in the user's current voice channel. - -10. `?play [youtube_url]`: Plays audio from the specified YouTube video in the user's current voice channel. - -11. `?join`: Makes the bot join the user's current voice channel. - -12. `?leave`: Makes the bot leave the current voice channel. - -13. `?profile @user`: Displays detailed profile information about the mentioned user. - -14. `?nick @user [new_nickname]`: Changes the nickname of the mentioned user. Requires manage nicknames permission. - -15. `?feedback [message]`: Allows users to submit feedback about the bot, which is stored in a database. - -## Installation - -1. Clone the repository: - \`\`\` - git clone https://github.com/your-username/Memo-bot.git - \`\`\` - -2. Navigate to the project directory: - \`\`\` - cd Memo-bot - \`\`\` - -3. Install the required dependencies: - \`\`\` - pip install -r requirements.txt - \`\`\` - -4. Set up your Discord bot token: - - Create a new application in the [Discord Developer Portal](https://discord.com/developers/applications) - - Create a bot for your application and copy the bot token - - Create a `config.json` file in the project root and add your token: - \`\`\`json - { - "token": "YOUR_BOT_TOKEN_HERE" - } - \`\`\` - -5. Set up the SQLite database: - \`\`\` - python setup/create_feedback_table.py - python setup/create_history_table.py - python setup/create_usage_table.py - \`\`\` - -## Usage - -1. Run the bot: - \`\`\` - python launcher.py - \`\`\` - -2. Invite the bot to your Discord server: - - Go to the OAuth2 URL generator in your Discord Developer Portal - - Select the "bot" scope and the necessary permissions - - Use the generated URL to invite the bot to your server - -3. Once the bot is in your server, use `?help` to see all available commands. - -## Configuration - -The `config.json` file contains important settings for the bot: - -\`\`\`json -{ - "defaults": { - "prefix": "?" - }, - "bot_version": "0.4.5", - "bot_name": "Memo", - "tts_mode": "fast", - "guilds": { - "1288144110880030795": { - "prefix": "*" - } - } -} -\`\`\` - -- `defaults.prefix`: The default command prefix for the bot. -- `bot_version`: The current version of the bot. -- `bot_name`: The name of the bot. -- `tts_mode`: The mode for text-to-speech functionality ("fast" or "slow"). -- `guilds`: Guild-specific settings, including custom prefixes. - -## Project Structure - -- `main/bot.py`: The main bot file containing command implementations and event handlers. -- `launcher.py`: The entry point for running the bot. -- `db_manager/`: Directory containing database management modules. -- `setup/`: Directory containing database setup scripts. -- `temp/audio/`: Directory for temporary audio files used by the TTS feature. -- `website/`: Directory containing HTML files for the bot's website. - -## Dependencies - -Memo Bot relies on several Python libraries: - -- discord.py: The core library for interacting with the Discord API. -- Pillow: Used for image manipulation in the `charinfo` command. -- gTTS: Google Text-to-Speech library for the TTS feature. -- yt_dlp: YouTube downloader used for the music playback feature. -- rich: Used for console output formatting and logging. -- langdetect: Used for language detection in the TTS feature. - -For a complete list of dependencies, refer to the `requirements.txt` file. - -## Development - -Memo Bot is under active development with nearly daily updates. The development process includes: - -- Regular feature additions and improvements -- Bug fixes and performance optimizations -- Refactoring for better code organization and maintainability -- Implementation of user feedback and feature requests - -Future development plans include: -- Implementing a music queue system -- Adding more fun and interactive commands -- Improving error handling and user feedback -- Enhancing the configuration system for more granular control - -## Contributing - -We welcome contributions to Memo Bot! Here's how you can contribute: - -1. Fork the repository -2. Create a new branch for your feature or bug fix -3. Make your changes and commit them with clear, descriptive commit messages -4. Push your changes to your fork -5. Submit a pull request to the main repository - -Please ensure your code adheres to the existing style conventions and includes appropriate tests. - -## License - -This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for the full license text. - -## Support - -If you need help or want to report a bug, you can: - -1. Open an issue on this GitHub repository -2. Join our support Discord server (link to be added) -3. Contact us via email at Memo@nerd-bear.org - -## Creator - -Memo Bot is created and maintained by Nerd Bear. For more information about the creator and other projects, visit [nerd-bear.org](https://nerd-bear.org). - ---- - -Memo Bot ยฉ 2024 by Nerd Bear. All rights reserved. -``` - -# setup\clear_feedback_table.py - -```py -import sqlite3 - -db_connection = sqlite3.connect("./memo.db") -db_cursor = db_connection.cursor() -db_cursor.execute("DROP TABLE feedback") -db_cursor.execute("CREATE TABLE feedback(used_id, message, datetime)") -db_connection.commit() -``` - -# setup\clear_history_table.py - -```py -import sqlite3 - -db_connection = sqlite3.connect("./memo.db") -db_cursor = db_connection.cursor() -db_cursor.execute("DROP TABLE history") -db_cursor.execute("CREATE TABLE history(user_id, guild_id, command, arguments, datetime)") -db_connection.commit() -``` - -# setup\clear_usage_table.py - -```py -import sqlite3 - -db_connection = sqlite3.connect("./memo.db") -db_cursor = db_connection.cursor() -db_cursor.execute("DROP TABLE usage") -db_cursor.execute("CREATE TABLE usage(command_name, arguments, level)") -db_connection.commit() -``` - -# setup\create_feedback_table.py - -```py -import sqlite3 - -db_connection = sqlite3.connect("./memo.db") -db_cursor = db_connection.cursor() -db_cursor.execute("CREATE TABLE feedback(used_id, message, datetime)") -db_connection.commit() -``` - -# setup\create_history_table.py - -```py -import sqlite3 - -db_connection = sqlite3.connect("./memo.db") -db_cursor = db_connection.cursor() -db_cursor.execute("CREATE TABLE history(user_id, guild_id, command, arguments, datetime)") -db_connection.commit() -``` - -# setup\create_usage_table.py - -```py -import sqlite3 - -db_connection = sqlite3.connect("./memo.db") -db_cursor = db_connection.cursor() -db_cursor.execute("CREATE TABLE usage(command_name, arguments, level)") -db_connection.commit() -``` - -# src\bot.py - -```py -import asyncio -import datetime -import os -import random -import sys -import functools -import unicodedata - -import disnake -from disnake.ext import commands -from disnake.ui import Button, Select, Modal, TextInput, View - -from rich import print as richPrint -from rich.console import Console -from rich.markdown import Markdown -from rich.panel import Panel -from deep_translator import GoogleTranslator - -import yt_dlp - -from db_manager import history, feedback -from src.utils.helper import * - -log_info("Loaded all needed imports", True) - - -CONFIG_PATH = "config.json" -log_info("Loaded config path", True) - -log_info("Loading Config", True) -config = load_config(CONFIG_PATH) -log_info("Completed loading config", True) - -log_info("Loading default values into Memory", True) -color_manager = ColorManager(config) -log_info("Initialized color manager", True) - -FOOTER_TEXT = config["defaults"].get("footer_text") -log_info("Loaded footer text", True) -FOOTER_ICON = config["defaults"].get("footer_icon") -log_info("Loaded footer icon", True) - -BOT_PREFIX = config["defaults"].get("prefix", "?") -log_info("Loaded bot prefix", True) -BOT_NAME = config.get("bot_name", "Memo Bot") -log_info("Loaded bot name", True) -BOT_VERSION = config.get("bot_version", "1.0.0") -log_info("Loaded bot version", True) - -TTS_MODE = config.get("tts_mode", "normal") -log_info("Loaded tts mode", True) - -LOGGING_CHANNEL_ID = int(config.get("log_channel_id", 0)) -log_info("Loaded logging channel id", True) - -intents = disnake.Intents.all() -log_info("Initialized intents", True) -Memo = commands.Bot(command_prefix="/", intents=intents) -log_info("Initialized bot", True) -console = Console() -log_info("Initialized console", True) - -set_langdetect_seed(config.get("tts_detector_factory_seed", 0)) -log_info("Loaded tts detector factory seed", True) - -bot_active = True -log_info("Initialized bot to be active", True) -log_info("Completed loading default values into Memory", True) - - -async def get_info_text() -> str: - return f""" - {BOT_NAME} v{BOT_VERSION} - Logged in as {Memo.user.name} (ID: {Memo.user.id}) - Connected to {len(Memo.guilds)} guilds - Bot is ready to use. Ping: {fetch_latency(Memo)}ms - Prefix: {BOT_PREFIX} - Initialization complete. - """ - - -def debug_command(func): - @functools.wraps(func) - async def wrapper(message: disnake.Message, *args: any, **kwargs: any): - embed = disnake.Embed( - title="Warning", - description=f"WARNING! This is a dev/debug command and will not be included in full release v1.0.0", - color=color_manager.get_color("Orange"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return await func(message, *args, **kwargs) - - return wrapper - - -@Memo.event -async def on_ready() -> None: - markdown = Markdown(f"# Discord {BOT_NAME} version {BOT_VERSION}") - console.print(markdown) - - info_text = await get_info_text() - panel = Panel( - info_text, title=f"{BOT_NAME} v{BOT_VERSION} Initialization Info", expand=False - ) - - console.print(panel) - - channel = Memo.get_channel(LOGGING_CHANNEL_ID) - - if channel: - embed = disnake.Embed( - title=f"{BOT_NAME} v{BOT_VERSION} Initialization Info", - description=info_text, - color=color_manager.get_color("Blue"), - timestamp=datetime.datetime.utcnow(), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await channel.send(embed=embed) - - await Memo.change_presence( - activity=disnake.Game(name=f"Run {BOT_PREFIX}help for help") - ) - - -@Memo.event -async def on_message(message: disnake.Message) -> None: - global bot_active - - if message.author == Memo.user: - return - - if isinstance(message.channel, disnake.DMChannel): - await send_error_embed( - message, - "Please run in server", - "When running commands or interacting with the bot, please do so in the server as we do not currently support DM interactions.", - FOOTER_TEXT, - FOOTER_ICON, - color_manager, - ) - return - - if not message.content.startswith(BOT_PREFIX): - if bot_active == False: - return - content = message.content.lower() - if any(word in content for word in ["nigger", "nigga", "negro", "nigro"]): - await handle_inappropriate_word(message) - if Memo.user in message.mentions: - await message.channel.send( - f"Hello {message.author.mention}! You mentioned me. How can I help you?" - ) - return - - if message.content.startswith("?") and len(message.content.strip()) <= 1: - return - - if not bot_active and message.content != f"{BOT_PREFIX}start": - embed = disnake.Embed( - title="Bot Offline", - description=f"{BOT_NAME} is currently offline. Use {BOT_PREFIX}start to activate.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - command = message.content.split()[0][len(BOT_PREFIX) :].lower() - args = message.content.split()[1:] - - history.add_history( - SHA3.hash_256(str(message.author.id)), - SHA3.hash_256(str(message.guild)), - command, - args, - ) - - match command: - case "help": - await help_command(message) - - case "timeout": - await timeout_command(message) - - case "kick": - await kick_command(message) - - case "ban": - await ban_command(message) - - case "unban": - await unban_command(message) - - case "shutdown": - await shutdown_command(message) - - case "start": - await start_command(message) - - case "charinfo": - await charinfo_command(message) - - case "join": - await join_vc_command(message) - - case "leave": - await leave_vc_command(message) - - case "tts": - await tts_command(message) - - case "play": - await play_command(message) - - case "profile": - await profile_command(message) - - case "nick": - await nick_command(message) - - case "feedback": - await feedback_command(message) - - case "restart": - await restart_command(message) - - case "translate": - await translate_command(message) - - case "ping": - await ping_command(message) - - case "server": - await server_command(message) - - case "joke": - await joke_command(message) - - case "coin": - await coin_command(message) - - case "quote": - await quote_command(message) - - case "mute": - await vc_mute_command(message) - - case "unmute": - await vc_unmute_command(message) - - case "deafen": - await vc_deafen_command(message) - - case "undeafen": - await vc_undeafen_command(message) - - case _: - embed = disnake.Embed( - title="Invalid Command", - description=f"The command you are running is not valid. Please run `?help` for a list of commands and their usages!", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - -async def handle_inappropriate_word(message: disnake.Message) -> None: - user = message.author - channel = message.channel - - dm_embed = disnake.Embed( - title="Inappropriate Word Detected", - description=f"{BOT_NAME} has detected an inappropriate word! Please do not send racist words in our server! Moderators have been informed!", - color=0xFF697A, - ) - dm_embed.add_field( - name="Rules", - value="Please read our rules before sending such messages!", - inline=False, - ) - dm_embed.add_field(name="Server", value=f"{message.guild.name}", inline=False) - dm_embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - dm_embed.set_thumbnail(url=message.guild.icon.url) - - try: - await user.send(embed=dm_embed) - except disnake.errors.Forbidden: - pass - - await message.delete() - - channel_embed = disnake.Embed( - title="Inappropriate Word Detected", - description=f"User {user.mention} tried to send a word that is marked not allowed!", - color=0xFF697A, - ) - channel_embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await channel.send(embed=channel_embed) - - -async def help_command(message: disnake.Message) -> None: - embed = fetch_help_embed( - color_manager, BOT_NAME, BOT_VERSION, BOT_PREFIX, FOOTER_TEXT, FOOTER_ICON - ) - await message.channel.send(embed=embed) - - -async def kick_command(message: disnake.Message) -> None: - if not message.author.guild_permissions.kick_members: - embed = disnake.Embed( - title="Permission Denied", - description="You don't have permission to use this command.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - if len(message.mentions) < 1: - embed = disnake.Embed( - title="Invalid Usage", - description=f"Please mention a user to kick. Usage: {BOT_PREFIX}kick @user [reason]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - member = message.mentions[0] - reason = " ".join(message.content.split()[2:]) or "No reason provided" - - try: - await member.send( - embed=disnake.Embed( - title="You've wBeen Kicked", - description=f"You were kicked from {message.guild.name}.\nReason: {reason}", - color=color_manager.get_color("Red"), - ) - ) - except: - pass - - await member.kick(reason=reason) - embed = disnake.Embed( - title="User Kicked", - description=f"{member.mention} has been kicked.\nReason: {reason}", - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - -async def ban_command(message: disnake.Message) -> None: - if not message.author.guild_permissions.ban_members: - embed = disnake.Embed( - title="Permission Denied", - description="You don't have permission to use this command.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - if len(message.mentions) < 1: - embed = disnake.Embed( - title="Invalid Usage", - description=f"Please mention a user to ban. Usage: {BOT_PREFIX}ban @user [reason]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - member = message.guild.get_member(message.mentions[0].id) - - if member is None: - embed = disnake.Embed( - title="Member Not Found", - description=f"Please mention a user to ban. Usage: {BOT_PREFIX}ban @user [reason]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - reason = " ".join(message.content.split()[2:]) or "No reason provided" - - try: - await member.send( - embed=disnake.Embed( - title="You've Been Banned", - description=f"You were banned from {message.guild.name}.\nReason: {reason}", - color=color_manager.get_color("Red"), - ) - ) - except: - pass - - await member.ban(reason=reason) - embed = disnake.Embed( - title="User Banned", - description=f"{member.mention} has been banned.\nReason: {reason}", - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - -@debug_command -async def shutdown_command(message: disnake.Message) -> None: - global bot_active - if not message.author.guild_permissions.administrator: - embed = disnake.Embed( - title="Permission Denied", - description="You don't have permission to use this command.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - bot_active = False - await Memo.change_presence(status=disnake.Status.invisible) - - for vc in Memo.voice_clients: - await vc.disconnect() - - embed = disnake.Embed( - title=f"{BOT_NAME} Shutting Down", - description=f"{BOT_NAME} is now offline.", - color=color_manager.get_color("Red"), - timestamp=datetime.datetime.utcnow(), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - -@debug_command -async def start_command(message: disnake.Message) -> None: - global bot_active - if not message.author.guild_permissions.administrator: - embed = disnake.Embed( - title="Permission Denied", - description="You don't have permission to use this command.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - bot_active = True - await Memo.change_presence( - status=disnake.Status.online, - activity=disnake.Game(name=f"Run {BOT_PREFIX}help for help"), - ) - embed = disnake.Embed( - title=f"{BOT_NAME} Starting Up", - description=f"{BOT_NAME} is now online.", - color=color_manager.get_color("Blue"), - timestamp=datetime.datetime.utcnow(), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - -async def charinfo_command(message: disnake.Message) -> None: - - try: - argument_text = " ".join(message.content.split()[1:]) - char_text = argument_text[0] - except IndexError: - await message.channel.send( - embed=disnake.Embed( - title="ERROR", - color=color_manager.get_color("Blue"), - description="Please provide a character to get information about.", - ) - ) - return - - unicode_value = ord(char_text) - char_name = unicodedata.name(char_text, "Could not find name!") - char_category = unicodedata.category(char_text) - - unicode_escape = f"\\u{unicode_value:04x}" - unicode_escape_full = f"\\U{unicode_value:08x}" - python_escape = repr(char_text) - - embed = disnake.Embed( - color=color_manager.get_color("Blue"), - title="Character info", - type="rich", - description=f"Information on character: {char_text}", - ) - - embed.add_field(name="Original character", value=char_text, inline=True) - embed.add_field(name="Character name", value=char_name, inline=True) - embed.add_field(name="Character category", value=char_category, inline=True) - embed.add_field(name="Unicode value", value=f"U+{unicode_value:04X}", inline=True) - embed.add_field(name="Unicode escape", value=unicode_escape, inline=True) - embed.add_field(name="Full Unicode escape", value=unicode_escape_full, inline=True) - embed.add_field(name="Python escape", value=python_escape, inline=True) - - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - - image_path = get_char_image(char_text) - - if image_path: - file = disnake.File(image_path, filename="character.png") - embed.set_thumbnail(url="attachment://character.png") - else: - file = None - - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - - await message.channel.send(embed=embed, file=file) - - if image_path and os.path.exists(image_path): - os.remove(image_path) - - -async def unban_command(message: disnake.Message) -> None: - if not message.author.guild_permissions.administrator: - embed = disnake.Embed( - title="Permission Denied", - description="You don't have permission to use this command.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - if len(message.content.strip().split) < 2: - embed = disnake.Embed( - title="Invalid Usage", - description=f"Please mention a user to unban. Usage: {BOT_PREFIX}unban [user_id]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - member = message.guild.get_member(int(message.content.strip().split()[1])) - - if member is None: - embed = disnake.Embed( - title="Member Not Found", - description=f"Please mention a user to unban. Usage: {BOT_PREFIX}unban [user_id]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - invite = message.channel.create_invite(reason="Invite unbanned user") - - try: - await member.send( - embed=disnake.Embed( - title="You've Been unbanned", - description=f"You were unbanned from {message.guild.name}", - color=color_manager.get_color("Blue"), - ).add_field(name="Invite link", value=invite) - ) - except: - pass - - try: - await message.guild.unban(user=member) - - except disnake.errors.Forbidden as e: - embed = disnake.Embed( - title="Forbidden", - description=f"Could not unban, {e}", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - except disnake.errors.NotFound as e: - embed = disnake.Embed( - title="Not found", - description=f"Could not unban, {e}", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - except disnake.errors.HTTPException as e: - embed = disnake.Embed( - title="Unknown", - description=f"Could not unban, {e}", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - embed = disnake.Embed( - title="User Unbanned", - description=f"{member.mention} has been unbanned.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - -async def timeout_command(message: disnake.Message) -> None: - if not message.author.guild_permissions.moderate_members: - embed = disnake.Embed( - title="Permission Denied", - description="You don't have permission to use this command.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - args = message.content.split()[1:] - if len(args) < 3: - embed = disnake.Embed( - title="Invalid Usage", - description=f"Usage: {BOT_PREFIX}timeout @user [reason]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - member = message.mentions[0] if message.mentions else None - if not member: - embed = disnake.Embed( - title="Invalid Usage", - description="Please mention a user to timeout.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - try: - duration = int(args[1]) - unit = args[2].lower() - - except ValueError: - embed = disnake.Embed( - title="Invalid Usage", - description="Duration must be a number.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - reason = " ".join(args[3:]) if len(args) > 3 else "No reason provided" - - if message.author.top_role <= member.top_role: - embed = disnake.Embed( - title="Permission Denied", - description="You cannot timeout this user as they have an equal or higher role.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - time_units = {"s": "seconds", "m": "minutes", "h": "hours", "d": "days"} - if unit not in time_units: - embed = disnake.Embed( - title="Invalid Usage", - description="Invalid time unit. Use 's' for seconds, 'm' for minutes, 'h' for hours, or 'd' for days.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - time_delta = datetime.timedelta(**{time_units[unit]: duration}) - - try: - await member.timeout(time_delta, reason=reason) - embed = disnake.Embed( - title="User Timed Out", - description=f"{member.mention} has been timed out for {duration}{unit}.\nReason: {reason}", - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - except disnake.errors.Forbidden: - embed = disnake.Embed( - title="Permission Error", - description="I don't have permission to timeout this user.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - except disnake.errors.HTTPException: - embed = disnake.Embed( - title="Error", - description="Failed to timeout the user. The duration might be too long.", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - try: - await member.timeout(time_delta, reason=reason) - embed = disnake.Embed( - title="You were timed out", - description=f"You (aka {member.mention}) have been timed out for {duration}{unit}.", - color=color_manager.get_color("Blue"), - ) - embed.add_field(name="reason", value=reason, inline=True) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await member.send(embed=embed) - - except: - pass - - -async def join_vc_command(message: disnake.Message) -> None: - try: - channel = Memo.get_channel(message.author.voice.channel.id) - await channel.connect() - except Exception as e: - richPrint(e) - - -async def leave_vc_command(message: disnake.Message) -> None: - try: - await message.guild.voice_client.disconnect() - except Exception as e: - pass - - -async def tts_command(message: disnake.Message) -> None: - text = " ".join(message.content.split()[1:]) - - if not text: - embed = disnake.Embed( - title="Missing arguments", - description=f"Please make sure you pass some text for the TTS command. Usage: {BOT_PREFIX}tts [message]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - return - - output_file = f"./temp/audio/{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}.mp3" - - try: - text_to_speech(text, output_file, TTS_MODE) - except Exception as e: - embed = disnake.Embed( - title="Error occurred", - description=f"A issue occurred during the generation of the Text-to-Speech mp3 file! Usage: {BOT_PREFIX}tts [message]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - try: - voice_channel = message.author.voice.channel - except: - embed = disnake.Embed( - title="Join voice channel", - description=f"Please join a voice channel to use this command! Usage: {BOT_PREFIX}tts [message]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - try: - vc = await voice_channel.connect() - except disnake.ClientException: - vc = message.guild.voice_client - - if vc.is_playing(): - vc.stop() - - vc.play( - disnake.FFmpegPCMAudio(source=output_file), - after=lambda e: asyncio.run_coroutine_threadsafe(vc.disconnect(), Memo.loop), - ) - - while vc.is_playing(): - await asyncio.sleep(0.1) - - if os.path.exists(output_file): - os.remove(output_file) - - embed = disnake.Embed( - title="Ended TTS", - description=f"Successfully generated and played TTS file. Disconnecting from <#{voice_channel.id}>", - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - -async def play_command(message: disnake.Message) -> None: - args = message.content.split(" ", 1) - if len(args) < 2: - await message.channel.send("Please provide a YouTube URL or search term.") - return - - query = args[1] - - try: - voice_channel = message.author.voice.channel - if not voice_channel: - raise AttributeError - except AttributeError: - embed = disnake.Embed( - title="Join voice channel", - description="Please join a voice channel to use this command!", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - try: - vc = await voice_channel.connect() - except disnake.ClientException: - vc = message.guild.voice_client - - if vc.is_playing(): - vc.stop() - - try: - with yt_dlp.YoutubeDL({"format": "bestaudio", "noplaylist": "True"}) as ydl: - info = ydl.extract_info(query, download=False) - URL = info["url"] - title = info["title"] - except Exception as e: - embed = disnake.Embed( - title="Error occurred", - description=f"An issue occurred while trying to fetch the audio: {str(e)}", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - vc.play( - disnake.FFmpegPCMAudio( - URL, - **{ - "before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5", - "options": "-vn", - }, - ) - ) - - embed = disnake.Embed( - title="Now Playing", - description=f"Now playing: {title}", - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - -async def profile_command(message: disnake.Message) -> None: - if len(message.mentions) < 1: - embed = disnake.Embed( - title="Invalid Usage", - description=f"Usage: {BOT_PREFIX}profile @user", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - try: - user = message.mentions[0] - - except Exception as e: - embed = disnake.Embed( - title="Invalid Usage", - description=f"Usage: {BOT_PREFIX}profile @user", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - try: - fetched_user = await Memo.fetch_user(user.id) - - except disnake.errors.NotFound as e: - embed = disnake.Embed( - title="Not found", - description=f"Error occurred while fetching user. Usage: {BOT_PREFIX}profile @user", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - except disnake.errors.HTTPException as e: - embed = disnake.Embed( - title="Unknown Error", - description=f"Error occurred while fetching user, but this exception does not have defined behavior. Usage: {BOT_PREFIX}profile @user", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - if user not in message.guild.members: - embed = disnake.Embed( - title="Member not in guild!", - description=f"Please make sure that the user you are searching for exists and is in this guild. Usage: {BOT_PREFIX}profile @user", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - avatar = user.avatar - name = user.display_name - username = user.name - user_id = user.id - status = user.status - creation = user.created_at.strftime("%d/%m/%y %H:%M:%S") - badges = [badge.name for badge in user.public_flags.all()] - banner_url = None - top_role = "<@&" + str(user.roles[-1].id) + ">" - roles: list[str] = [] - print(user.premium_since) - roles_str = "" - - for role in user.roles: - roles.append(f"<@&{role.id}>") - - roles.pop(0) # Removes @everyone role - - for i in range(len(roles)): - roles_str = roles_str + str(roles[i]) - - try: - banner_url = fetched_user.banner.url - except: - pass - - if status == disnake.enums.Status(value="dnd"): - status = "โ›” Do not disturb" - - elif status == disnake.enums.Status(value="online"): - status = "๐ŸŸข Online" - - elif status == disnake.enums.Status(value="idle"): - status = "๐ŸŸก Idle" - - else: - status = "โšซ Offline" - - embed = disnake.Embed( - title=f"{name}'s Profile", - description="Users public discord information, please don't use for bad or illegal purposes!", - ) - embed.add_field(name="Display Name", value=name, inline=True) - embed.add_field(name="Username", value=username, inline=True) - embed.add_field(name="User ID", value=user_id, inline=True) - embed.add_field(name="Creation Time", value=creation, inline=True) - embed.add_field(name="Status", value=status, inline=True) - embed.add_field(name="Badges", value=badges, inline=True) - embed.add_field(name="Top role", value=top_role, inline=True) - embed.add_field( - name="Roles", - value=("No roles" if roles_str.strip() == "" else roles_str), - inline=True, - ) - embed.set_thumbnail( - url=( - avatar - if avatar - else "https://i.pinimg.com/474x/d6/c1/09/d6c109542c43e5b7c6699761c8c78d16.jpg" - ) - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - embed.set_image(url=(banner_url if banner_url != None else "")) - embed.color = color_manager.get_color("Blue") - - await message.channel.send(embed=embed) - - -async def nick_command(message: disnake.Message) -> None: - if ( - not message.author.guild_permissions.administrator - | message.author.guild_permissions.administrator - ): - embed = disnake.Embed( - title="Missing permission", - description=f"Missing required permission `manage_nicknames`. Please run `{BOT_PREFIX}help` for more information!", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - if len(message.mentions) < 1: - embed = disnake.Embed( - title="Invalid Usage", - description=f"Usage: {BOT_PREFIX}nick @user [new_nickname]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - user = message.mentions[0] - - if user not in message.guild.members: - embed = disnake.Embed( - title="Member not in guild!", - description=f"Please make sure that the user you are searching for exists and is in this guild. Usage: {BOT_PREFIX}nick @user [new_nick]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - args = message.content.split(" ") - - try: - await user.edit(nick=" ".join(args[2:])) - embed = disnake.Embed( - title="Successfully updated nickname!", - description=f"Successfully updated nickname of <@{user.id}> to {" ".join(args[2:])}", - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - except Exception as e: - embed = disnake.Embed( - title="Issue occurred", - description=f"An issue occurred during this operation. This exception was caught by a general handler. {e}", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - -async def feedback_command(message: disnake.Message) -> None: - args = message.content.split()[1:] - feedback_text = " ".join(args) - - if len(args) < 1: - embed = disnake.Embed( - title="Invalid Usage", - description=f"Usage: {BOT_PREFIX}feedback [message]", - color=color_manager.get_color("Red"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - return - - feedback.add_feedback(message.author.id, feedback_text) - - embed = disnake.Embed( - color=color_manager.get_color("Blue"), - title="Recorded Feedback", - description=f"Recorded feedback from <@{message.author.id}>", - ) - embed.add_field(name="Message", value=feedback_text, inline=False) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - -@debug_command -async def restart_command(message: disnake.Message) -> None: - embed = disnake.Embed( - title="Restarting", - description=f"The restart will take approximately 10 to 30 seconds on average.", - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await message.channel.send(embed=embed) - - for vc in Memo.voice_clients: - await vc.disconnect(force=True) - - if hasattr(Memo.http, "_client_session") and Memo.http._client_session: - await Memo.http._client_session.close() - await asyncio.sleep(0.5) - - try: - await Memo.close() - except: - pass - - os.execv(sys.executable, ["python"] + sys.argv) - - -async def translate_command(message: disnake.Message) -> None: - translate_text = message.content.split(" ", 1)[1] - - try: - translator = GoogleTranslator(source="auto", target="en") - translated_text = translator.translate(translate_text) - - embed = disnake.Embed( - title="Translation", - description=translated_text, - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - - await message.channel.send(embed=embed) - - except Exception as e: - await message.channel.send(f"An error occurred: {str(e)}") - - -async def ping_command(message: disnake.Message) -> None: - bot_latency = fetch_latency(Memo) - - embed = disnake.Embed( - title="Bot latency", - description=f"The current bot latency is approximately `{bot_latency}ms`", - color=color_manager.get_color("Blue"), - ) - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - - await message.channel.send(embed=embed) - - -async def server_command(message: disnake.Message) -> None: - try: - guild = message.guild - - if not guild.me.guild_permissions.administrator: - embed = disnake.Embed( - title="Permission Denied", - description="You need administrator permissions to gather all server stats.", - color=color_manager.get_color("Red"), - ) - embed.set_footer(text=FOOTER_TEXT, icon_url=FOOTER_ICON) - await message.channel.send(embed=embed) - return - - if Memo.intents.members: - try: - await guild.chunk() - except disnake.HTTPException: - log_info("Failed to fetch all members. Some stats may be incomplete.") - - embed = disnake.Embed( - title=f"{guild.name} Server Stats", - color=color_manager.get_color("Blue"), - timestamp=datetime.datetime.utcnow(), - ) - - if guild.icon: - embed.set_thumbnail(url=guild.icon.url) - - embed.add_field(name="Server ID", value=guild.id, inline=True) - embed.add_field( - name="Owner", - value=guild.owner.mention if guild.owner else "Unknown", - inline=True, - ) - embed.add_field( - name="Created At", - value=f"", - inline=True, - ) - embed.add_field( - name="Boost Level", value=f"Level {guild.premium_tier}", inline=True - ) - embed.add_field( - name="Boost Count", value=guild.premium_subscription_count, inline=True - ) - - total_members = guild.member_count - bots = sum(1 for m in guild.members if m.bot) - embed.add_field(name="Members", value=total_members, inline=True) - embed.add_field(name="Bots", value=bots, inline=True) - - embed.add_field(name="Total Channels", value=len(guild.channels), inline=True) - embed.add_field(name="Roles", value=len(guild.roles), inline=True) - - if guild.description: - embed.add_field(name="Description", value=guild.description, inline=False) - if guild.banner: - embed.set_image(url=guild.banner.url) - - embed.set_footer(text=FOOTER_TEXT, icon_url=FOOTER_ICON) - - await message.channel.send(embed=embed) - - except disnake.Forbidden: - await send_error_embed( - message, - "Permission Error", - "I don't have permission to access some server information.", - FOOTER_TEXT=FOOTER_TEXT, - FOOTER_ICON=FOOTER_ICON, - color_manager=color_manager, - ) - except disnake.HTTPException as e: - await send_error_embed( - message, - "HTTP Error", - f"An HTTP error occurred: {e}", - FOOTER_TEXT=FOOTER_TEXT, - FOOTER_ICON=FOOTER_ICON, - color_manager=color_manager, - ) - except Exception as e: - await send_error_embed( - message, - "Unexpected Error", - f"An unexpected error occurred: {e}", - FOOTER_TEXT=FOOTER_TEXT, - FOOTER_ICON=FOOTER_ICON, - color_manager=color_manager, - ) - log_info(f"Unexpected error in server command: {e}") - - -async def joke_command(message: disnake.Message) -> None: - joke = await fetch_random_joke() - - if joke: - embed = disnake.Embed( - color=color_manager.get_color("Blue"), title="Dad joke", description=joke - ) - embed.set_footer(text=FOOTER_TEXT, icon_url=FOOTER_ICON) - await message.channel.send(embed=embed) - else: - send_error_embed( - message=message, - title="Error", - description="Sorry, I couldn't fetch a joke at the moment.", - FOOTER_TEXT=FOOTER_TEXT, - FOOTER_ICON=FOOTER_ICON, - color_manager=color_manager, - ) - - -async def coin_command(message: disnake.Message) -> None: - embed = disnake.Embed( - title="Coin Flip", - description="The coin is spinning...", - color=color_manager.get_color("Blue"), - ) - embed.set_thumbnail( - url="https://media.istockphoto.com/id/141325539/vector/heads-or-tails.jpg?s=612x612&w=0&k=20&c=V8GPGyuVWFMl4awXzlCp1lhYE5hKiKBybnZocR1i7Uw=" - ) - embed.set_footer(text=FOOTER_TEXT, icon_url=FOOTER_ICON) - - message = await message.channel.send(embed=embed) - - await asyncio.sleep(1.5) - - result = "Heads" if random.choice([True, False]) else "Tails" - embed.description = f"The coin landed on: **{result}**!" - - embed.set_thumbnail( - url=( - "https://e7.pngegg.com/pngimages/547/992/png-clipart-computer-icons-coin-gold-pile-of-gold-coins-gold-coin-gold-thumbnail.png" - if result == "Heads" - else "https://e7.pngegg.com/pngimages/443/910/png-clipart-gold-peso-coin-logo-coin-philippine-peso-peso-coin-gold-coin-text-thumbnail.png" - ) - ) - - await message.edit(embed=embed) - - -async def quote_command(message: disnake.Message) -> None: - quote = fetch_quote_of_the_day() - text = quote[0] - author = quote[1] - - embed = disnake.Embed( - title="Quote of the day", - description=f'"{text}" - {author}', - color=color_manager.get_color("Blue"), - ) - embed.set_footer(text=FOOTER_TEXT, icon_url=FOOTER_ICON) - - await message.channel.send(embed=embed) - - -async def vc_mute_command(message: disnake.Message) -> None: - if len(message.mentions) < 1: - await message.channel.send("Please mention a valid member.") - return - - member = message.guild.get_member(message.mentions[0].id) - - if not member: - await message.channel.send("Please mention a valid member.") - return - - if not member.voice: - await message.channel.send("That user is not in a voice channel.") - return - - member.edit(mute=True) - - -async def vc_unmute_command(message: disnake.Message) -> None: - if len(message.mentions) < 1: - await message.channel.send("Please mention a valid member.") - return - - member = message.guild.get_member(message.mentions[0].id) - - if not member: - await message.channel.send("Please mention a valid member.") - return - - member.edit(mute=False) - - -async def vc_deafen_command(message: disnake.Message) -> None: - if len(message.mentions) < 1: - await message.channel.send("Please mention a valid member.") - return - - member = message.guild.get_member(message.mentions[0].id) - - if not member: - await message.channel.send("Please mention a valid member.") - return - - member.edit(deafen=True) - - -async def vc_undeafen_command(message: disnake.Message) -> None: - if len(message.mentions) < 1: - await message.channel.send("Please mention a valid member.") - return - - member = message.guild.get_member(message.mentions[0].id) - - if not member: - await message.channel.send("Please mention a valid member.") - return - - member.edit(deafen=False) - - -@Memo.event -async def on_message_delete(message: disnake.Message): - if message.author == Memo.user: - return - - channel = Memo.get_channel(LOGGING_CHANNEL_ID) - if not channel: - return - - embed = disnake.Embed( - title="Message Deleted", - color=color_manager.get_color("Red"), - timestamp=datetime.datetime.utcnow(), - ) - embed.add_field(name="Author", value=message.author.mention) - embed.add_field(name="Channel", value=message.channel.mention) - embed.add_field(name="Content", value=message.content or "No content") - - if message.attachments: - embed.add_field( - name="Attachments", value="\n".join([a.url for a in message.attachments]) - ) - - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await channel.send(embed=embed) - - -@Memo.event -async def on_message_edit(before: disnake.Message, after: disnake.Message): - if before.author == Memo.user: - return - - channel = Memo.get_channel(LOGGING_CHANNEL_ID) - if not channel: - return - - if before.content == after.content: - return - - embed = disnake.Embed( - title="Message Edited", - color=color_manager.get_color("Orange"), - timestamp=datetime.datetime.utcnow(), - ) - embed.add_field(name="Author", value=before.author.mention) - embed.add_field(name="Channel", value=before.channel.mention) - embed.add_field(name="Before", value=before.content or "No content") - embed.add_field(name="After", value=after.content or "No content") - - embed.set_footer( - text=FOOTER_TEXT, - icon_url=FOOTER_ICON, - ) - await channel.send(embed=embed) - - -@Memo.event -async def on_member_join(member: disnake.Member): - channel = member.guild.text_channels[0] - - if not channel: - return - - embed = disnake.Embed( - title=f"Welcome to the server {member.mention}!", - color=color_manager.get_color("Green"), - timestamp=datetime.datetime.utcnow(), - ) - - embed.add_field( - name="Account Created At", value=f"" - ) - embed.set_thumbnail(member.avatar.url) - embed.set_footer(FOOTER_TEXT, FOOTER_ICON) - await channel.send(embed=embed) - - -@Memo.event -async def on_member_remove(member: disnake.Member): - channel = member.guild.text_channels[0] - - if not channel: - return - - embed = disnake.Embed( - title=f"Goodbye {member.mention}!", - color=color_manager.get_color("Red"), - timestamp=datetime.datetime.utcnow(), - ) - - embed.set_thumbnail(member.avatar.url) - embed.set_footer(FOOTER_TEXT, FOOTER_ICON) - await channel.send(embed=embed) - - -@commands.slash_command(name="help", description="Shows the command help embed") -async def slash_help(interaction: disnake.ApplicationCommandInteraction): - embed = fetch_help_embed( - color_manager, BOT_NAME, BOT_VERSION, BOT_PREFIX, FOOTER_TEXT, FOOTER_ICON - ) - await interaction.response.send_message(embed=embed, ephemeral=True) - - -@commands.slash_command( - name="info", description="Shows important information about the bot." -) -async def slash_info(interaction: disnake.ApplicationCommandInteraction): - embed = fetch_info_embed( - color_manager, BOT_NAME, BOT_VERSION, BOT_PREFIX, FOOTER_TEXT, FOOTER_ICON - ) - await interaction.response.send_message(embed=embed, ephemeral=True) -``` - -# src\utils\helper.py - -```py -from typing import Optional, Tuple, List, Dict, Any, Union -from rich import print as richPrint -import json -import datetime -import disnake -import aiohttp -import requests -import ssl -import os -import urllib3 -from disnake.ext import commands -import tempfile -from langdetect import detect, DetectorFactory -from langdetect.lang_detect_exception import LangDetectException -from PIL import Image, ImageDraw, ImageFont -from collections import Counter -from gtts import gTTS -import hashlib - - -class SHA3: - @staticmethod - def salt_hash( - input_str: str, salt: Optional[bytes] = None - ) -> Tuple[Union[str, bytes], bytes]: - """ - Generate a salted hash of the input string using PBKDF2 HMAC-SHA256. - """ - if salt is None: - salt = SHA3.generate_salt(size=32, hex=True) - encoded_str = input_str.encode("utf-8") - hashed = hashlib.pbkdf2_hmac("sha256", encoded_str, salt, 100000) - return salt, hashed - - @staticmethod - def hash_256(input_str: str) -> str: - """ - Generate a SHA-256 hash of the input string. - """ - encoded_str = input_str.encode("utf-8") - return hashlib.sha3_256(encoded_str).hexdigest() - - @staticmethod - def hash_384(input_str: str) -> str: - """ - Generate a SHA-384 hash of the input string. - """ - encoded_str = input_str.encode("utf-8") - return hashlib.sha3_384(encoded_str).hexdigest() - - @staticmethod - def hash_512(input_str: str) -> str: - """ - Generate a SHA-512 hash of the input string. - """ - encoded_str = input_str.encode("utf-8") - return hashlib.sha3_512(encoded_str).hexdigest() - - @staticmethod - def hash_224(input_str: str) -> str: - """ - Generate a SHA-224 hash of the input string. - """ - encoded_str = input_str.encode("utf-8") - return hashlib.sha3_224(encoded_str).hexdigest() - - @staticmethod - def generate_salt(size: int = 32, hex: bool = True) -> Union[str, bytes]: - """ - Generate a random salt of the specified size. - """ - salt = os.urandom(size).hex() if hex else os.urandom(size) - return salt - - @staticmethod - def compare_hash_to_salted( - stored_salt: bytes, salted_and_hashed: bytes, hashed: str - ) -> bool: - """ - Compare a salted hash to a stored salted hash. - """ - _, new_hash = SHA3.salt_hash(hashed, stored_salt) - return new_hash == salted_and_hashed - - -def set_langdetect_seed(seed: int = 0) -> None: - """ - Set the seed for language detection to ensure consistent results. - """ - DetectorFactory.seed = seed - - -def text_to_speech(text: str, output_file: str, tts_mode: str) -> None: - """ - Convert text to speech and save it to a file. - """ - try: - slow = tts_mode.lower() == "slow" - - language = detect(text) - - tts = gTTS(text=text, lang=language, slow=slow) - tts.save(output_file) - except Exception as e: - richPrint(f"ERROR_LOG ~ Text-to-speech conversion failed: {e}") - - -def load_config(config_path: str = "config.json") -> Dict[str, Any]: - """ - Load the configuration from a JSON file. - """ - try: - with open(config_path, "r") as f: - return json.load(f) - except FileNotFoundError: - richPrint( - f"WARNING: Config file not found at {config_path}. Using default configuration." - ) - return {"default_prefix": "?", "guilds": {}} - except json.JSONDecodeError: - richPrint( - f"ERROR: Invalid JSON in config file {config_path}. Using default configuration." - ) - return {"default_prefix": "?", "guilds": {}} - - -def save_config(config: Dict[str, Any], config_path: str = "config.json") -> None: - """ - Save the configuration to a JSON file. - """ - try: - with open(config_path, "w") as f: - json.dump(config, f, indent=4) - except IOError as e: - richPrint(f"ERROR: Failed to save config to {config_path}: {e}") - - -def log_info(value: str = "None", startup_log: bool = False) -> None: - """ - Log an information message with a timestamp. - """ - timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - print(timestamp, end=" ") - richPrint( - f"[bold][blue]INFO[/blue][/bold] {"[purple]startup.[/purple]" if startup_log else ""}{value}" - ) - - -def fetch_latency(client: commands.Bot, shouldRound: bool = True) -> float: - """ - Fetch the latency of the Discord client. - """ - latency = client.latency * 1000 - return round(latency) if shouldRound else latency - - -async def send_error_embed( - message: disnake.Message, - title: str, - description: str, - FOOTER_TEXT: str, - FOOTER_ICON: str, - color_manager: "ColorManager", -) -> None: - """ - Send an error embed to the specified Discord message channel. - """ - embed = disnake.Embed( - title=title, - description=description, - color=color_manager.get_color("Red"), - ) - embed.set_footer(text=FOOTER_TEXT, icon_url=FOOTER_ICON) - await message.channel.send(embed=embed) - - -def get_char_image( - char: str, bg: str = "white", fg: str = "black", format: str = "png" -) -> Optional[str]: - """ - Generate an image of a single character. - """ - try: - img = Image.new("RGB", (200, 200), color=bg) - d = ImageDraw.Draw(img) - - try: - font = ImageFont.truetype("arial.ttf", 120) - except IOError: - font = ImageFont.load_default() - - d.text((100, 100), char, font=font, fill=fg, anchor="mm") - - with tempfile.NamedTemporaryFile( - delete=False, suffix=f".{format}" - ) as temp_file: - img.save(temp_file, format=format.upper()) - temp_file_path = temp_file.name - - return temp_file_path - except Exception as e: - richPrint(f"ERROR: Failed to generate character image: {e}") - return None - - -def detect_language(text: str) -> str: - """ - Detect the language of the given text using multiple attempts for improved accuracy. - """ - try: - detections = [detect(text) for _ in range(5)] - most_common = Counter(detections).most_common(1)[0][0] - return most_common - except LangDetectException as e: - richPrint(f"ERROR: Language detection failed: {e}") - return "unknown" - - -def fetch_help_embed( - color_manager: "ColorManager", - bot_name: str, - bot_version: str, - bot_prefix: str, - footer_text: str, - footer_icon: str, -) -> disnake.Embed: - """ - Create and return a help embed for the bot. - """ - help_embed = disnake.Embed( - color=color_manager.get_color("Blue"), - title=f"{bot_name} Help Information", - description=f"Here are the available commands (prefix: {bot_prefix}):", - ) - help_embed.set_footer(text=footer_text, icon_url=footer_icon) - - commands = { - "help": {"desc": "Show this help message", "usage": f"{bot_prefix}help"}, - "charinfo": { - "desc": "Shows information and a image of the character provided", - "usage": f"{bot_prefix}charinfo [character]", - }, - "tts": { - "desc": "Join the vc you are in and uses Text-to-Speech to say your text", - "usage": f"{bot_prefix}tts [input_text]", - }, - "nick": { - "desc": "Changes guild specific username of a member (Mod only)", - "usage": f"{bot_prefix}nick @user [new_nick]", - }, - "feedback": { - "desc": "Adds your feedback to our database", - "usage": f"{bot_prefix}feedback [message]", - }, - "play": { - "desc": "Plays a song in the voice channel you are in", - "usage": f"{bot_prefix}play [youtube_url]", - }, - "profile": { - "desc": "Gets information about the user", - "usage": f"{bot_prefix}profile @user", - }, - "server": { - "desc": "Gets information about the server", - "usage": f"{bot_prefix}server", - }, - "joke": { - "desc": "Fetches a random dad joke", - "usage": f"{bot_prefix}joke", - }, - "coin": { - "desc": "Flips a coin, and lands on heads or tails", - "usage": f"{bot_prefix}coin", - }, - "quote": { - "desc": "Fetches a random quote of the day", - "usage": f"{bot_prefix}quote", - }, - "ping": { - "desc": "Gets the ping (latency) of the Discord Bot", - "usage": f"{bot_prefix}ping", - }, - "translate": { - "desc": "Translates the provided text to english", - "usage": f"{bot_prefix}translate [text]", - }, - "timeout": { - "desc": "Timeout a user for a specified duration (Mod only)", - "usage": f"{bot_prefix}timeout @user [reason]", - }, - "kick": { - "desc": "Kick a user from the server (Mod only)", - "usage": f"{bot_prefix}kick @user [reason]", - }, - "ban": { - "desc": "Ban a user from the server (Admin only)", - "usage": f"{bot_prefix}ban @user [reason]", - }, - "unban": { - "desc": "Unbans a user from the server (Admin only)", - "usage": f"{bot_prefix}unban [user_id]", - }, - } - - for cmd, info in commands.items(): - help_embed.add_field( - name=f"{bot_prefix}{cmd}", - value=f"{info['desc']}\nUsage: `{info['usage']}`", - inline=False, - ) - - return help_embed - - -def fetch_info_embed( - color_manager: "ColorManager", bot_name: str, bot_version: str, bot_prefix: str -) -> disnake.Embed: - """ - Create and return an info embed for the bot. - """ - info_embed = disnake.Embed( - color=color_manager.get_color("Blue"), - title=f"{bot_name} v{bot_version} Info", - description=f"Here is some general information about the bot, please keep in mind that the bot is in development.", - ) - - info_embed.add_field(name="Command Information", value=f"Prefix: `{bot_prefix}`") - - -async def fetch_random_joke() -> Optional[str]: - """ - Fetch a random joke from an API. - """ - url = "https://icanhazdadjoke.com/" - headers = {"Accept": "application/json"} - - async with aiohttp.ClientSession() as session: - async with session.get(url, headers=headers) as response: - if response.status == 200: - data = await response.json() - return data["joke"] - else: - return None - - -def fetch_quote_of_the_day() -> Union[Tuple[str, str], str]: - """ - Fetch the quote of the day from an API. - Returns either a tuple of (quote, author) or an error message string. - """ - urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - url = "https://api.quotable.io/random" - - try: - context = ssl.create_default_context() - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - - response = requests.get(url, verify=False) - response.raise_for_status() - data = response.json() - - quote = data["content"] - author = data["author"] - - return quote, author - - except requests.RequestException as e: - return f"An error occurred: {e}" - - -class ColorManager: - """ - Manage colors for Discord embeds and other color-related functionality. - """ - - def __init__(self, config: dict): - """ - Initialize the ColorManager with a configuration dictionary. - - Args: - config (dict): A dictionary containing color configurations. - """ - self.colors: dict = config.get("colors", {}) - - def get_color(self, color_name: str) -> int: - """ - Get the integer representation of a color by its name. - - Args: - color_name (str): The name of the color to retrieve. - - Returns: - int: The integer representation of the color. - - Raises: - ValueError: If the color name is not found in the configuration. - """ - if color_name not in self.colors: - raise ValueError(f"Color '{color_name}' not found in configuration") - return int(self.colors[color_name].lstrip("#"), 16) - - def list_colors(self) -> list: - """ - Get a list of all available color names. - - Returns: - list: A list of color names. - """ - return list(self.colors.keys()) - - def create_color_embed( - self, title: str, description: str, color_name: str - ) -> disnake.Embed: - """ - Create a Discord embed with the specified color. - - Args: - title (str): The title of the embed. - description (str): The description of the embed. - color_name (str): The name of the color to use for the embed. - - Returns: - discord.Embed: The created Discord embed. - """ - try: - color = self.get_color(color_name) - return disnake.Embed(title=title, description=description, color=color) - except ValueError as e: - richPrint(f"ERROR: Failed to create color embed: {e}") - return disnake.Embed(title=title, description=description) -``` - -# website\404.html - -```html - - - - - - 404 - Page Not Found | Memo Bot - - - - - - - - -
-
-

404

-

Oops! Page not found

-

It seems like Memo Bot couldn't find the page you're looking for.

-
- - Return to Homepage - - -
-
-
- - - - - -``` - -# website\articles\add-feedback.html - -```html - - - - - - Memo Bot - Feedback Guide - - - - - - - - - - - - - - - - - -
-
-
-
-
-

Submitting Feedback for Memo Bot

-
-
- -
-
- Author Avatar -
-

Nerd Bear

-

Memo Bot Developer

-
-
- -
- - - - - โ€ข - 3 min read -
- -
-

The Importance of Your Feedback

-

Your feedback is crucial for the continuous improvement of Memo Bot. Whether you've encountered a bug, have a feature request, or simply want to share your thoughts, we want to hear from you! The feedback command makes it easy to submit your input directly through Discord.

- -

Using the Feedback Command

-

Submitting feedback is straightforward with the ?feedback command. Here's how to use it:

-
?feedback [your message]
-

Replace [your message] with your actual feedback. Be as detailed as possible to help us understand your thoughts or the issue you're experiencing.

- -

Examples of Good Feedback

-

Here are some examples of effective feedback submissions:

-
    -
  • ?feedback The ?play command sometimes fails to join the voice channel. Could you look into this?
  • -
  • ?feedback I love the ?profile command! It would be great if it could also show the user's top 3 most active channels.
  • -
  • ?feedback The bot seems to lag when processing commands in servers with over 1000 members. Any way to optimize this?
  • -
- -

What Happens After You Submit Feedback

-

After submitting your feedback:

-
    -
  1. Your feedback is securely stored in our database.
  2. -
  3. The development team regularly reviews all feedback submissions.
  4. -
  5. Your input may influence future updates and improvements to Memo Bot.
  6. -
  7. For urgent issues, consider also reaching out through our support channels.
  8. -
- -

Best Practices for Submitting Feedback

-
    -
  • Be specific: Provide as much detail as possible about your experience or suggestion.
  • -
  • One idea per submission: If you have multiple suggestions, submit them separately for easier processing.
  • -
  • Be constructive: Explain not just what you dislike, but how you think it could be improved.
  • -
  • Include context: Mention your server size, the command you were using, or any relevant settings.
  • -
- -

Your feedback plays a vital role in shaping the future of Memo Bot. We appreciate every submission and take your input seriously in our development process.

- -
-

Pro Tip:

-

If you're reporting a bug, try to include steps to reproduce the issue. This helps our development team identify and fix the problem more quickly!

-
-
-
-
- -
-

Comments

-

No comments could be found for this article

-
-
- - - - - - -``` - -# website\articles\articles.html - -```html - - - - - - Memo Bot - Articles - - - - - - - - - - - - - - - - - -
-

Memo Bot Articles

- -
- -
-
-

Configuration Guide

-

Learn how to configure Memo Bot for your server's needs.

-
-
- Author Avatar - Nerd Bear -
- 5 min read -
- Read More โ†’ -
-
- - -
-
-

Commands Guide

-

Explore all available commands and how to use them effectively.

-
-
- Author Avatar - Nerd Bear -
- 8 min read -
- Read More โ†’ -
-
- - -
-
-

Submitting Feedback

-

Learn how to submit feedback to help improve Memo Bot.

-
-
- Author Avatar - Nerd Bear -
- 3 min read -
- Read More โ†’ -
-
-
-
- - - - - - -``` - -# website\articles\commands-guide.html - -```html - - - - - - Memo Bot - Commands Guide - - - - - - - - - - - - - - - - - -
-
-
-
-
-

Memo Bot Commands Guide

-
-
- -
-
- Author Avatar -
-

Nerd Bear

-

Memo Bot Developer

-
-
- -
- - - - - โ€ข - 4 min read -
- -
-

Getting Started with Memo Bot Commands

-

Memo Bot comes packed with a variety of commands to help you manage your server and engage with your community. In this guide, we'll walk you through the most important commands and how to use them effectively.

- -

Basic Command Structure

-

All Memo Bot commands start with a prefix. By default, this prefix is set to "?", but it can be customized in the config file. Here's the basic structure of a command:

-
?commandName [argument1] [argument2] ...
- -

Essential Commands

- -

1. Help Command

-

The help command is your go-to for information about all available commands:

-
?help
-

This will display a list of all available commands along with a brief description of each.

- -

2. Moderation Commands

-

Memo Bot offers several moderation commands to help you manage your server:

-
    -
  • Kick: ?kick @user [reason]
  • -
  • Ban: ?ban @user [reason]
  • -
  • Unban: ?unban @user
  • -
  • Timeout: ?timeout @user <duration> <unit> [reason]
  • -
-

Remember, you need the appropriate permissions to use these commands.

- -

3. Fun and Utility Commands

-

Memo Bot also includes commands for entertainment and utility:

-
    -
  • Character Info: ?charinfo [character]
  • -
  • Text-to-Speech: ?tts [message]
  • -
  • Play Music: ?play [youtube_url]
  • -
  • User Profile: ?profile @user
  • -
- -

4. Bot Management Commands

-

For server administrators, there are commands to manage the bot itself:

-
    -
  • Shutdown: ?shutdown
  • -
  • Start: ?start
  • -
  • Change Nickname: ?nick @user [new_nickname]
  • -
- -

Advanced Usage Tips

-

Here are some tips to help you get the most out of Memo Bot:

-
    -
  • Use the ?help command followed by a specific command name for detailed usage information.
  • -
  • When using moderation commands, always provide a reason to maintain transparency.
  • -
  • The ?tts command is great for making announcements in voice channels.
  • -
  • Use ?profile to quickly get information about a user, including their roles and join date.
  • -
- -

Troubleshooting Common Issues

-

If you're experiencing issues with commands, try these steps:

-
    -
  1. Ensure you're using the correct prefix (default is "?").
  2. -
  3. Check that you have the necessary permissions for the command.
  4. -
  5. Verify that the bot is online and has the required permissions in your server.
  6. -
  7. If a command isn't working, try restarting the bot using the ?shutdown and ?start commands (admin only).
  8. -
- -

Remember, the key to effectively using Memo Bot is experimentation. Don't be afraid to try out different commands and see how they can benefit your server!

- -
-

Pro Tip:

-

Create a dedicated channel for bot commands to keep your main chat channels clutter-free. This also helps new users learn how to interact with the bot by seeing others use it.

-
-
-
-
- -
-

Comments

-

No comments could be found for this article

-
-
- - - - - - -``` - -# website\articles\config-guide.html - -```html - - - - - - Memo Bot - Config Guide - - - - - - - - - - - - - - - - - -
-
-
-
-
-

Memo Bot Config Guide

-
-
- -
-
- Author Avatar -
-

Nerd Bear

-

Memo Bot Developer

-
-
- -
- - - - - โ€ข - 5 min read -
- -
-

Understanding the Config File

-

The config file for Memo Bot is a JSON file named config.json. It contains various settings that control the bot's behavior. Let's dive into its structure and how you can customize it to suit your needs.

- -
-{
-    "defaults": {
-        "prefix": "?",
-        "footer_text": "This bot is created and hosted by Nerd bear",
-        "footer_icon": "https://as2.ftcdn.net/v2/jpg/01/17/00/87/1000_F_117008730_0Dg5yniuxPQLz3shrJvLIeBsPfPRBSE1.jpg"
-    },
-    "bot_version": "0.4.6",
-    "bot_name": "Memo",
-    "tts_mode": "fast",
-    "log_channel_id": "1290060885485948950",
-    "tts_detector_factory_seed": "0",
-    "colors": {
-        "Red": "#FFB3BA",
-        "Blue": "#B5DEFF"
-        // ... other colors ...
-    },
-    "guilds": {
-        "1288144110880030795": {
-            "prefix": "*"
-        }
-    }
-}
-                    
- -

Key Sections:

-
    -
  • defaults: Contains default settings for the bot.
  • -
  • bot_version: The current version of the bot.
  • -
  • bot_name: The name of the bot.
  • -
  • tts_mode: The mode for text-to-speech functionality.
  • -
  • log_channel_id: The ID of the channel where logs will be sent.
  • -
  • colors: A list of color codes used for various bot functions.
  • -
  • guilds: Server-specific settings, such as custom prefixes.
  • -
- -

How to Modify the Config File

-

Customizing your Memo Bot installation is straightforward. Follow these steps to modify the config file:

-
    -
  1. Locate the config.json file in your bot's root directory.
  2. -
  3. Open the file with a text editor (e.g., Notepad++, Visual Studio Code).
  4. -
  5. Make your desired changes, ensuring to maintain the correct JSON format.
  6. -
  7. Save the file after making changes.
  8. -
  9. Restart the bot for the changes to take effect.
  10. -
- -

Common Modifications

-

Let's explore some common modifications you might want to make to your Memo Bot configuration:

- -

Changing the Default Prefix

-

To change the default prefix, modify the "prefix" value under "defaults":

-
-"defaults": {
-    "prefix": "!",  // Change '?' to your desired prefix
-    ...
-}
-                    
- -

Setting a Custom Prefix for a Specific Server

-

To set a custom prefix for a specific server, add or modify an entry under "guilds":

-
-"guilds": {
-    "YOUR_SERVER_ID": {
-        "prefix": "$"  // Replace with your desired prefix
-    }
-}
-                    
- -

Changing the Bot Name

-

To change the bot's name, modify the "bot_name" value:

-
-"bot_name": "Your New Bot Name",
-                    
- -

Modifying Colors

-

To change or add colors, modify the "colors" section:

-
-"colors": {
-    "Red": "#FF0000",
-    "Blue": "#0000FF",
-    "CustomColor": "#HEXCODE"
-}
-                    
- -

Important Notes

-

Before you start tweaking your config file, keep these important points in mind:

-
    -
  • Always backup your config file before making changes.
  • -
  • Ensure your JSON syntax is correct to avoid errors.
  • -
  • Some changes may require a bot restart to take effect.
  • -
  • Be cautious when changing critical settings like log_channel_id.
  • -
- -

Remember, customizing your Memo Bot is all about making it work best for your server and community. Don't be afraid to experiment with different settings to find what works best for you!

- -
-

Pro Tip:

-

Consider using a JSON validator tool to check your config file for syntax errors before restarting your bot. This can save you time and prevent potential issues.

-
-
-
-
- -
-

Comments

-

No comments could be found for this article

-
-
- - - - - - -``` - -# website\commands\ban\index.html - -```html - - - - - - Memo Bot - Ban Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Moderation -
-

Ban Command

-

Permanently remove a user from your server

-
- - -
- -
- -
-

Usage

-
- ?ban @user [reason] -
-

The ban command allows moderators to permanently remove a user from the server. The user cannot rejoin unless unbanned.

-
-

Arguments:

-
    -
  • @user - The user to ban (mention required)
  • -
  • reason - Optional: Reason for the ban
  • -
-
-
- - -
-

Examples

-
-
-

Basic ban:

-
?ban @UserName
-
-
-

Ban with reason:

-
?ban @UserName 7 Repeated violations of server rules
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

User Banned

-

@UserName has been banned.

-

Reason: Repeated violations of server rules

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - Ban Members -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Additional Notes

-
    -
  • The bot must have a role higher than the banned user
  • -
  • Banned users cannot rejoin with invites
  • -
  • The bot will attempt to DM the user the reason for their ban
  • -
  • Server owner cannot be banned
  • -
  • Ban is logged in the server's audit log
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\charinfo\index.html - -```html - - - - - - Memo Bot - Character Info Command - - - - - - - - - - - - - - - - -
- -
- -

Character Info Command

-

Get detailed Unicode information about any character

-
- - -
- -
- -
-

Usage

-
- ?charinfo [character] -
-

The charinfo command provides detailed Unicode information about any character, including emojis, special characters, and letters.

-
-

Arguments:

-
    -
  • character - The character you want to analyze
  • -
-
-
- - -
-

Information Provided

-
-
-

Basic Info:

-
    -
  • Original character
  • -
  • Character name
  • -
  • Character category
  • -
-
-
-

Unicode Data:

-
    -
  • Unicode value (U+XXXX)
  • -
  • Unicode escape sequence
  • -
  • Full Unicode escape
  • -
-
-
-

Additional Info:

-
    -
  • Python escape sequence
  • -
  • Character preview
  • -
  • Visual representation
  • -
-
-
-
- - -
-

Examples

-
-
-

Get info about an emoji:

-
?charinfo ๐Ÿ˜€
-
-
-

Get info about a special character:

-
?charinfo โ™ 
-
-
-

Get info about a letter:

-
?charinfo A
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Character info

-

Information on character: A

-

Original character: A

-

Character name: LATIN CAPITAL LETTER A

-

Character category: Lu

-

Unicode value: U+0041

-

Unicode escape: \u0041

-

Full Unicode escape: \U00000041

-

Python escape: 'A'

-
-
-
-
-
-
- Memo Bot -
-
-
-

ERROR

-

Please provide a character to get information about.

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Supported Characters

-
    -
  • Standard letters
  • -
  • Numbers
  • -
  • Emojis
  • -
  • Special symbols
  • -
  • Unicode characters
  • -
-
- -
-

Common Uses

-
    -
  • Finding Unicode values
  • -
  • Getting character names
  • -
  • Learning character categories
  • -
  • Getting escape sequences
  • -
  • Character analysis
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\coin\index.html - -```html - - - - - - Memo Bot - Coin Flip Command - - - - - - - - - - - - - - - - -
- -
- -

Coin Flip Command

-

Flip a virtual coin and get heads or tails

-
- - -
- -
- -
-

Usage

-
- ?coin -
-

The coin command flips a virtual coin and displays the result. The bot will first announce it's flipping the coin, then show whether it landed on heads or tails.

-
-

Arguments:

-
    -
  • None required - Simply type ?coin
  • -
-
-
- - -
-

How It Works

-
-
-

The coin flip happens in two steps:

-
    -
  1. Bot announces "Flipping a coin..."
  2. -
  3. Result is shown: "The coin landed on heads/tails!"
  4. -
-
-
-
- - -
-

Example

-
-
-

Flip a coin:

-
?coin
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Flipping a coin...

-
-
-
-
-
-
- Memo Bot -
-
-
-

The coin landed on heads! ๐Ÿช™

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Possible Results

-
    -
  • Heads
  • -
  • Tails
  • -
-
- -
-

Best Used For

-
    -
  • Making random decisions
  • -
  • Settling debates
  • -
  • Simple games
  • -
  • Quick choices
  • -
  • Team selection
  • -
-
- -
-

Good to Know

-
    -
  • Results are random
  • -
  • 50/50 probability
  • -
  • Shows flipping animation
  • -
  • Works in any channel
  • -
  • Instant results
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\feedback\index.html - -```html - - - - - - Memo Bot - Feedback Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Utility -
-

Feedback Command

-

Submit feedback to help improve Memo Bot

-
- - -
- -
- -
-

Usage

-
- ?feedback [message] -
-

The feedback command allows you to submit suggestions, bug reports, or general feedback about Memo Bot. All feedback is stored and reviewed regularly by the development team.

-
-

Arguments:

-
    -
  • message - Your feedback message
  • -
-
-
- - -
-

Feedback Best Practices

-
-
-

Good Feedback Examples:

-
    -
  • Bug reports with steps to reproduce
  • -
  • Specific feature requests
  • -
  • Detailed improvement suggestions
  • -
  • Command enhancement ideas
  • -
-
-
-

Avoid:

-
    -
  • Abuse or harassment
  • -
  • Spam submissions
  • -
  • Very short/vague feedback
  • -
  • Support requests (use ?help instead)
  • -
-
-
-
- - -
-

Examples

-
-
-

Bug report:

-
?feedback The ?play command sometimes fails to join voice channels in servers with over 1000 members
-
-
-

Feature request:

-
?feedback Would be great to have a queue system for the music commands
-
-
-

General feedback:

-
?feedback Love the profile command! Maybe add an option to show favorite channels?
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Feedback Received

-

Thank you for your feedback! Your message has been stored and will be reviewed by our team.

-
-
-
-
-
-
- Memo Bot -
-
-
-

Error

-

Please provide a feedback message. Usage: ?feedback [message]

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

What Happens Next?

-
    -
  • Feedback is securely stored
  • -
  • Reviewed by development team
  • -
  • Used to guide improvements
  • -
  • May influence future updates
  • -
-
- -
-

Tips for Good Feedback

-
    -
  • Be specific and detailed
  • -
  • One idea per submission
  • -
  • Include examples if possible
  • -
  • Explain why it matters
  • -
  • Be constructive
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\help\index.html - -```html - - - - - - Memo Bot - Help Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Moderation -
-

Help Command

-

Display a list of all available commands.

-
- - -
- -
- -
-

Usage

-
- ?help -
-

The help command shows you a large embed of all the commands that you can run in the guild along with their short description.

-
- - -
-

Examples

-
-
-

Help:

-
?help
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Memo Help Information

-

?tts

-

Join the vc you are in and uses Text-to-Speech to say your text
Usage: ?tts [input_text]

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Additional Notes

-
    -
  • There are some commands that are marked as secret/easter egg commands and are not documented.
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\index.html - -```html - - - - - - Memo Bot - Commands - - - - - - - - - - - - - - - - - -
-
- - Memo Bot - -

Command List

-

Explore all available commands for Memo Bot

-
- -
-
- - -
-
- -
-
-
-
-

?kick

- Moderation -
-

Kick a user from the server with an optional reason.

-
- -
- -
-
-
-

?ban

- Moderation -
-

Ban a user from the server with an optional reason.

-
- -
- -
-
-
-

?unban

- Moderation -
-

Unban a previously banned user.

-
- -
- -
-
-
-

?timeout

- Moderation -
-

Timeout a user for a specified duration.

-
- -
- -
-
-
-

?nick

- Moderation -
-

Change a user's nickname.

-
- -
- -
-
-
-

?play

- Music -
-

Play a song from YouTube in your voice channel.

-
- -
- -
-
-
-

?join

- Music -
-

Make the bot join your voice channel.

-
- -
- -
-
-
-

?leave

- Music -
-

Make the bot leave the voice channel.

-
- -
- -
-
-
-

?help

- Utility -
-

Display a list of all available commands.

-
- -
- -
-
-
-

?profile

- Utility -
-

View detailed information about a user's profile.

-
- -
- -
-
-
-

?feedback

- Utility -
-

Submit feedback about the bot.

-
- -
- -
-
-
-

?translate

- Utility -
-

Translate text to English from any language.

-
- -
- -
-
-
-

?ping

- Utility -
-

Check the bot's current latency.

-
- -
- -
-
-
-

?server

- Utility -
-

View detailed information about the server.

-
- -
- -
-
-
-

?charinfo

- Fun -
-

Get detailed information about a character or emoji.

-
- -
- -
-
-
-

?tts

- Fun -
-

Convert text to speech in a voice channel.

-
- -
- -
-
-
-

?joke

- Fun -
-

Get a random dad joke.

-
- -
- -
-
-
-

?coin

- Fun -
-

Flip a coin and get heads or tails.

-
- -
- -
-
-
-

?quote

- Fun -
-

Get the quote of the day.

-
- -
-
-
- - - - - - - -``` - -# website\commands\join\index.html - -```html - - - - - - Memo Bot - Join Command - - - - - - - - - - - - - - - - -
- -
- -

Join Command

-

Make the bot join your current voice channel

-
- - -
- -
- -
-

Usage

-
- ?join -
-

The join command makes Memo Bot join your current voice channel. You must be in a voice channel for this command to work.

-
-

Arguments:

-
    -
  • None required - Bot joins your current voice channel
  • -
-
-
- - -
-

Example

-
-
-

Basic usage:

-
?join
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Joined Voice Channel

-

Successfully joined General

-
-
-
-
-
-
- Memo Bot -
-
-
-

Error

-

You must be in a voice channel to use this command.

-
-
-
-
-
- - -
-

Voice Channel Requirements

-
-
-

For the command to work:

-
    -
  • You must be in a voice channel
  • -
  • The voice channel must be visible to the bot
  • -
  • The bot must have permission to join the channel
  • -
  • The voice channel must not be at capacity
  • -
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - Connect -
  • -
  • - - - - View Channel -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Common Issues

-
    -
  • User not in a voice channel
  • -
  • Bot missing permissions
  • -
  • Voice channel at capacity
  • -
  • Region voice server issues
  • -
-
- -
-

Good to Know

-
    -
  • Bot stays in channel until disconnected
  • -
  • Will automatically disconnect after inactivity
  • -
  • Can be used before playing music
  • -
  • Works in any accessible voice channel
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\joke\index.html - -```html - - - - - - Memo Bot - Joke Command - - - - - - - - - - - - - - - - -
- -
- -

Joke Command

-

Get a random dad joke to lighten the mood

-
- - -
- -
- -
-

Usage

-
- ?joke -
-

The joke command returns a random dad joke. Each time you use the command, you'll get a different joke!

-
-

Arguments:

-
    -
  • None required - Simply type ?joke
  • -
-
-
- - -
-

Example Jokes

-
-
-

Sample Responses:

-
    -
  • "Why don't eggs tell jokes? They'd Memok up!"
  • -
  • "What do you call a fake noodle? An impasta!"
  • -
  • "Why did the scarecrow win an award? He was outstanding in his field!"
  • -
-
-
-
- - -
-

Example

-
-
-

Get a random joke:

-
?joke
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Dad Joke Time! ๐Ÿ˜„

-

Why don't eggs tell jokes?

-

They'd Memok up!

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Features

-
    -
  • Random selection
  • -
  • Family-friendly content
  • -
  • Classic dad humor
  • -
  • Different joke every time
  • -
-
- -
-

Best Used For

-
    -
  • Breaking the ice
  • -
  • Lightening the mood
  • -
  • Starting conversations
  • -
  • Having a quick laugh
  • -
-
- -
-

Good to Know

-
    -
  • All jokes are dad jokes
  • -
  • Content is always clean
  • -
  • Jokes may repeat eventually
  • -
  • Works in any channel
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\kick\index.html - -```html - - - - - - Memo Bot - Kick Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Moderation -
-

Kick Command

-

Remove a user from your server temporarily

-
- - -
- -
- -
-

Usage

-
- ?kick @user [reason] -
-

The kick command allows moderators to remove a user from the server. The user can rejoin with a new invite.

-
-

Arguments:

-
    -
  • @user - The user to kick (mention required)
  • -
  • reason - Optional reason for the kick
  • -
-
-
- - -
-

Examples

-
-
-

Basic kick:

-
?kick @UserName
-
-
-

Kick with reason:

-
?kick @UserName Spamming in general chat
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

User Kicked

-

@UserName has been kicked.

-

Reason: Spamming in general chat

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - Kick Members -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Additional Notes

-
    -
  • The bot must have a role higher than the kicked user
  • -
  • Kicked users can rejoin with a new invite
  • -
  • The bot will attempt to DM the user the reason for their kick
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\leave\index.html - -```html - - - - - - Memo Bot - Leave Command - - - - - - - - - - - - - - - - -
- -
- -

Leave Command

-

Make the bot leave its current voice channel

-
- - -
- -
- -
-

Usage

-
- ?leave -
-

The leave command makes Memo Bot leave the voice channel it's currently in. The bot must be in a voice channel for this command to work.

-
-

Arguments:

-
    -
  • None required - Bot leaves its current voice channel
  • -
-
-
- - -
-

Example

-
-
-

Basic usage:

-
?leave
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Left Voice Channel

-

Successfully left General

-
-
-
-
-
-
- Memo Bot -
-
-
-

Error

-

I'm not in a voice channel.

-
-
-
-
-
- - -
-

Command Behavior

-
-
-

When using this command:

-
    -
  • Bot will stop any currently playing audio
  • -
  • Bot will immediately disconnect from the voice channel
  • -
  • Command can be used by any member
  • -
  • No need to be in the same voice channel as the bot
  • -
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Common Issues

-
    -
  • Bot not in any voice channel
  • -
  • Bot temporarily unresponsive
  • -
  • Network connectivity issues
  • -
-
- -
-

Good to Know

-
    -
  • Stops any playing audio
  • -
  • Can be used from any channel
  • -
  • Immediate disconnection
  • -
  • No confirmation needed
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\nick\index.html - -```html - - - - - - Memo Bot - Nickname Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Moderation -
-

Nickname Command

-

Change a user's nickname in the server

-
- - -
- -
- -
-

Usage

-
- ?nick @user [new_nickname] -
-

The nickname command allows moderators to change a user's display name in the server. If no new nickname is provided, it will reset to their original username.

-
-

Arguments:

-
    -
  • @user - The user to rename (mention required)
  • -
  • new_nickname - Optional: New nickname for the user (omit to reset)
  • -
-
-
- - -
-

Nickname Rules

-
-
-

Length Requirements:

-
    -
  • Minimum: 1 character
  • -
  • Maximum: 32 characters
  • -
-
-
-

Restrictions:

-
    -
  • Cannot contain Discord's blocked words
  • -
  • Cannot contain server-specific blocked words
  • -
  • Cannot impersonate other users
  • -
  • Cannot use certain special characters
  • -
-
-
-
- - -
-

Examples

-
-
-

Set new nickname:

-
?nick @UserName Cool Person
-
-
-

Reset nickname:

-
?nick @UserName
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Nickname Changed

-

Changed nickname for @UserName to "Cool Person"

-
-
-
-
-
-
- Memo Bot -
-
-
-

Error

-

Cannot change nickname: Missing permissions or nickname invalid.

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - Manage Nicknames -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Additional Notes

-
    -
  • Bot must have a role higher than the user
  • -
  • Cannot change server owner's nickname
  • -
  • Changes are logged in the audit log
  • -
  • Users with "Change Nickname" permission can change their own nickname
  • -
  • Nickname changes are immediate
  • -
-
- -
-

Common Issues

-
    -
  • Nickname too long (>32 characters)
  • -
  • Contains blocked words or characters
  • -
  • Bot role hierarchy insufficient
  • -
  • User has higher roles than bot
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\ping\index.html - -```html - - - - - - Memo Bot - Ping Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Utility -
-

Ping Command

-

Check the bot's current response time

-
- - -
- -
- -
-

Usage

-
- ?ping -
-

The ping command shows the bot's current latency (response time) in milliseconds. This can help you check if the bot is experiencing any delays or connection issues.

-
-

Arguments:

-
    -
  • None required - Simply type ?ping
  • -
-
-
- - -
-

Understanding Latency

-
-
-

Good Latency:

-

50-150ms

-
-
-

Moderate Latency:

-

150-300ms

-
-
-

High Latency:

-

300ms+

-
-
-
- - -
-

Example

-
-
-

Check bot latency:

-
?ping
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

๐Ÿ“ Pong!

-

Bot Latency: 87ms

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

When to Use

-
    -
  • Bot seems unresponsive
  • -
  • Commands are delayed
  • -
  • Checking connection quality
  • -
  • Troubleshooting issues
  • -
-
- -
-

Notes

-
    -
  • Latency varies by region
  • -
  • Results may fluctuate
  • -
  • Lower is better
  • -
  • Simple health check tool
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\play\index.html - -```html - - - - - - Memo Bot - Play Command - - - - - - - - - - - - - - - - -
- -
- -

Play Command

-

Play music from YouTube in your voice channel

-
- - -
- -
- -
-

Usage

-
- ?play youtube_url -
-

The play command allows you to play audio from a YouTube video in your current voice channel.

-
-

Arguments:

-
    -
  • youtube_url - The full URL of a YouTube video
  • -
-
-
- - -
-

URL Requirements

-
-
-

Accepted URL Formats:

-
    -
  • Standard YouTube URLs (youtube.com/watch?v=...)
  • -
  • Short YouTube URLs (youtu.be/...)
  • -
-
-
-

Not Supported:

-
    -
  • YouTube playlists
  • -
  • Non-YouTube URLs
  • -
  • YouTube Shorts
  • -
  • Live streams
  • -
  • Age-restricted videos
  • -
-
-
-
- - -
-

Examples

-
-
-

Play with standard URL:

-
?play https://www.youtube.com/watch?v=dQw4w9WgXcQ
-
-
-

Play with short URL:

-
?play https://youtu.be/dQw4w9WgXcQ
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Now Playing

-

Title: Never Gonna Give You Up

-

Channel: Rick Astley

-

Duration: 3:32

-
-
-
-
-
-
- Memo Bot -
-
-
-

Error

-

Invalid YouTube URL or video unavailable.

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - Connect -
  • -
  • - - - - Speak -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Current Limitations

-
    -
  • No queue system
  • -
  • Cannot pause/resume
  • -
  • No volume control
  • -
  • No skip function
  • -
  • One song at a time
  • -
  • Must wait for current song to finish
  • -
-
- -
-

Requirements

-
    -
  • Must be in a voice channel
  • -
  • Bot must have permission to join
  • -
  • Video must be public
  • -
  • Video must be available in your region
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\profile\index.html - -```html - - - - - - Memo Bot - Profile Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Utility -
-

Profile Command

-

View detailed information about a user's Discord profile

-
- - -
- -
- -
-

Usage

-
- ?profile @user -
-

The profile command displays comprehensive information about a user's Discord profile, including their roles, status, badges, and more.

-
-

Arguments:

-
    -
  • @user - The user to look up (mention required)
  • -
-
-
- - -
-

Displayed Information

-
-
-

Basic Info:

-
    -
  • Display Name
  • -
  • Username
  • -
  • User ID
  • -
  • Account Creation Date
  • -
-
-
-

Status & Roles:

-
    -
  • Current Status (Online/Offline/DND/Idle)
  • -
  • Top Role
  • -
  • All Roles List
  • -
-
-
-

Visual Elements:

-
    -
  • Profile Picture
  • -
  • Banner (if available)
  • -
  • Discord Badges
  • -
-
-
-
- - -
-

Example

-
-
-

Look up user profile:

-
?profile @UserName
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

UserName's Profile

-

Display Name: UserName

-

Username: User#1234

-

User ID: 123456789012345678

-

Creation Time: 17/10/24 12:34:56

-

Status: ๐ŸŸข Online

-

Top Role: @Admin

-

Roles: @Admin, @Moderator, @Member

-
-
-
-
-
-
- Memo Bot -
-
-
-

Invalid Usage

-

Usage: ?profile @user

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Common Issues

-
    -
  • User not mentioned
  • -
  • User not in server
  • -
  • Invalid user mention
  • -
  • User account deleted
  • -
-
- -
-

Status Indicators

-
    -
  • ๐ŸŸข Online
  • -
  • โ›” Do Not Disturb
  • -
  • ๐ŸŸก Idle
  • -
  • โšซ Offline
  • -
-
- -
-

Notes

-
    -
  • Shows all available badges
  • -
  • Banner shown if available
  • -
  • Shows creation timestamp
  • -
  • Lists all user roles
  • -
  • Default avatar used if none set
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\quote\index.html - -```html - - - - - - Memo Bot - Quote Command - - - - - - - - - - - - - - - - -
- -
- -

Quote Command

-

Get an inspiring quote from a famous person

-
- - -
- -
- -
-

Usage

-
- ?quote -
-

The quote command returns a random famous quote. Each use of the command provides a different quote from history's most notable figures.

-
-

Arguments:

-
    -
  • None required - Simply type ?quote
  • -
-
-
- - -
-

Example Quotes

-
-
-

Sample Responses:

-
    -
  • - "Be the change you wish to see in the world." -
    - Mahatma Gandhi
    -
  • -
  • - "I have not failed. I've just found 10,000 ways that won't work." -
    - Thomas A. Edison
    -
  • -
  • - "Two things are infinite: the universe and human stupidity; and I'm not sure about the universe." -
    - Albert Einstein
    -
  • -
-
-
-
- - -
-

Example

-
-
-

Get a random quote:

-
?quote
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Quote of the Moment โœจ

-

"Success is not final, failure is not fatal: it is the courage to continue that counts."

-

- Winston Churchill

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Quote Categories

-
    -
  • Inspirational
  • -
  • Historical
  • -
  • Scientific
  • -
  • Literary
  • -
  • Philosophical
  • -
  • Leadership
  • -
-
- -
-

Best Used For

-
    -
  • Daily inspiration
  • -
  • Starting discussions
  • -
  • Channel messages
  • -
  • Server greetings
  • -
  • Educational content
  • -
-
- -
-

Good to Know

-
    -
  • Quotes from verified sources
  • -
  • Attribution included
  • -
  • Family-friendly content
  • -
  • Works in any channel
  • -
  • Random selection each time
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\server\index.html - -```html - - - - - - Memo Bot - Server Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Utility -
-

Server Command

-

View detailed statistics about the current Discord server

-
- - -
- -
- -
-

Usage

-
- ?server -
-

The server command displays comprehensive statistics and information about the current Discord server, including member counts, boost status, and more.

-
-

Arguments:

-
    -
  • None required - Shows information for current server
  • -
-
-
- - -
-

Displayed Information

-
-
-

Basic Info:

-
    -
  • Server Name
  • -
  • Server ID
  • -
  • Owner
  • -
  • Creation Date
  • -
  • Description (if set)
  • -
-
-
-

Member Stats:

-
    -
  • Total Members
  • -
  • Bot Count
  • -
  • Total Channels
  • -
  • Role Count
  • -
-
-
-

Boost Information:

-
    -
  • Boost Level
  • -
  • Total Boosts
  • -
-
-
-

Visual Elements:

-
    -
  • Server Icon
  • -
  • Server Banner (if set)
  • -
-
-
-
- - -
-

Example

-
-
-

View server stats:

-
?server
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Server Name Server Stats

-

Server ID: 123456789012345678

-

Owner: @ServerOwner

-

Created At: October 17, 2024 12:34:56 PM UTC

-

Boost Level: Level 2

-

Boost Count: 7

-

Members: 1500

-

Bots: 5

-

Total Channels: 20

-

Roles: 15

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Common Issues

-
    -
  • Failed to fetch all members
  • -
  • HTTP connection errors
  • -
  • Permission errors
  • -
-
- -
-

Notes

-
    -
  • Shows server banner if available
  • -
  • Displays server icon if set
  • -
  • Includes timestamp of check
  • -
  • Shows server description if set
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\timeout\index.html - -```html - - - - - - Memo Bot - Timeout Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Moderation -
-

Timeout Command

-

Temporarily restrict a user's ability to interact with the server

-
- - -
- -
- -
-

Usage

-
- ?timeout @user [duration] [unit] [reason] -
-

The timeout command temporarily prevents a user from sending messages, reacting to messages, or joining voice channels.

-
-

Arguments:

-
    -
  • @user - The user to timeout (mention required)
  • -
  • duration - Number value for the timeout duration
  • -
  • unit - Time unit (s = seconds, m = minutes, h = hours, d = days)
  • -
  • reason - Optional: Reason for the timeout
  • -
-
-
- - -
-

Time Units

-
-
-

s (Seconds)

-

Example: ?timeout @user 30 s

-
-
-

m (Minutes)

-

Example: ?timeout @user 5 m

-
-
-

h (Hours)

-

Example: ?timeout @user 2 h

-
-
-

d (Days)

-

Example: ?timeout @user 1 d

-
-
-
- - -
-

Examples

-
-
-

Basic timeout (1 hour):

-
?timeout @UserName 1 h
-
-
-

Timeout with reason:

-
?timeout @UserName 1 d Spamming in general chat
-
-
-

Short timeout (5 minutes):

-
?timeout @UserName 5 m Cool down period
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

User Timed Out

-

@UserName has been timed out for 1 day.

-

Reason: Spamming in general chat

-

Timeout will expire: [timestamp]

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - Timeout Members -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Additional Notes

-
    -
  • Maximum timeout duration is 28 days
  • -
  • Bot must have a role higher than the user
  • -
  • Server owner cannot be timed out
  • -
  • The bot will attempt to DM the user
  • -
  • Timeouts are logged in the audit log
  • -
  • Users keep their roles during timeout
  • -
-
- -
-

Limitations

-
    -
  • Cannot combine time units
  • -
  • Time must be a whole number
  • -
  • Timed out users can still read messages
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\translate\index.html - -```html - - - - - - Memo Bot - Translate Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Utility -
-

Translate Command

-

Translate text from any language to English

-
- - -
- -
- -
-

Usage

-
- ?translate [text] -
-

The translate command automatically detects the language of the input text and translates it to English.

-
-

Arguments:

-
    -
  • text - The text you want to translate to English
  • -
-
-
- - -
-

Language Support

-
-
-

Features:

-
    -
  • Automatic language detection
  • -
  • Translation to English only
  • -
  • Support for most world languages
  • -
  • Handles special characters
  • -
-
-
-
- - -
-

Examples

-
-
-

Spanish to English:

-
?translate Hola, ยฟcรณmo estรกs?
-
-
-

French to English:

-
?translate Bonjour, comment allez-vous?
-
-
-

German to English:

-
?translate Guten Tag, wie geht es dir?
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Translation

-

Original (Spanish): Hola, ยฟcรณmo estรกs?

-

English: Hello, how are you?

-
-
-
-
-
-
- Memo Bot -
-
-
-

Error

-

Please provide text to translate. Usage: ?translate [text]

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - None required -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Common Issues

-
    -
  • No text provided
  • -
  • Text too long
  • -
  • Unsupported characters
  • -
  • Connection errors
  • -
-
- -
-

Limitations

-
    -
  • English output only
  • -
  • Text-only translation
  • -
  • No image translation
  • -
  • No custom language selection
  • -
-
- -
-

Tips

-
    -
  • Keep text concise for best results
  • -
  • Proper punctuation helps accuracy
  • -
  • Use complete sentences when possible
  • -
  • Original text is shown for reference
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\tts\index.html - -```html - - - - - - Memo Bot - Text-to-Speech Command - - - - - - - - - - - - - - - - -
- -
- -

Text-to-Speech Command

-

Convert text to speech and play it in a voice channel

-
- - -
- -
- -
-

Usage

-
- ?tts [message] -
-

The TTS command converts your text message into speech and plays it in your current voice channel.

-
-

Arguments:

-
    -
  • message - The text you want to convert to speech
  • -
-
-
- - -
-

Requirements

-
-
-

Before Using:

-
    -
  • You must be in a voice channel
  • -
  • Bot needs permission to join voice channels
  • -
  • Bot needs permission to speak
  • -
  • Text must not be empty
  • -
-
-
-
- - -
-

Examples

-
-
-

Basic message:

-
?tts Hello everyone!
-
-
-

Longer message:

-
?tts Welcome to our Discord server. Hope you enjoy your stay!
-
-
-
- - -
-

Command Behavior

-
-
    -
  1. Bot joins your voice channel
  2. -
  3. Converts text to speech
  4. -
  5. Plays the audio
  6. -
  7. Automatically disconnects after playing
  8. -
  9. Cleans up temporary audio files
  10. -
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

Ended TTS

-

Successfully generated and played TTS file. Disconnecting from #General

-
-
-
-
-
-
- Memo Bot -
-
-
-

Join voice channel

-

Please join a voice channel to use this command! Usage: ?tts [message]

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - Connect -
  • -
  • - - - - Speak -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Common Issues

-
    -
  • Not in a voice channel
  • -
  • Missing message text
  • -
  • Bot lacks permissions
  • -
  • Channel at capacity
  • -
  • TTS generation fails
  • -
-
- -
-

Good to Know

-
    -
  • Auto-disconnects after playing
  • -
  • Stops current audio if playing
  • -
  • Works in any voice channel
  • -
  • Temporary files are cleaned up
  • -
-
-
-
-
- - - - - - -``` - -# website\commands\unban\index.html - -```html - - - - - - Memo Bot - Unban Command - - - - - - - - - - - - - - - - -
- -
-
- โ† Back to Commands - Moderation -
-

Unban Command

-

Remove a user's ban and allow them to rejoin the server

-
- - -
- -
- -
-

Usage

-
- ?unban user_id [reason] -
-

The unban command allows moderators to remove a ban from a user, allowing them to rejoin the server with a new invite.

-
-

Arguments:

-
    -
  • user_id - The ID of the user to unban
  • -
  • reason - Optional: Reason for the unban
  • -
-
-
- - -
-

Finding a User's ID

-

To unban a user, you'll need their User ID. Here's how to find it:

-
    -
  1. Enable Developer Mode in Discord (User Settings > App Settings > Advanced > Developer Mode)
  2. -
  3. Check the server's ban list (Server Settings > Bans)
  4. -
  5. Right-click on the user and select "Copy ID"
  6. -
-
-

- Note: User IDs are long numbers, like "123456789012345678" -

-
-
- - -
-

Examples

-
-
-

Basic unban:

-
?unban 123456789012345678
-
-
-

Unban with reason:

-
?unban 123456789012345678 Appeal approved
-
-
-
- - -
-

Bot Response

-
-
-
-
- Memo Bot -
-
-
-

User Unbanned

-

Successfully unbanned User#1234 (123456789012345678)

-

Reason: Appeal approved

-
-
-
-
-
-
- Memo Bot -
-
-
-

Error

-

User is not banned or ID is invalid.

-
-
-
-
-
-
- - -
- -
-

Required Permissions

-
    -
  • - - - - Ban Members -
  • -
-
- -
-

Cooldown

-

No cooldown

-
- -
-

Related Commands

- -
- -
-

Additional Notes

-
    -
  • You must use the user's ID, not their username
  • -
  • The action is logged in the server's audit log
  • -
  • Users must be re-invited after being unbanned
  • -
  • The command works even if the user has left the server
  • -
-
-
-
-
- - - - - - -``` - -# website\donate.html - -```html - - - - - - Memo Bot - Support Development - - - - - - - - - - - - - - - - -
- -
-

Support Memo Bot

-

Help keep Memo Bot running and improving

- -
- - -
-

Why Support Us?

-
-
-
๐Ÿš€
-

Keep Memo Bot Running

-

Your support helps cover hosting costs and ensures Memo Bot stays online 24/7.

-
-
-
โญ
-

Enable New Features

-

Donations help us develop new features and improve existing ones.

-
-
-
๐Ÿ’™
-

Support Development

-

Show your appreciation and help motivate continued development.

-
-
-
- - -
-

Donation Tiers

-
- -
-
-

Basic Supporter

-
$5
-
    -
  • - - - - Supporter role in our Discord Server -
  • -
  • - - - - Name in bot credits -
  • -
-
-
- - -
-
MOST POPULAR
-
-

Premium Supporter

-
$10
-
    -
  • - - - - All Basic benefits -
  • -
  • - - - - Early access to features -
  • -
  • - - - - Priority support -
  • -
-
-
- - -
-
-

Elite Supporter

-
$20
-
    -
  • - - - - All Premium benefits -
  • -
  • - - - - Custom role color -
  • -
  • - - - - Feature request priority -
  • -
-
-
-
-
- - -
-

Frequently Asked Questions

-
-
-

How do I claim my rewards?

-

After donating, join our Discord server and open a ticket. Provide your PayPal transaction ID, and we'll set up your rewards within 24 hours.

-
-
-

Is my donation recurring?

-

No, all donations are one-time payments. You can choose to donate again anytime you want to support us further.

-
-
-

How long do rewards last?

-

Rewards are permanent! Once you receive your perks, they'll stay with your account indefinitely.

-
-
-

Can I donate a custom amount?

-

Yes! You can modify the donation amount on PayPal. You'll receive the tier rewards for the nearest tier below your donation amount.

-
-
-
- - -
-

Ready to Support Memo Bot?

-

Your support helps keep Memo Bot free for everyone!

- -
-
- - - - - - -``` - -# website\home.html - -```html - - - - - - Memo - Discord Bot - - - - - - - - - - - - - - - - - -
-
- - Memo Bot - -

Welcome to Memo Bot

-

The ultimate do-it-all Discord bot for moderation and fun!

- -
-
- -
-

Features

-
-
-

Moderation

-

Powerful tools to keep your server safe and clean.

-
-
-

Fun Commands

-

Engage your community with interactive and entertaining commands.

-
-
-

Customization

-

Tailor Memo to fit your server's unique needs.

-
-
-
- -
-
-

- Recent changes to our Privacy Policy. Please review. -

-
-
- - - - - - -``` - -# website\privacy-policy.html - -```html - - - - - - - Memo - Discord Bot | Privacy Policy - - - - - - - - - - - - - - - - - - -
- -
-

1. Information We Collect

- When you use Memo Bot, we may collect certain information such as your Discord user ID, server ID, message content when using bot commands, and other relevant data necessary for the bot's functionality. We also have a Database of all the commands that users ran, and the hashed user ID. This is needed when moderators of a guild want to check what commands a user ran.

- -

2. How We Use Your Information

-

We use the collected information to provide and improve Memo Bot's services, including command execution, server management, and user interaction. We do not sell or share your personal information with third parties.

- -

3. Data Storage and Security

-

We take reasonable measures to protect your data from unauthorized access or disclosure. However, no method of transmission over the internet or electronic storage is 100% secure.

- -

4. Your Rights

-

You have the right to access, correct, or delete your personal information. To exercise these rights, please contact us using the information provided in the "Contact Us" section.

- -

5. Changes to This Privacy Policy

-

We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.

- -

6. Compliance with Discord's Policies

-

Memo Bot complies with Discord's Developer Terms of Service and Developer Policy. We do not collect or use any data beyond what is necessary for the bot's functionality and what is allowed by Discord's policies.

- -

7. Children's Privacy

-

Memo Bot is not intended for use by children under the age of 13. We do not knowingly collect personal information from children under 13. If you are a parent or guardian and you are aware that your child has provided us with personal information, - please contact us.

- -

8. Contact Us

-

If you have any questions about this Privacy Policy, please contact us at Memo@nerd-bear.org.

-
-
- - - - -``` - -# website\support.html - -```html - - - - - - Memo Bot - Support - - - - - - - - - - - - - - - - - -
-
- - Memo Bot - -

Support Center

-

Get help with Memo Bot

-
- -
- -
-

Help Articles

-

Browse our collection of help articles to find answers to common questions:

- - - View all articles โ†’ - -
- - -
-

Contact Support

-

Can't find what you're looking for? Our support team is here to help!

- -
- - -

Response Time: Within 24 hours

-
- -
-

- Tip: For faster support, please include your server ID and a detailed description of your issue in your email. -

-
-
-
-
- - - - - - -``` - -# website\terms-of-use.html - -```html - - - - - - Memo - Discord Bot | Terms of Use - - - - - - - - - - - - - - - - - -
- - -
-

1. Acceptance of Terms

-

By using Memo Bot, you agree to these Terms of Use. If you disagree with any part of these terms, please do not use our bot.

- -

2. Use of the Bot

-

Memo Bot is provided for Discord server management and entertainment purposes. You agree to use it only for its intended purposes and in compliance with Discord's Terms of Service.

- -

3. User Responsibilities

-

You are responsible for all activities that occur under your Discord account while using Memo Bot. Do not use the bot for any illegal or unauthorized purpose.

- -

4. Modifications to Bot or Terms

-

We reserve the right to modify or discontinue Memo Bot at any time. We may also revise these Terms of Use at our discretion. Continued use of the bot after any changes constitutes acceptance of those changes.

- -

5. Limitation of Liability

-

Memo Bot is provided "as is" without warranties of any kind. We are not liable for any damages or losses related to your use of the bot.

- -

6. Privacy

-

Our use and collection of your information is governed by our Privacy Policy. By using Memo Bot, you consent to our data practices as described in that policy.

- -

7. Termination

-

We may terminate or suspend your access to Memo Bot immediately, without prior notice, for conduct that we believe violates these Terms of Use or is harmful to other users of the bot, us, or third parties, or for any other reason.

- -

8. Governing Law

-

These Terms shall be governed by and construed in accordance with the laws of United States of America and the United Kingdom, without regard to its conflict of law provisions.

- -

9. Contact Us

-

If you have any questions about these Terms, please contact us at Memo@nerd-bear.org.

-
-
- - - - -``` - -# website\versions.html - -```html - - - - - - Memo Bot - Version History - - - - - - - - - - - - - - - - - -
- - -
- -
- - - -
-
-

Memo 0.4.4 Beta pre-release ๐Ÿงช

-

Program size: ~0.04 MB (~43.1 KB)

-

Release date: 05/10/2024 2:45 AM BST

- Download v0.4.4 - -

This is a simple beta testing release with around 17 simple commands, the commands are: help, charinfo, tts, profile, play, join, leave, timeout, kick, ban, unban, shutdown, start, stream, play, watch, and listen. Nearly daily updates are to be expected.

- -

Warnings

-
    -
  • This version contains the usage of deprecated classes/functions and similar.
  • -
  • Unstable and not defined behaviour, this version might contain errors that are not handled properly or at all and general bugs.
  • -
  • No proper separation, this program version is currently a relative mess and not well structured, which may cause issues when customizing and or setting the bot up.
  • -
  • The charinfo command still has no support for a large number of special characters
  • -
  • Ban & Unban commands don't handle all exceptions and may cause undefined/buggy behaviour
  • -
  • There is no untimeout command and timeouts can only be in one unit of one amount
  • -
  • Status config commands still exist and have not been removed yet
  • -
  • Join/leave commands still not added to help embed
  • -
  • Many commands missing proper or any error handling
  • -
  • More unknown issues may exist
  • -
- -

Change log:

-
    -
  • Changed the default Logger construct-er log output path to ./logs/output.log instead of ./logs/output.log
  • -
  • Added tts command
  • -
  • Added tts command to the help embed
  • -
  • Added leave command
  • -
  • Added join command
  • -
  • Added run logs to join the command
  • -
  • Added run logs to leave command
  • -
  • Added run logs to TTS command
  • -
  • Changed tts command messages to be embedded
  • -
  • Added more error handling to the tts command
  • -
  • Updated tts command success embed to have a channel link and not a name
  • -
  • Added play command
  • -
  • Patched play command to not leave after starting to play
  • -
  • Added play command to help embed
  • -
  • Added profile command
  • -
  • Added profile command to help embed
  • -
  • Added failsafes and exception handling in the profile command
  • -
- -

Update notes:

-

I will remove all status config commands as they affect the bot across all guilds (Servers) and are just added as a proof of concept. The commands and features I will be adding are config files, untimeout commands, voice chat mute commands, per guild config, music features, and other fun features! Another feature, probably the biggest one (since it will allow for a lot of new features) will be the music queue backend change since it will allow for many new features.

- -

Created by Nerd Bear (This version of Memo is open source and under the apache2 license)

-
- -
-

Memo 0.4.3 Beta pre-release ๐Ÿงช

-

Program size: ~0.03 MB (~30.8 KB)

-

Release date: 04/10/2024 2:17 AM BST

- Download v0.4.3 - -

This is a simple beta testing release with around 12 simple commands, the commands are: help, charinfo, timeout, kick, ban, unban, shutdown, start, stream, play, watch, and listen. Nearly daily updates are to be expected.

- -

Warnings

-
    -
  • This version contains the usage of deprecated classes/functions and similar.
  • -
  • Unstable and not defined behavior, this version might contain errors that are not handled properly or at all and general bugs.
  • -
  • No proper separation, this program version is currently a relative mess and not well structured, which may cause issues when customizing and or setting the bot up.
  • -
  • The charinfo command still has no support for a large number of special characters
  • -
  • Ban & Unban commands don't handle all exceptions and may cause undefined/buggy behavior
  • -
  • There is no untimeout command and timeouts can only be in one unit of one amount
  • -
  • Status config commands still exist and have not been removed yet
  • -
  • More unknown issues may exist
  • -
- -

Change log:

-
    -
  • Added unban command
  • -
  • Added unban command to help embed
  • -
  • Added exception handling to all cases of unban command
  • -
  • Changed Bot Intents from default to all
  • -
  • Changed the Logger class constructer to default to a relative output path
  • -
  • Ran blacklint on source code to increase readability
  • -
  • Specified bot command parameter types for syntax highlighting
  • -
  • Added timeout command
  • -
  • Added timeout command to help embed
  • -
  • Changed the timeout command to send a dm to the user
  • -
- -

Update notes:

-

I will be removing all status config commands as they affect the bot across all guilds (Servers) and are just added as a proof of concept. The commands and features I will be adding are config files, untimeout command, voice chat mute commands, per guild config, music features, and other fun features!

- -

Created by Nerd Bear (This version of Memo is open source and under the apache2 license)

-
- -
-

Memo 0.4.2 Beta pre-release ๐Ÿงช

-

Program size: ~0.02 MB (~18.3 KB)

-

Release date: 03/10/2024 3:39 AM BST

- Download v0.4.2 - -

This is a simple beta testing release with around 10 simple commands, the commands are: help, charinfo, kick, ban, shutdown, start, stream, play, watch and listen. Nearly daily updates are to be expected.

- -

Warnings

-
    -
  • This version contains the usage of deprecated classes/functions and similar.
  • -
  • Unstable and not defined behaviour, this version might contain errors that are not handled properly or at all and general bugs.
  • -
  • No proper separation, this program version is currently a relative mess and not well structured, which may cause issues when customizing and or setting the bot up.
  • -
  • The charinfo command still has no support for a large number of special characters
  • -
  • More unknown issues may exist
  • -
- -

Change log:

-
    -
  • Removed all logger.info logs
  • -
  • Added footer to the DM_EMBED of the word filter
  • -
  • Added the charinfo command to the help embed
  • -
  • Changed the logger initialization to be a relative logger output path
  • -
  • Added logger info level logs to show what user ran what command when
  • -
- -

Created by Nerd Bear (This version of Memo is open source and under the apache2 license)

-
- -
-

Memo 0.4.1 Beta pre-release ๐Ÿงช

-

Program size: ~0.02 MB (~18.3 KB)

-

Release date: 02/10/2024 4:09 AM BST

- Download v0.4.1 - -

This is a simple beta testing release with around 10 simple commands, the commands are: help, charinfo, kick, ban, shutdown, start, stream, play, watch and listen. Nearly daily updates are to be expected.

- -

Warnings

-
    -
  • This version contains the usage of deprecated classes/functions and similar.
  • -
  • Unstable and not defined behaviour, this version might contain errors that are not handled properly or at all and general bugs.
  • -
  • No proper separation, this program version is currently a relative mess and not well structured, which may cause issues when customizing and or setting the bot up.
  • -
  • May include hard-coded paths to files that may not exist or path formats meant for another OS
  • -
  • More unknown issues may exist
  • -
- -

Created by Nerd Bear (This version of Memo is open source and under the apache2 license)

-
-
-
- - - - - - -``` - diff --git a/db_manager/history.py b/db_manager/history.py index 8423c0c..a29eebd 100644 --- a/db_manager/history.py +++ b/db_manager/history.py @@ -1,34 +1,91 @@ import sqlite3 import datetime import json +from typing import List, Union -def add_history(user_id: int, guild_id: int, command: str, arguments: list[str] = ["none"]) -> bool: - """Uses SQLite to add user command to history of commands ran - - ### Params: - `user_id` `int` The user id of the person who ran the command. - `guild_id` `int` The guild id of the server that the user ran the command in. - `command` `str` The name of the command that the user ran. - `arguments` `list[str]` The arguments passed in to the command, defaults to ["none"]. - - ### Return: - Returns a bool (True on success) +def sanitize_input(value: Union[int, str, List[str]]) -> Union[int, str, List[str]]: + """Sanitizes input values before database insertion + + Args: + value: The value to sanitize + + Returns: + Sanitized value """ - args_json = json.dumps(arguments) - datetime_value = datetime.datetime.now().strftime("%S:%M:%H %d/%m/%y") + if isinstance(value, int): + return abs(int(value)) + elif isinstance(value, str): + return sqlite3.escape_string(str(value)) + elif isinstance(value, list): + return [sqlite3.escape_string(str(x)) for x in value] + else: + raise ValueError("Invalid input type") + +def add_history(user_id: int, guild_id: int, command: str, arguments: List[str] = ["none"]) -> bool: + """Uses SQLite to add user command to history of commands ran with enhanced security - db_connection = sqlite3.connect("./memo.db") - db_cursor = db_connection.cursor() + Args: + user_id: The user id of the person who ran the command + guild_id: The guild id of the server that the user ran the command in + command: The name of the command that the user ran + arguments: The arguments passed to the command, defaults to ["none"] + Returns: + bool: True on success, False on failure + + Raises: + ValueError: If input validation fails + """ try: - db_cursor.execute( - "INSERT INTO history (user_id, guild_id, command, arguments, datetime) VALUES (?, ?, ?, ?, ?)", - (user_id, guild_id, command, args_json, datetime_value) - ) - db_connection.commit() - return True + clean_user_id = sanitize_input(user_id) + clean_guild_id = sanitize_input(guild_id) + clean_command = sanitize_input(command) + clean_arguments = sanitize_input(arguments) + + if not isinstance(clean_user_id, int) or not isinstance(clean_guild_id, int): + raise ValueError("Invalid ID format") + + if not clean_command: + raise ValueError("Command cannot be empty") + + args_json = json.dumps(clean_arguments, ensure_ascii=True) + + datetime_value = datetime.datetime.now().strftime("%S:%M:%H %d/%m/%y") + + with sqlite3.connect("./memo.db", isolation_level='EXCLUSIVE') as db_connection: + db_connection.set_trace_callback(print) + db_connection.execute("PRAGMA foreign_keys = ON") + db_connection.execute("PRAGMA journal_mode = WAL") + + db_cursor = db_connection.cursor() + + query = """ + INSERT INTO history + (user_id, guild_id, command, arguments, datetime) + VALUES + (?, ?, ?, ?, ?) + """ + + db_cursor.execute(query, ( + clean_user_id, + clean_guild_id, + clean_command, + args_json, + datetime_value + )) + + db_connection.commit() + return True + + except ValueError as e: + print(f"ERROR_LOG: Validation error: {e}") + return False + except json.JSONDecodeError as e: + print(f"ERROR_LOG: JSON encoding error: {e}") + return False except sqlite3.Error as e: - print(f"ERROR_LOG: SQLite error: {e}") + print(f"ERROR_LOG: Database error: {e}") return False - finally: - db_connection.close() \ No newline at end of file + except Exception as e: + print(f"ERROR_LOG: Unexpected error: {e}") + return False \ No newline at end of file diff --git a/memo.db b/memo.db index 1d7779f..eba1ecd 100644 Binary files a/memo.db and b/memo.db differ diff --git a/temp/audio/063722105352.mp3 b/temp/audio/063722105352.mp3 deleted file mode 100644 index e7806fe..0000000 Binary files a/temp/audio/063722105352.mp3 and /dev/null differ diff --git a/temp/audio/743837710520.mp3 b/temp/audio/743837710520.mp3 deleted file mode 100644 index a9a600a..0000000 Binary files a/temp/audio/743837710520.mp3 and /dev/null differ diff --git a/website/commands/deafen/index.html b/website/commands/deafen/index.html new file mode 100644 index 0000000..da9bba1 --- /dev/null +++ b/website/commands/deafen/index.html @@ -0,0 +1,216 @@ + + + + + + Memo Bot - Deafen Command + + + + + + + + + + + + + + + + +
+ +
+
+ โ† Back to Commands + Moderation +
+

Deafen Command

+

Prevent a user from hearing audio in voice channels

+
+ + +
+ +
+ +
+

Usage

+
+ ?deafen @user [reason] +
+

The deafen command prevents a user from hearing any audio in voice channels. The user will remain in the voice channel but cannot receive audio.

+
+

Arguments:

+
    +
  • @user - The user to deafen (mention required)
  • +
  • reason - Required: Reason for the deafen
  • +
+
+
+ + +
+

Requirements

+
+
+

Before Using:

+
    +
  • User must be in a voice channel
  • +
  • Bot must have permission to deafen members
  • +
  • Your role must be higher than the target user's role
  • +
  • User must not already be deafened
  • +
  • Reason must be provided
  • +
+
+
+
+ + +
+

Examples

+
+
+

Basic deafen with reason:

+
?deafen @UserName Disruptive behavior in voice chat
+
+
+
+ + +
+

Bot Response

+
+
+
+
+ Memo Bot +
+
+
+

Voice Deafen

+

Deafened @UserName

+

Reason: Disruptive behavior in voice chat

+
+
+
+
+
+
+ Memo Bot +
+
+
+

Error

+

Please mention a valid member and provide a reason. Usage: ?deafen @user [reason]

+
+
+
+
+
+
+ + +
+ +
+

Required Permissions

+
    +
  • + + + + Deafen Members +
  • +
+
+ +
+

Cooldown

+

No cooldown

+
+ +
+

Related Commands

+ +
+ +
+

Additional Notes

+
    +
  • Deafen persists until manually removed
  • +
  • The bot attempts to DM the user when deafened
  • +
  • Action is logged in audit log
  • +
  • Cannot deafen users with higher roles
  • +
  • Requires explicit reason
  • +
+
+ +
+

Common Issues

+
    +
  • User not in voice channel
  • +
  • User already deafened
  • +
  • Missing permissions
  • +
  • Role hierarchy conflicts
  • +
  • No reason provided
  • +
  • Invalid user mention
  • +
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/website/commands/index.html b/website/commands/index.html index 0e5c8de..01bf4fe 100644 --- a/website/commands/index.html +++ b/website/commands/index.html @@ -122,6 +122,58 @@

?timeout

+
+
+
+

?mute

+ Moderation +
+

Server mutes a member with the provided reason.

+
+ +
+ +
+
+
+

?unmute

+ Moderation +
+

Un server mutes a member with the provided reason.

+
+ +
+ +
+
+
+

?deafen

+ Moderation +
+

Server deafens a member with the provided reason.

+
+ +
+ +
+
+
+

?undeafen

+ Moderation +
+

Un server deafens a member with the provided reason.

+
+ +
+
diff --git a/website/commands/mute/index.html b/website/commands/mute/index.html new file mode 100644 index 0000000..62c0747 --- /dev/null +++ b/website/commands/mute/index.html @@ -0,0 +1,214 @@ + + + + + + Memo Bot - Mute Command + + + + + + + + + + + + + + + + +
+ +
+
+ โ† Back to Commands + Moderation +
+

Mute Command

+

Server mute a user in voice channels

+
+ + +
+ +
+ +
+

Usage

+
+ ?mute @user [reason] +
+

The mute command prevents a user from speaking in voice channels. The user will remain in the voice channel but cannot transmit audio.

+
+

Arguments:

+
    +
  • @user - The user to mute (mention required)
  • +
  • reason - Required: Reason for the mute
  • +
+
+
+ + +
+

Requirements

+
+
+

Before Using:

+
    +
  • User must be in a voice channel
  • +
  • Bot must have permission to mute members
  • +
  • Your role must be higher than the target user's role
  • +
  • User must not already be muted
  • +
+
+
+
+ + +
+

Examples

+
+
+

Basic mute with reason:

+
?mute @UserName Disruptive behavior in voice chat
+
+
+
+ + +
+

Bot Response

+
+
+
+
+ Memo Bot +
+
+
+

Voice Mute

+

Muted @UserName

+

Reason: Disruptive behavior in voice chat

+
+
+
+
+
+
+ Memo Bot +
+
+
+

Error

+

Please mention a valid member and provide a reason. Usage: ?mute @user [reason]

+
+
+
+
+
+
+ + +
+ +
+

Required Permissions

+
    +
  • + + + + Mute Members +
  • +
+
+ +
+

Cooldown

+

No cooldown

+
+ +
+

Related Commands

+ +
+ +
+

Additional Notes

+
    +
  • Mute is specific to voice channels
  • +
  • The bot attempts to DM the user when muted
  • +
  • Mute persists until manually unmuted
  • +
  • Action is logged in audit log
  • +
  • Cannot mute users with higher roles
  • +
+
+ +
+

Common Issues

+
    +
  • User not in voice channel
  • +
  • Missing permissions
  • +
  • User already muted
  • +
  • Role hierarchy conflicts
  • +
  • No reason provided
  • +
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/website/commands/undeafen/index.html b/website/commands/undeafen/index.html new file mode 100644 index 0000000..068fbca --- /dev/null +++ b/website/commands/undeafen/index.html @@ -0,0 +1,214 @@ + + + + + + Memo Bot - Undeafen Command + + + + + + + + + + + + + + + + +
+ +
+
+ โ† Back to Commands + Moderation +
+

Undeafen Command

+

Remove a server-wide deafen from a user

+
+ + +
+ +
+ +
+

Usage

+
+ ?undeafen @user [reason] +
+

The undeafen command restores a user's ability to hear in voice channels after being deafened. The user must be in a voice channel for the command to work.

+
+

Arguments:

+
    +
  • @user - The user to undeafen (mention required)
  • +
  • reason - Required: Reason for the undeafen
  • +
+
+
+ + +
+

Requirements

+
+
+

Before Using:

+
    +
  • User must be in a voice channel
  • +
  • User must currently be deafened
  • +
  • Bot must have permission to deafen members
  • +
  • Your role must be higher than the target user's role
  • +
+
+
+
+ + +
+

Examples

+
+
+

Basic undeafen with reason:

+
?undeafen @UserName Deafen duration completed
+
+
+
+ + +
+

Bot Response

+
+
+
+
+ Memo Bot +
+
+
+

Voice Undeafen

+

Undeafened @UserName

+

Reason: Deafen duration completed

+
+
+
+
+
+
+ Memo Bot +
+
+
+

Error

+

Member not in voice channel. Usage: ?undeafen @user [reason]

+
+
+
+
+
+
+ + +
+ +
+

Required Permissions

+
    +
  • + + + + Deafen Members +
  • +
+
+ +
+

Cooldown

+

No cooldown

+
+ +
+

Related Commands

+ +
+ +
+

Additional Notes

+
    +
  • User must be currently deafened
  • +
  • The bot attempts to DM the user when undeafened
  • +
  • Action is logged in audit log
  • +
  • Cannot undeafen users with higher roles
  • +
  • User must be in a voice channel
  • +
+
+ +
+

Common Issues

+
    +
  • User not in voice channel
  • +
  • User not currently deafened
  • +
  • Missing permissions
  • +
  • Role hierarchy conflicts
  • +
  • No reason provided
  • +
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/BotUpdate/cogs/__init__.py b/website/commands/unmute/index.html similarity index 100% rename from BotUpdate/cogs/__init__.py rename to website/commands/unmute/index.html