Extension Portal

Table of Contents

Introduction to Extensions

The ZygnalBot Extensions System allows you to create and load custom cogs (modules) without restarting your bot. This powerful feature enables you to:

Note: Extensions are loaded from the "Extensions" folder in your bot's root directory. Each extension is a Python file containing a cog class and a setup function.

Setting Up the Extensions Folder

1

Create the Extensions Directory

First, create a folder named "Extensions" in your bot's root directory. This is where all your custom cogs will be stored.

ZygnalBot/
├── data/
├── cogs/
├── Extensions/  <-- Create this folder
├── Main_bot.py
└── other files...

The bot will automatically create this folder if it doesn't exist when you try to load an extension.

Creating Your First Extension

1

Basic Extension Structure

Each extension is a Python file with a specific structure. Here's a template for a basic extension:

import discord
        from discord.ext import commands
        import asyncio  # Required for handling async add_cog
        
        class MyCustomCog(commands.Cog):
            def __init__(self, bot):
                self.bot = bot
                
            @commands.command(name="hello")
            async def hello_command(self, ctx):
                """A simple hello command"""
                await ctx.send("Hello from my custom extension!")
                
        def setup(bot):
            # Create the cog instance
            cog = MyCustomCog(bot)
            
            # Get the event loop and schedule the async add_cog
            loop = asyncio.get_event_loop()
            loop.create_task(bot.add_cog(cog))
            
            # Return the cog for potential use by the loader
            return cog
Important: Every extension MUST have a setup(bot) function that adds your cog to the bot. In ZygnalBot, the add_cog method is asynchronous, but the extension system expects a synchronous setup function. The pattern above handles this by scheduling the async task in the event loop.
2

Save Your Extension

Save your extension file in the Extensions folder with a descriptive name and the .py extension.

For example: Extensions/greetings.py

Loading Extensions with Commands

!load Command

!load filename

Loads an extension from the Extensions folder. You can specify the filename with or without the .py extension.

Example: !load greetings or !load greetings.py

1

Loading an Extension

To load your extension, use the !load command followed by the filename (without the path):

!load greetings

The bot will search for greetings.py in the Extensions folder and attempt to load it.

If successful, you'll see a confirmation message with details about the loaded cog and any new commands that are now available.

Note: Only the bot owner can use the extension management commands for security reasons.

Managing Your Extensions

1

Available Management Commands

ZygnalBot provides several commands to manage your extensions:

!load

!load filename
Loads a new extension from the Extensions folder

!unload

!unload cogname
Unloads a currently active cog

!reload

!reload cogname
Reloads a cog to apply changes without restarting the bot

!listcogs

!listcogs
Lists all currently loaded cogs

!coghelp

!coghelp
Shows detailed help for cog management commands
2

Updating Extensions

When you make changes to an extension file, you can apply those changes without restarting the bot:

  1. Edit your extension file in the Extensions folder
  2. Save the changes
  3. Use !reload CogName to reload the cog with your changes

For example, if you updated your MyCustomCog class in greetings.py:

!reload MyCustomCog
Tip: The reload command uses the cog's class name, not the filename. You can find the correct name by using !listcogs first.

Example Extensions

1

Simple Utility Extension

Here's an example of a utility extension with multiple commands:

