on_member_update nickname change doesn't work out as intended - discord

I was trying to set events for my bot, however, it always returns this error after trying to edit the nickname on my server:
#commands.Cog.listener()
async def on_member_update(self, ctx, member_before, member_after):
nickname_after = f"{member_after.nickname}" if not member_after.nickname else member_after.nickname
nickname_before = f"{member_before.nickname}" if not member_before.nickname else member_before.nickname
embed = discord.Embed(timestamp = ctx.message.created_at , color = discord.Colour.random() ,
title = "Member Update")
embed.add_field(name = "Nickname before:" , value = f"{nickname_before}" , inline = True)
embed.add_field(name = "Nickname after:" , value = f"{nickname_after}" , inline = True)
embed.set_thumbnail(url = f"{member_after.avatar.url}")
embed.set_author(name = f"{self.osamu.user}" , icon_url = f"{self.osamu.avatar.url}")
channel = self.osamu.get_channel(898199584726188042)
return await channel.send(embed = embed)
The following traceback has been detected:
Ignoring exception in on_member_update
Traceback (most recent call last):
File "/home/ohnezahn/.local/lib/python3.9/site-packages/discord/client.py", line 352, in _run_event
await coro(*args, **kwargs)
TypeError: on_member_update() missing 1 required positional argument: 'member_after'
What I'm trying to achieve: if the user is editing his nickname the bot shall send the embed in the Channel which I've set (channel = self.osamu.get_channel(898199584726188042)). However, the response is the traceback, no embed whatsoever...

According to the docs, on_member_update only takes 2 parameters: before and after. Since your listener seems to be in a class you also need to use self as the first argument.
So if you remove the ctx from your arguments it should work properly.
#commands.Cog.listener()
async def on_member_update(self, member_before, member_after):
https://discordpy.readthedocs.io/en/master/api.html?highlight=on_member_update#discord.on_member_update

Related

Discord.py user command counter

