How do I use cogs with discord.py?
Asked Answered
U

3

10

I have quite a large bot for Discord written up. It has over 1000 lines of code. When I researched how to do it on Youtube and here, nothing seems to be working. I was wondering if someone could explain how to use a cog properly, possibly with photo examples. I can show what code I have to help you understand what I want.

An example would be that I want to have all of my mod commands in a separate file, just so its cleaner and more organized. so, how do I go about doing this? Here is an example of my code:

Mod Commands I want to move to a separate file using a cog

#kick Command
@bot.command(pass_context=True)
@commands.has_role("BotUser")
async def kick(ctx, user: discord.Member):
  await bot.say(":boot:{} has been kicked from the server... Too bad...".format(user.name))
  await bot.kick(user)
  await bot.delete_message(ctx.message)

#Ban Command
@bot.command(pass_context=True)
@commands.has_role("BotUser")
async def ban(ctx, user: discord.Member):
  await bot.say(":rofl: {} has been banned until further notice... Sucks to Suck")
  await bot.ban(user)
  await bot.delete_message(ctx.message)

#Clear command (Can only clear messages over 14 days old!)
@bot.command(pass_context=True)
@commands.has_role("BotUser")
async def clear(ctx, amount=100):
  channel = ctx.message.channel
  messages = []
  async for message in bot.logs_from(channel, limit=int(amount) + 1):
    messages.append(message)
  await bot.delete_messages(messages)
  await bot.delete_message(ctx.message)

Imports currently that I have

import discord
from discord.ext import commands
from discord.ext.commands import Bot
import asyncio
import json
import os

Prefix and directory

bot = commands.Bot(command_prefix='.')
bot.remove_command('help')
players = {}
queues = {}
os.chdir(r"C:\Users\anakr\Documents\DiscordBots\Bot Test")

Calling token ID - token id is above, not shown:

bot.run(TOKEN)

I am unsure how to start a cog completely, what else to import, how to call the file. I know Java well, but I am trying to work on my python skills with Discord.

Uncalledfor answered 28/11, 2018 at 21:12 Comment(1)
Please include any code as a code block, not as images.Hathcock
G
27

Note:

The below was written for the older 0.16 version, which did not have good documentation of cogs. The new 1.0 version has good documentation, and has completely changed the structure of cogs. If you're using a modern version of , you should consult the official documentation.

Introduction

Every cog has two parts: a class and a setup function. Almost all setup functions look the same:

def setup(bot):
    bot.add_cog(Cog(bot))

where Cog is the cog class.

The cog class contains all of our commands and events as methods.

Main Changes

There are four main transformations that you need to do to change your bot to a cog:

  1. Replace bot.command with commands.command (commands being from discord.ext import commands)

  2. Change the signatures of your functions to include self at the beginning, as all of your commands and events are now methods of the cog class

  3. Change all references to bot to refer to self.bot instead

  4. Remove all bot.event decorators. Event listeners from your cog are registered on name alone

There are also some gotchas:

  1. Remove await bot.process_commands(message) from any on_message events in your cog. For any message this should only be awaited once. The default on_message does already does this for you.

  2. Registering an event through a cog does not remove other callbacks related to that event, from your main file or other cogs. That means that your bot could respond to a on_member_join event multiple times for example, if you have behaviour for that event defined in multiple places.

Example

Let's say you have the following discord.py bot, bot.py in the directory src:

from discord.ext import commands

bot = commands.Bot(command_prefix='!')

@bot.command(pass_context=True)
@commands.has_role("Mod")
async def acommand(ctx, argument):
   await bot.say("Stuff")

@bot.event
async def on_message(message):
    print(message.content)
    await bot.process_commands(message)

bot.run("token")

You then factor that functionality out into a cog src/cogs/maincog.py

from discord.ext import commands

class MainCog:
    def __init__(self, bot):
        self.bot = bot

    @commands.command(pass_context=True)
    @commands.has_role("Mod")
    async def acommand(self, ctx, argument):
       await self.bot.say("Stuff")        

    async def on_message(self, message):
        print(message.content)

def setup(bot):
    bot.add_cog(MainCog(bot))

And your bot.py file would look like

from discord.ext import commands

bot = commands.Bot(command_prefix='!')

bot.load_extension("cogs.maincog")

bot.run("token")

Note that to load the extension at cogs/maincog.py, we use load_extension("cogs.maincog").

Other features

Cogs also allow you to define some special methods. Most of these are available only in and are documented here.

  1. __global_check, formerly __check, runs before every command and must return True for that command to proceed.

  2. __local_check runs only before commands from this cog.

  3. __global_check_once I believe that this is similar to __global_check except that it only checks once in the case of subcommands. I haven't used this much.

  4. __unload You can live refresh your bot by unloading the extension, then reloading it, allowing you to update your cogs without taking your bot offline. This method is called when you unload the extension, or when your bot stop running, in case you need to do cleanup.

  5. __before_invoke and __after_invoke are run before and after every command from this cog, respectively.

  6. __error is an error handler for commands from this cog.

Guam answered 28/11, 2018 at 21:37 Comment(1)
Note that this answer is significantly outdated. Check the docs to get current infos.Sharecropper
P
3

A basic cog template looks like this:

import discord
import asyncio
from discord.ext import commands

class cog_name(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

def setup(bot):
    bot.add_cog(cog_name(bot))

and to add this cog to your bot, in your main file, add the following:

client.load_extension('script_name')

and if the cog script is inside a folder, add

client.load_extension('folder_name.script_name')
Pathogen answered 29/11, 2020 at 3:40 Comment(3)
what if I'm using the client as the @client decorator thing.Wary
how do I fit the client in thatWary
because a cog is a instance of the class commands.Cog and the client is not in the same script, you will have to use @commands.command()Pathogen
P
-1

If it's a moderation cog, you can follow this code that is provided: the main file:

import discord
from discord.ext import commands
import moderation
bot = commands.Bot(command_prefix=your_prefix) 
async def load():
  cogs = [moderation]
  for i in range(len(cogs)):
    cogs[i].setup(bot)
@bot.event
async def on_ready():
  print("Bot is ready!")
  await load()
  print("Cogs synced!")

bot.run(your token)

The moderation file:

from discord.ext import commands
import discord
from discord import app_commands

class moderation(commands.Cog):
  def __init__(self,bot):
    self.bot:commands.Bot = bot
  @app_commands.command()
  @app_commands.default_permissions(ban_members=True)
  async def ban(self,interaction:discord.Interaction,member:discord.Member,*,reason:str):
    await member.ban()
    await interaction.response.send_message(f"{member} was banned!")
  @app_commands.command()
  @app_commands.default_permissions(ban_members=True)
  async def kick(self,interaction:discord.Interaction,member:discord.Member,*,reason:str):
    await member.kick()
    await interaction.response.send_message(f"{member} was kicked!")
  @app_commands.command()
  async def clear(self,interaction:discord.Interaction, amount:int):
    await interaction.channel.purge(limit=amount) 
async def setup(bot):
  await bot.add_cog(moderation(bot))
Paranymph answered 28/7, 2023 at 9:30 Comment(1)
This is not a good example. Do not manually call setup after importing everything, just use load_extension. There's no reason to deviate from the examples in the docs. This is an ugly solution.Invisible

© 2022 - 2024 — McMap. All rights reserved.