import discord
        from discord.ext import commands
        import random
        import datetime
        import asyncio  # Required for handling async add_cog
        
        class UtilityCommands(commands.Cog):
            def __init__(self, bot):
                self.bot = bot
                
            @commands.command(name="roll")
            async def roll_dice(self, ctx, dice: str = "1d6"):
                """Roll dice in NdN format"""
                try:
                    rolls, limit = map(int, dice.split('d'))
                    
                    if rolls > 25:
                        await ctx.send("I can't roll more than 25 dice at once!")
                        return
                        
                    results = [random.randint(1, limit) for _ in range(rolls)]
                    
                    embed = discord.Embed(
                        title="🎲 Dice Roll",
                        description=f"Rolling {dice}...",
                        color=discord.Color.blue()
                    )
                    
                    embed.add_field(name="Results", value=str(results))
                    embed.add_field(name="Sum", value=str(sum(results)))
                    
                    await ctx.send(embed=embed)
                    
                except Exception as e:
                    await ctx.send(f"Error: {str(e)}\nFormat should be NdN (e.g., 3d6)")
            
            @commands.command(name="time")
            async def current_time(self, ctx):
                """Shows the current time in different formats"""
                now = datetime.datetime.now()
                
                embed = discord.Embed(
                    title="⏰ Current Time",
                    color=discord.Color.gold()
                )
                
                embed.add_field(
                    name="Standard Format", 
                    value=now.strftime("%Y-%m-%d %H:%M:%S"),
                    inline=False
                )
                
                embed.add_field(
                    name="12-Hour Format", 
                    value=now.strftime("%I:%M:%S %p"),
                    inline=True
                )
                
                embed.add_field(
                    name="Date Only", 
                    value=now.strftime("%B %d, %Y"),
                    inline=True
                )
                
                await ctx.send(embed=embed)
        
        def setup(bot):
            # Create the cog instance
            cog = UtilityCommands(bot)
            
            # Get the event loop and schedule the async add_cog
            loop = asyncio.get_event_loop()
            loop.create_task(bot.add_cog(cog))
            
            # Return the cog for potential use by the loader
            return cog

Save this as Extensions/utility.py and load it with !load utility.

2

Fun Commands Extension

Here's an example of a fun commands extension:

import discord
        from discord.ext import commands
        import random
        import asyncio
        
        class FunCommands(commands.Cog):
            def __init__(self, bot):
                self.bot = bot
                self.magic_responses = [
                    "It is certain.",
                    "It is decidedly so.",
                    "Without a doubt.",
                    "Yes, definitely.",
                    "You may rely on it.",
                    "As I see it, yes.",
                    "Most likely.",
                    "Outlook good.",
                    "Yes.",
                    "Signs point to yes.",
                    "Reply hazy, try again.",
                    "Ask again later.",
                    "Better not tell you now.",
                    "Cannot predict now.",
                    "Concentrate and ask again.",
                    "Don't count on it.",
                    "My reply is no.",
                    "My sources say no.",
                    "Outlook not so good.",
                    "Very doubtful."
                ]
                
            @commands.command(name="8ball")
            async def magic_8ball(self, ctx, *, question: str):
                """Ask the magic 8-ball a question"""
                if not question.endswith('?'):
                    question += '?'
                    
                embed = discord.Embed(
                    title="🔮 Magic 8-Ball",
                    description=f"**Question:** {question}",
                    color=discord.Color.purple()
                )
                
                # Add suspense with typing indicator
                async with ctx.typing():
                    await asyncio.sleep(1.5)
                    
                embed.add_field(
                    name="Answer", 
                    value=random.choice(self.magic_responses),
                    inline=False
                )
                
                await ctx.send(embed=embed)
            
            @commands.command(name="choose")
            async def choose(self, ctx, *, options: str):
                """Choose between multiple options (separated by commas)"""
                option_list = [opt.strip() for opt in options.split(',')]
                
                if len(option_list) < 2:
                    await ctx.send("Please provide at least two options separated by commas!")
                    return
                    
                embed = discord.Embed(
                    title="🤔 Making a Choice",
                    description=f"Choosing from {len(option_list)} options...",
                    color=discord.Color.green()
                )
                
                # Add suspense with typing indicator
                async with ctx.typing():
                    await asyncio.sleep(1)
                    
                chosen = random.choice(option_list)
                embed.add_field(name="I choose", value=f"**{chosen}**", inline=False)
                
                await ctx.send(embed=embed)
        
        def setup(bot):
            # Create the cog instance
            cog = FunCommands(bot)
            
            # Get the event loop and schedule the async add_cog
            loop = asyncio.get_event_loop()
            loop.create_task(bot.add_cog(cog))
            
            # Return the cog for potential use by the loader
            return cog

Save this as Extensions/fun.py and load it with !load fun.

Troubleshooting

1

Common Issues and Solutions

Missing setup() Function

If you get an error about a missing setup function, make sure your extension includes:

def setup(bot):
            # Create the cog instance
            cog = YourCogName(bot)
            
            # Get the event loop and schedule the async add_cog
            loop = asyncio.get_event_loop()
            loop.create_task(bot.add_cog(cog))
            
            # Return the cog for potential use by the loader
            return cog

