Using get_member in Cogs - python-3.x

I have two applications one main.py which is my main file and the other one is Loops.py in which I use Cogs and tasks inside them so there is a problem.I have a function to change the nickname of my bot every second to another name like this :
botsnick = cycle(['𝐊','𝐊𝐚','𝐊𝐚𝐊','𝐊𝐚𝐊𝐚','𝐊𝐚𝐊𝐚𝐁','𝐊𝐚𝐊𝐚𝐁𝐨','𝐊𝐚𝐊𝐚𝐁𝐨𝐭','𝐊𝐚𝐊𝐚𝐁𝐨𝐭 𝐓','𝐊𝐚𝐊𝐚𝐁𝐨𝐭 𝐓𝐞','𝐊𝐚𝐊𝐚𝐁𝐨𝐭 𝐓𝐞𝐬','𝐊𝐚𝐊𝐚𝐁𝐨𝐭 𝐓𝐞𝐬𝐭'])
#tasks.loop(seconds=1)
async def change_nick():
server = client.get_guild(771043820157534228)
bots = server.get_member(848887911884521502)
await bots.edit(nick=next(botsnick))
It is working properly in main.py but as soon as I implement this code into Loops.py and inside Cog like this :
class Loops(commands.Cog):
def __init__(self,client):
self.client = client
self.change_nick.start()
botsnick = cycle(['𝐊','𝐊𝐚','𝐊𝐚𝐊','𝐊𝐚𝐊𝐚','𝐊𝐚𝐊𝐚𝐁','𝐊𝐚𝐊𝐚𝐁𝐨','𝐊𝐚𝐊𝐚𝐁𝐨𝐭','𝐊𝐚𝐊𝐚𝐁𝐨𝐭 𝐓','𝐊𝐚𝐊𝐚𝐁𝐨𝐭 𝐓𝐞','𝐊𝐚𝐊𝐚𝐁𝐨𝐭 𝐓𝐞𝐬','𝐊𝐚𝐊𝐚𝐁𝐨𝐭 𝐓𝐞𝐬𝐭'])
#tasks.loop(seconds=1)
async def change_nick(self):
server = self.client.get_guild(771043820157534228)
bots = server.get_member(848887911884521502)
await bots.edit(nick=next(self.botsnick))
def setup(client):
client.add_cog(Loops(client))
I get this error :
bots = server.get_member(848887911884521502)
AttributeError: 'NoneType' object has no attribute 'get_member'
but there is no error in main.py if I use this fucntion there what is wrong?

get_member gets the Member from cache, since the loop starts immediately, your bot's internal cache isn't ready, as a result it doesn't find anything and returns None
Simple fix is to use await self.client.wait_until_ready() in a #yourtaskname.before_loop
client.wait_until_ready()

Related

Echo a message from another app in discord

I have a bot running in a separate app, but there is a specific variable holding data that I want to also be echo'd on my discord server. The bot itself is huge, but I can pin the specific method here
import discord
import asyncio
import rpChat
global note
class EchoBot(rpChat.ChatClient):
def on_PRI(self, character, message):
super().on_PRI(character, message)
note = character + ": " + message
global note
if message[:1] == "~":
super().PRI("A GameMaster", note)
to try to send this message to discord, I have the following. This is not put in the class above, but just below it, and is not in a class itself:
client = discord.Client()
async def discordEcho():
"""Background task that sends message sent from chat to an appropriate Discord channel
when global messege data is updated"""
await client.wait_until_ready()
channel = client.get_channel(serverroom}
while not client.is_closed():
global note
await channel.send(channel, note)
The data to grab the channel IDs are found in a json
file = open("credentials.json", "r", encoding="utf-8")
info = json.load(file)
file.close()
token = info["discord"]
serverroom = info["serverroom"]
client.loop.create_task(discordEcho())
client.run(token)
When I try this, I obtain:
await client.send_message(channel, message)
AttributeError: 'Client' object has no attribute 'send_message'
And I am unsure why. I have built a bot for both this chat platform, and for discord, but this is the first time I've ever attempted to bridge messages between the two. I am sure what I have offered is clear as mud, but any help would be appreciated, if possible.
Edit:
Edited changes to code. It is working thanks to comment below.
client.send_message is an old, outdated method, it has been replaced with Messageable.send
async def discordEcho():
"""Background task that sends message sent from chat to an appropriate Discord channel
when global messege data is updated"""
await client.wait_until_ready()
channel = client.get_channel(serverroom)
while not client.is_closed():
global note
await channel.send(note)
Side note: you first need to wait_until_ready before getting the channel to wait until the cache is done loading, otherwise channel will be None

