Discord等级系统报错:TypeError: on_message()缺少必填参数'ctx'
TypeError: on_message() missing 1 required positional argument: 'ctx' in Your Discord.py Level System Let's break down what's causing this error and fix your level system step by step:
1. The Core Issue: Incorrect Event Listener Setup
Your on_message method in the Levels cog is missing the required decorator and has an incorrect parameter signature. In Discord.py cogs, event listeners need to be marked with @commands.Cog.listener(), and the on_message event only takes self and message as parameters (no ctx here). Discord.py was throwing the error because it couldn't match the expected argument structure.
2. Broken Command Decorator
When defining commands inside a cog, you can't use @client.command() — you need to use @commands.command() instead. Cogs rely on the commands module's decorators to properly bind to the bot instance, so using the client-level decorator breaks the cog's functionality.
3. Other Minor Bugs to Fix
- In your
levelcommand, you're checking ifmember.idexists inself.users, but your JSON stores IDs as strings. You need to convertmember.idto a string for the check to work. - Your embed has two fields both named "Level" — change the second one to "EXP" or "Experience" to avoid confusion.
- Added a safety check for missing
users.jsonfiles to prevent startup crashes. - Reduced the save interval from 5 seconds to 5 minutes to avoid excessive disk writes (a more reasonable frequency for data persistence).
Corrected Full Code
import discord import asyncio import json from discord.ext import commands class Levels(commands.Cog): # Inherit from commands.Cog for proper cog functionality def __init__(self, client): self.bot = client self.bot.loop.create_task(self.save_users()) # Handle missing JSON file gracefully try: with open(r'C:\Users\conex_000\PycharmProjects\Shirochka\users.json', 'r') as f: self.users = json.load(f) except FileNotFoundError: self.users = {} async def save_users(self): await self.bot.wait_until_ready() while not self.bot.is_closed(): with open(r'C:\Users\conex_000\PycharmProjects\Shirochka\users.json', 'w') as f: json.dump(self.users, f, indent=4) await asyncio.sleep(300) # Save every 5 minutes instead of 5 seconds def lvl_up(self, author_id): cur_xp = self.users[author_id]['exp'] cur_lvl = self.users[author_id]['level'] required_xp = round((4 * (cur_lvl ** 3)) / 5) if cur_xp >= required_xp: self.users[author_id]['level'] += 1 # Reset XP to the amount over the required threshold (optional but cleaner) self.users[author_id]['exp'] = cur_xp - required_xp return True else: return False @commands.Cog.listener() # Mandatory decorator for cog event listeners async def on_message(self, message): if message.author == self.bot.user: return author_id = str(message.author.id) if author_id not in self.users: self.users[author_id] = { 'level': 1, 'exp': 0 } self.users[author_id]['exp'] += 1 if self.lvl_up(author_id): await message.channel.send(f"{message.author.mention} is now level {self.users[author_id]['level']}! 🎉") @commands.command(name="level") # Use commands.command() for cog commands async def level_command(self, ctx, member: discord.Member = None): member = ctx.author if not member else member member_id = str(member.id) if member_id not in self.users: await ctx.send("This user hasn't sent any messages yet!") else: embed = discord.Embed(color=member.color, timestamp=ctx.message.created_at) embed.set_author(name=f'Level Info - {member.display_name}', icon_url=member.avatar.url) embed.add_field(name='Level', value=self.users[member_id]['level'], inline=True) embed.add_field(name='EXP', value=self.users[member_id]['exp'], inline=True) # Optional: Add progress to next level required_xp = round((4 * (self.users[member_id]['level'] ** 3)) / 5) embed.add_field(name='To Next Level', value=f"{self.users[member_id]['exp']}/{required_xp}", inline=False) await ctx.send(embed=embed) # Don't forget to load the cog in your main bot code! # Example: # client = commands.Bot(command_prefix="!", intents=discord.Intents.all()) # client.add_cog(Levels(client)) # client.run("YOUR_BOT_TOKEN")
Key Improvements Added
- Added error handling for missing
users.jsonfiles to prevent startup crashes - Reduced save frequency to avoid unnecessary disk wear
- Improved the level-up logic to reset XP after leveling up (cleaner progression)
- Enhanced the embed to show the user's avatar and progress to the next level
- Renamed the command method to avoid name conflicts with the class itself
After making these changes, restart your bot, send a few messages, and you should see the level-up messages work correctly. The !level command will also display the user's level and experience properly.
内容的提问来源于stack exchange,提问作者Clonexy