Coroutine Never Awaited Warning

If you see a warning like RuntimeWarning: coroutine 'BotBase.add_cog' was never awaited, it means you're not properly handling the asynchronous nature of the add_cog method. Make sure you're using the pattern shown above in your setup function.

Import Errors

If you're getting import errors, check that:

  • All required modules are installed
  • You're using the correct import paths
  • Your code doesn't have syntax errors
  • You've imported the asyncio module for handling async tasks

Extension Not Found

If the bot can't find your extension:

  • Make sure the file is in the Extensions folder
  • Check that the filename matches what you're trying to load
  • Verify the file has a .py extension
Tip: The error messages from the !load command are designed to be helpful. They'll often tell you exactly what's wrong and how to fix it.
2

Debugging Extensions

If your extension loads but doesn't work correctly:

  1. Check the bot's console for error messages
  2. Add print statements to your code for debugging
  3. Use try/except blocks to catch and display errors
  4. Make sure your commands have the correct permissions

Example of adding error handling to a command:

@commands.command(name="debug_example")
        async def debug_example(self, ctx, *, text: str = None):
            """Example command with error handling"""
            try:
                if text is None:
                    await ctx.send("You need to provide some text!")
                    return
                    
                # Your command logic here
                result = text.upper()  # Just an example operation
                await ctx.send(f"Result: {result}")
                
            except Exception as e:
                # Log the error to console
                print(f"Error in debug_example command: {e}")
                
                # Send error message to Discord
                await ctx.send(f"An error occurred: {type(e).__name__}: {str(e)}")
                
                # You could also send more detailed error info to a specific channel
                error_channel = self.bot.get_channel(YOUR_ERROR_CHANNEL_ID)
                if error_channel:
                    await error_channel.send(f"Error in {ctx.command} used by {ctx.author}:\n```py\n{type(e).__name__}: {str(e)}\n```")
3

Testing Your Extensions

Here's a simple test extension you can use to verify that your extension system is working correctly:

import discord
        from discord.ext import commands
        import asyncio
        
        class TestExtension(commands.Cog):
            def __init__(self, bot):
                self.bot = bot
                print("TestExtension loaded successfully!")
                
            @commands.Cog.listener()
            async def on_ready(self):
                print("TestExtension is ready!")
                
            @commands.command(name="testinfo")
            async def test_info(self, ctx):
                """Shows information about this test extension"""
                embed = discord.Embed(
                    title="✅ Test Extension Information",
                    description="This is a simple test extension for ZygnalBot.",
                    color=discord.Color.green()
                )
                
                embed.add_field(
                    name="Purpose",
                    value="This extension demonstrates how to create a custom cog that can be loaded without restarting the bot.",
                    inline=False
                )
                
                embed.add_field(
                    name="Commands",
                    value="• `!testinfo` - Shows this information message",
                    inline=False
                )
                
                embed.add_field(
                    name="Extension Management",
                    value=(
                        "• `!listcogs` - Verify this extension is loaded\n"
                        "• `!reload TestExtension` - Test reloading this extension\n"
                        "• `!unload TestExtension` - Test unloading this extension"
                    ),
                    inline=False
                )
                
                embed.set_footer(text="ZygnalBot Made By TheHolyoneZ")
                await ctx.send(embed=embed)
        
        def setup(bot):
            # Create a new cog instance
            cog = TestExtension(bot)
            
            # Get the event loop
            loop = asyncio.get_event_loop()
            
            # Schedule the add_cog coroutine to run
            loop.create_task(bot.add_cog(cog))
            
            # Return the cog for potential use by the loader
            return cog

Save this as Extensions/Test_Extension.py and load it with !load Test_Extension.

Tip: This test extension includes console print statements that will appear in your bot's console when the extension is loaded and when the bot is ready, which can help with debugging.

Best Practices

Conclusion

The ZygnalBot Extensions System provides a powerful way to extend your bot's functionality without restarting. By creating modular, well-designed extensions, you can:

Remember that extensions are loaded from the Extensions folder and must include a proper setup function. Use the management commands to load, unload, and reload your extensions as needed.

Need help? If you encounter any issues or have questions about creating extensions, join our support server or check the documentation for more examples and guidance.
Support Me