Discord.py API get user data from IP

I've been looking into the Discord API source code and I was wondering if you could get a user's data. Not just returning their username. There is nothing that I could find in the API's docks.
This is the furthest I got:
data = await discord.client.Client.fetch_user_profile(discord.client.Client.fetch_user_profile, id)
But I keep on getting the error:
CommandInvokeError: Command raised an exception: AttributeError: 'function' object has no attribute '_connection'
Caused by a what I think may be a function and a variable having the same name. Are there any ways to fix this, or any other methods to get a users data that works. Preferably without having to change the source code. Thanks for any help in advance.
And just in case you need it, here is the entirety of my code:
#import libarys
import discord
from discord.ext import commands
import os
#setup bot
bot = commands.Bot(command_prefix='&', owner_id=MyID, case_insensitive=True)
#bot.event
async def on_ready():
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name='for &help'))
print(f'\n\nLogged in as: {bot.user} - {bot.user.id}\nVersion: {discord.__version__}\n')
#find user data command
#bot.command()
async def find(ctx, id: int):
member = await ctx.guild.fetch_member(id)
data = await discord.client.Client.fetch_user_profile(discord.client.Client.fetch_user_profile, id)
print(member, data)
#connect bot
bot.run(os.environ.get('TOKEN'), bot=True, reconnect=True)
EDIT: This code is just a mock up to get a working concept, I don't care to make it more refined yet.
This is a really bad code, I don't even know how you came up with that code. You're not supposed to refer to the class itself but to the instance, your code fixed
#bot.command()
async def find(ctx, id: int):
member = await ctx.guild.fetch_member(id)
data = await bot.fetch_user_profile(id)
print(member, data)
Also note that the fetch_user_profile method it's deprecated since version 1.7 and bot accounts cannot use this endpoint
Reference:
Client.fetch_user_profile

discord.py - Join/Leave messages error, doesn't work

Again, my code is not catching up, I have no idea why ...
The console shows no error.
Here is my code:
#bot.event
async def on_member_join(member):
channel = bot.get_channel(792786709350973454)
author = ctx.message.author
ico = author.avatar_url
embed = discord.Embed(
color = discord.Color.red(),
)
embed.set_author(name=(f'• Hello {member} on my server!!'))
embed.add_field(name='Warning,', value='read the regulations!', inline=False)
embed.set_footer(text="dBot created by Diablo#4700", icon_url=ico)
await channel.send(embed=embed)
There are more then one problems in the code snippet. I'll list them all here with fixes.
Firstly the main problem, you are declaring an author variable with ctx.author.name What is ctx here? ctx is passed in commands only. This is an event. It uses member as parameter only you can't use ctx here.
Secondly, the next problem which isn't really playing a part in stopping the command output but is a problem that you are sending member in your embed author message. Again member is an object and you can't use it directly you have to put the attribute after member that you need like, member.name, member.mention, member.avatar_url
Fixed Code:
#bot.event
async def on_member_join(member):
channel = bot.get_channel(792786709350973454)
# author = ctx.message.author # first problem, you don't really need this line
ico = member.avatar_url # since above line is useless you would change this line too
embed = discord.Embed(
color = discord.Color.red(),
)
embed.set_author(name=f'• Hello {member.name} on my server!!') # second problem was here.
embed.add_field(name='Warning,', value='read the regulations!', inline=False)
embed.set_footer(text="dBot created by Diablo#4700", icon_url=ico)
await channel.send(embed=embed)
I have labelled the errors with comments.
(IMPORTANT) Intents:
Just in case you are not aware, You need privileged gateway intents in order to track member events. Make sure to enable both intents from bot section in your application from Developers Portal of Discord and then in your code at the top (where you are initializing bot)...
import discord
from discord.ext import commands
client= commands.Bot(command_prefix="prefix", intents=discord.Intents.all())
...
# rest of the code
References:
on_member_join event
context or ctx
Intents Discord.Py

Discord.py, Required argument that is missing