I am currently trying to implement a counter to show how many times a user has used a command like "*kiss #randomuser", and then it would show in the embed footer or under title "user has kissed someone {number} of times"
I have tried the solution found here How do I get a counter to work within a discord.py command function?
but nothing I have tried has worked, I apologize but im not every good with this
bot.counter = 0
#bot.command()
##commands.cooldown(1,10,commands.BucketType.user)
async def Random(ctx, member:discord.Member = None):
bot.counter += 1
if member == ctx.author:
em = discord.Embed(title=f'{ctx.author.display_name}, You can not perform this action on yourself ', color = random.randint(0, 16777215) )
await ctx.reply(embed=em)
else:
e = discord.Embed(title=f'{ctx.author.display_name} is doing something random to {member.name}', color = random.randint(0, 16777215))
content = []
link = random.choice(content)
e.set_image(url=f'{link}')
e.set_footer(text=f"This user has used this command {bot.counter}")
await ctx.reply(embed=e)
```

Discord.py exception "Missing 1 required positional argument: 'ctx' " although I included 'ctx' as one of the function parameters

so I'm having an issue while creating a discord music bot and I've been trying to solve it for a while now, but I can't find any solution.
Here's the constructor and three functions to search and play music that are necessary for the command to actually work (I'll only include the skip command since I get the same exception with the rest of the commands)
class music(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.is_playing = False
self.is_paused = False
self.music_queue = []
self.YDL_OPTIONS = {"format": "bestaudio", "noplaylist": "True"}
self.FFMPEG_OPTIONS = {"before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5", "options": "-vn"}
self.vc = None
async def play_music(self, ctx):
if len(self.music_queue) > 0:
self.is_playing = True
m_url = self.music_queue[0][0]["source"]
if self.vc == None or not self.vc.is_connected():
self.vc == await self.music_queue[0][1].connect()
if self.vc == None:
await ctx.send("I'm sorry, but I can't join the voice channel")
return
else:
await self.vc.move_to(self.music_queue[0][1])
self.music_queue.pop(0)
self.vc.play(discord.FFmpegPCMAudio(m_url, **self.FFMPEG_OPTIONS), after=lambda e: self.play_next())
else:
self.is_playing = False
def play_next(self):
if len(self.music_queue) > 0:
self.is_playing = True
m_url = self.music_queue[0][0]["source"]
self.music_queue.pop(0)
self.vc.play(discord.FFmpegPCMAudio(m_url, **self.FFMPEG_OPTIONS), after=lambda e: self.play_next())
else:
self.is_playing = False
def search(self, item):
with YoutubeDL(self.YDL_OPTIONS) as ydl:
try:
info = ydl.extract_info("ytsearch:{0}".format(item), download=False)["entries"][0]
except Exception:
return False
return {"source": info["formats"][0]["url"], "title": info["title"]}
async def setup(client):
await client.add_cog(music(client))
bot.py with setup_hook and the command
class MyBot(commands.Bot):
async def setup_hook(self):
await self.load_extension("dustobot")
#commands.command(aliases=["s"], help="Skips the current song that is being played")
async def skip(self, ctx, *args):
if self.vc != None and self.vc:
self.vc.stop()
await self.play_music(ctx) #Calling a function that plays the next song in a queue
And this is the error I get:
2023-01-21 12:28:37 ERROR discord.ext.commands.bot Ignoring exception in command skip
Traceback (most recent call last):
File "C:\Users\private_path\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ext\commands\core.py", line 229, in wrapped
ret = await coro(*args, **kwargs)
TypeError: music.skip() missing 1 required positional argument: 'ctx'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\private_path\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ext\commands\bot.py", line 1349, in invoke
await ctx.command.invoke(ctx)
File "C:\Users\private_path\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ext\commands\core.py", line 1023, in invoke
await injected(*ctx.args, **ctx.kwargs) # type: ignore
File "C:\Users\private_path\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\discord\ext\commands\core.py", line 238, in wrapped
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: music.skip() missing 1 required positional argument: 'ctx'
Is it because I'm using a deprecated discord.py syntax?
Thanks in advance for taking your time to help me, I really appreciate it.
Btw this is my main.py:
import os
import discord
from dustobot import music #This imports the music class
from dotenv import load_dotenv
from discord.ext import commands
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
intents = discord.Intents.default()
intents.message_content = True
client = commands.Bot(command_prefix="!", intents=intents)
client.remove_command("help") #I have my own help command
if __name__ == "__main__":
client.run(TOKEN)
The issue is caused by the way you created the command:
#client refers to the bot
#client.command(aliases=["s"], help="Skips the current song that is being played")
The decorator doesn't expect this to be a method in a class. As such, it's not invoked as a method either, so self won't be passed automatically and the Context will be the first argument (self). As a result, the library won't pass a value for ctx, which results in your error.
In cogs, commands should be created using #commands.command instead of #<Bot_instance>.command, the reason being that you shouldn't have your Bot instance available to you in that scope.
You could've also gotten this by reading the manual for Cogs...
Every command is marked with the commands.command() decorator.

get more input from one command (discord.py)

I'm trying to make an embed system that asks multiple questions and then put them together and send the embed with all the info given, but I can't figure out how to get multiple inputs in one command.
Based on what I understand from your question, you're only checking for one possible input with m.content == "hello". You can either remove it or add an in statement. Do view the revised code below.
#commands.command()
async def greet(ctx):
await ctx.send("Say hello!")
def check(m):
return m.channel == channel # only check the channel
# alternatively,
# return m.content.lower() in ["hello", "hey", "hi"] and m.channel == channel
msg = await bot.wait_for("message", check=check)
await ctx.send(f"Hello {msg.author}!")
In the case of the newly edited question, you can access the discord.Message class from the msg variable. For example, you can access the message.content. Do view the code snippet below.
#commands.command()
async def test(ctx):
def check(m):
return m.channel == ctx.channel and m.author == ctx.author
# return message if sent in current channel and author is command author
em = discord.Embed()
await ctx.send("Title:")
msg = await bot.wait_for("message",check=check)
em.title = msg.content
# in this case, you can continue to use the same check function
await ctx.send("Description:")
msg = await bot.wait_for("message",check=check)
em.description = msg.content
await ctx.send(embed=em)

How to check if a user has permission to use a command? Discordpy

I am creating a custom help command and I have a problem. The default help command can somehow hide commands that are not available for a user that invoked the command. I was looking everywhere and I can't find any solution. I tried using hidden attribute of a command object but it is not working. The best solution would be a method like Member.can_use(command) that would return True or False but there is nothing like that in docs. Or at least I cannot find it.
Help would be appreciated.
Use the Command.can_run method, an example would be:
#bot.command()
async def can_it_run(ctx, command_name: str):
command = bot.get_command(command_name)
can_use = await command.can_run(ctx)
if can_use:
await ctx.send(f"You can use {command_name}")
else:
await ctx.send(f"You can't use {command_name}")
Sadly if a check raises an exception (like commands.guild_only) the exception(s) won't be suppressed, also if you want to check if someone else (not the invoker) can use the command you have to overwrite Context.author, a handy function would be:
async def can_run(ctx, command_name: str, member: discord.Member=None) -> bool:
command = bot.get_command(command_name)
if command is None: # Command doesn't exist / invalid command name
return False # Or raise an exception
if member is not None: # If a member is passed overwrite the attribute
ctx.author = member
try:
return await command.can_run(ctx)
except Exception:
return False
#bot.command()
async def can_it_run(ctx, command_name: str, member: discord.Member=None):
resp = await can_run(ctx, command_name, member)
await ctx.send(resp)
You could just check if the user countains a certain id by doing the following:
if (message.author.id == "WHoever ID1" || message.author.id == "Whoever ID2"):
#Do Something
If you want to put a bunch of ID's in then just do the following:
bool = false;
arr = ["ID1", "ID2", "ID3"]
for x in arr:
if(x == message.author.id):
bool = true;
if(bool):
#Do stuff
bool = false;
This is very vague code but it should be something similar to this

No JSON object could be decoded - RPC POST call

var body = JSON.stringify(params);
// Create an XMLHttpRequest 'POST' request w/ an optional callback handler
req.open('POST', '/rpc', async);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.setRequestHeader("Content-length", body.length);
req.setRequestHeader("Connection", "close");
if (async) {
req.onreadystatechange = function() {
if(req.readyState == 4 && req.status == 200) {
var response = null;
try {
response = JSON.parse(req.responseText);
} catch (e) {
response = req.responseText;
}
callback(response);
}
};
}
// Make the actual request
req.send(body);
---- on the server side ----
class RPCHandler(BaseHandler):
'''#user_required'''
def post(self):
RPCmethods = ("UpdateScenario", "DeleteScenario")
logging.info(u'body ' + self.request.body)
args = simplejson.loads(self.request.body)
---- Get the following error on the server logs
body %5B%22UpdateScenario%22%2C%22c%22%2C%224.5%22%2C%2230frm%22%2C%22Refinance%22%2C%22100000%22%2C%22740%22%2C%2294538%22%2C%2250000%22%2C%22owner%22%2C%22sfr%22%2C%22Fremont%22%2C%22CA%22%5D=
No JSON object could be decoded: line 1 column 0 (char 0):
Traceback (most recent call last):
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/webapp/_webapp25.py", line 703, in call
handler.post(*groups)
File "/base/data/home/apps/s~mortgageratealert-staging/1.357912751535215625/main.py", line 418, in post
args = json.loads(self.request.body)
File "/base/python_runtime/python_lib/versions/1/simplejson/init.py", line 388, in loads
return _default_decoder.decode(s)
File "/base/python_runtime/python_lib/versions/1/simplejson/decoder.py", line 402, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/base/python_runtime/python_lib/versions/1/simplejson/decoder.py", line 420, in raw_decode
raise JSONDecodeError("No JSON object could be decoded", s, idx)
JSONDecodeError: No JSON object could be decoded: line 1 column 0 (char 0)
--- firebug shows the following ---
Parameters application/x-www-form-urlencoded
["UpdateScenario","c","4....
Source
["UpdateScenario","c","4.5","30frm","Refinance","100000","740","94538","50000","owner","sfr","Fremont","CA"]
Based on the firebug report and also the logs shows self.request.body as anticipated. However simplejson load doesn't like it.
Please help!
TWO OPTIONS:
It seems that you need to escape the contents of self.request.body.
Import urllib and change the last line of code in your sample to:
args = simplejson.loads(urllib.unquote(self.request.body))
Or you can try this other option: You are sending a complete json string in the POST body. You might not need to have the following line in your javascript:
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
Take it out, and see if you still need to use the urllib.unquote solution above.
You do not send JSON content. You have to add this header : contentType: 'application/json'

Resources