from discord.utils import get
import discord
TOKEN = '########' #Taken out
BOT_PREFIX = '!'
ROLE = 'Bot'
bot = commands.Bot(command_prefix=BOT_PREFIX)
#bot.event
async def on_ready():
print("Logged in as: " + bot.user.name + "\n")
#bot.command(pass_context=True)
#commands.has_role("Sergeant") # This must be exactly the name of the appropriate role
async def addrole(ctx, members):
member = ctx.message.author
role = get(member.server.roles, name="Test")
await bot.add_roles(member, role)
bot.run(TOKEN)
So the code above is what I use.
Traceback (most recent call last):
File "C:\Users\Joshua\AppData\Local\Programs\Python\Python37-32\lib\site-packages\discord\ext\commands\bot.py", line 892, in invoke
await ctx.command.invoke(ctx)
File "C:\Users\Joshua\AppData\Local\Programs\Python\Python37-32\lib\site-packages\discord\ext\commands\core.py", line 790, in invoke
await self.prepare(ctx)
File "C:\Users\Joshua\AppData\Local\Programs\Python\Python37-32\lib\site-packages\discord\ext\commands\core.py", line 751, in prepare
await self._parse_arguments(ctx)
File "C:\Users\Joshua\AppData\Local\Programs\Python\Python37-32\lib\site-packages\discord\ext\commands\core.py", line 670, in _parse_arguments
transformed = await self.transform(ctx, param)
File "C:\Users\Joshua\AppData\Local\Programs\Python\Python37-32\lib\site-packages\discord\ext\commands\core.py", line 516, in transform
raise MissingRequiredArgument(param)
discord.ext.commands.errors.MissingRequiredArgument: author is a required argument that is missing.
And this is the error, I belive this is todo with the rewrite but honestly I can't get to grips with it. If anyboby could shine some light on this I would really appreciate it.
Alright, so first you need to import commands from discord at the top of your file. Your bot variable calls this when grabbing the prefix:
from discord.ext import commands
Next, you no longer need pass_content=True in #bot.command(), as context is now always passed. The documentation stating that can be found here.
I noticed that you are checking if the author has a role by name. Although this does work, a better method is checking if the author has the permissions to manage roles, or if he/she is an administrator. That is typed like so:
#commands.has_permissions(administrator=True)
A full list of the different arguments that you can pass into that decorator are found here.
Now regarding your error message, you have a parameter called members in your command function. Since this is a positional parameter, you must provide it when typing your command in discord. However, its not doing anything in your code, so you can remove it:
async def addrole(ctx):
More info on command parameters can be found here.
In you variable named member, you can change ctx.message.author to ctx.author.
In the role variable, when attempting to get the roles on your server, we no longer use member.server.roles. It has been changed to guild, as shown in the docs. Here's how you would type it:
role = get(member.guild.roles, name="Test")
And the final change is adding the role to the member. The rewrite version of discord.py removed a lot of functionality from bot. The add_roles function is now called from a member object, so this would be written like so:
await member.add_roles(role)
We're taking your member variable (ctx.author, which is a member object) and adding a role (the role variable, which fetches a role object with the name Test) to that member. Docs on it are found here.
So your code should look something like this:
from discord.ext import commands
from discord.utils import get
import discord
TOKEN = '########' # Taken out
BOT_PREFIX = '!'
ROLE = 'Bot'
bot = commands.Bot(command_prefix=BOT_PREFIX)
#bot.event
async def on_ready():
print(f"Logged in as: {bot.user.name}")
#bot.command()
#commands.has_permissions(administrator=True)
async def addrole(ctx):
member = ctx.author
role = get(member.guild.roles, name="Test")
await member.add_roles(role)
bot.run(TOKEN)
I hope this helps with your problem :-)
Here's some links that can help you get a better grip on how discord.py-rewrite works:
Migrating to the rewrite branch
Discord.py Rewrite API

Python async CancelledError() with no details

The following code fails and I'm not able to get the actual error, I just get numerous CancelledError messages
import aiobotocore, asyncio
async def replicate_to_region(chunks, region):
session = aiobotocore.get_session()
client = session.create_client('dynamodb', region_name=region)
start = 0
while True:
chunk = chunks[start]
item = {'my_table': chunk}
response = await client.batch_write_item(RequestItems=item)
async def main():
asyncio.gather(*(replicate_to_region(payloads, region) for region in regions))
asyncio.run(main())
I get the following errors;
client_session: <aiohttp.client.ClientSession object at 0x7f6fb65a34a8>
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7f6fb64c82b0>
_GatheringFuture exception was never retrieved
future: <_GatheringFuture finished exception=CancelledError()>
concurrent.futures._base.CancelledError
_GatheringFuture exception was never retrieved
future: <_GatheringFuture finished exception=CancelledError()>
I've tried quite a number of variations of the replicate_to_region function but they all fail with the same error above. It would be useful just to be able to see what the actual error is.
async def main():
asyncio.gather(...)
asyncio.gather() is an awaitable itself:
awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)
It means you should use await when deal with it:
async def main():
await asyncio.gather(*(replicate_to_region(payloads, region) for region in regions))
off-topic:
I didn't work with aiobotocore and not sure if it's important, but it's better to do as documentation says. In particular you should probably use async with when creating a client as example shows.

Resources