Discord.py : coroutine was never awaited - python-3.x

Can't seem to fix this code where I'm trying to get the name of the discord server from it's invite link.
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
async def get_invite_name(link):
name = await client.fetch_invite(link).guild.name
return name
print(get_invite_name('https://discord.com/invite/veefriends'))
Tried putting await infront of client.fetch_invite(link).guild.name but it didn't work. I don't understand async.
Tried what #matthew-barlowe suggested but it spit out more errors -
File "~/DLG/sandbox.py", line 14, in <module>
print(asyncio.run(get_invite_name('https://discord.com/invite/veefriends')))
File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
return future.result()
File "~/DLG/sandbox.py", line 12, in get_invite_name
return await invite.guild.name
AttributeError: 'coroutine' object has no attribute 'guild'
sys:1: RuntimeWarning: coroutine 'Client.fetch_invite' was never awaited

You have to await the async wrapper function get_invite_name as well. Running it in asyncio.run(get_invite_name('https://discord.com/invite/veefriends')) will handle that in a non async setting. You will need to import asyncio as well.
import discord
import asyncio
from discord.ext import commands
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
async def get_invite_name(link):
response = await client.fetch_invite(link)
name = response.guild.name
return name
print(asyncio.run(get_invite_name('https://discord.com/invite/veefriends')))
If you were calling it in another async function then just await get_invite_name('https://discord.com/invite/veefriends') would be sufficient

Related

Discord bot runs differently when called in-file and from outside file

I am trying to integrate a discord bot into an app such that when the user of the app does something, it calls a function that uploads something to discord. This means that the bot only has an on_ready function, and then a client.close() at the end. I wrapped the entire thing inside its own function for easy access from the main app file. Here's the code
def uploader(filePath):
homeDir = str(Path.home())
with open(f'{homeDir}/Library/Application Support/FileBreakerApp/userInfo.json', 'r') as json_file:
userInfo = json.load(json_file)
bot = discord.Client()
#bot.event
async def on_ready():
botChannel = await bot.fetch_channel(userInfo["botChannel"])
await botChannel.send("This confirms that uploader is running")
file = SplitFile(filePath)
for name in file.chunkNames:
fileObj = discord.File(
open(f'{homeDir}/Library/Application Support/FileBreakerApp/filePieces/{name}', 'rb'),
filename=name)
await botChannel.send(file=fileObj)
print("sent file")
await botChannel.send(f'successfully uploaded:{file.fullName}')
await bot.close()
bot.run('CENSORED')
What I want it to do is after it sends "sucessfully uploaded..." it will close the bot, so the code can move on from bot.run() and end the method. This worked perfectly when I called the outermost function within the file it was written in, but when I called it from the main file it threw the following error:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 1884, in __call__
return self.func(*args)
File "/Users/nathanwolf/Documents/coding/PycharmProjects/Discord-File-Breaker/Interface.py", line 62, in uploadFile
uploader(filename)
File "/Users/nathanwolf/Documents/coding/PycharmProjects/Discord-File-Breaker/Uploader.py", line 27, in uploader
bot.run('CENSORED')
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/discord/client.py", line 635, in run
loop.add_signal_handler(signal.SIGINT, lambda: loop.stop())
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/unix_events.py", line 89, in add_signal_handler
self._check_closed()
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 510, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
I read some sources saying that it had to do with enabling server members intent, so I did that on the developer portal, to no avail. Few others seem to have this problem, and one source said it had to do with bot.close(). Given that, here is my current theory:
I run several others of this kind of function in the app (a bot nested in a function), all with the same token so I could run them through one bot user. By the time this runs, another function like it has been run already, with no issues. This suggests that when it closes the event loop, the next call of bot.run doesn't reopen it.
How can I reopen the bot's event loop at the beginning of this function?(bot.start(TOKEN) doesn't appear to work)
Thanks!
You essentially need to create a new event loop before running this. You can either do this in the function or better (imo) make this an async function and create an event loop in your main file.
def uploader(filePath):
import asyncio
homeDir = str(Path.home())
with open(f'{homeDir}/Library/Application Support/FileBreakerApp/userInfo.json', 'r') as json_file:
userInfo = json.load(json_file)
bot = discord.Client()
#bot.event
async def on_ready():
botChannel = await bot.fetch_channel(userInfo["botChannel"])
await botChannel.send("This confirms that uploader is running")
file = SplitFile(filePath)
for name in file.chunkNames:
fileObj = discord.File(
open(f'{homeDir}/Library/Application Support/FileBreakerApp/filePieces/{name}', 'rb'),
filename=name)
await botChannel.send(file=fileObj)
print("sent file")
await botChannel.send(f'successfully uploaded:{file.fullName}')
await bot.close()
loop = asyncio.new_event_loop()
loop.run_until_complete(bot.start('CENSORED')
loop.close()
OR
# main.py
import asyncio as aio
loop = aio.new_event_loop()
loop.run_until_complete(uploader("./YOUR/PATH/HERE"))
loop.close()
# your func
async def uploader(filePath):
homeDir = str(Path.home())
with open(f'{homeDir}/Library/Application Support/FileBreakerApp/userInfo.json', 'r') as json_file:
userInfo = json.load(json_file)
bot = discord.Client()
#bot.event
async def on_ready():
botChannel = await bot.fetch_channel(userInfo["botChannel"])
await botChannel.send("This confirms that uploader is running")
file = SplitFile(filePath)
for name in file.chunkNames:
fileObj = discord.File(
open(f'{homeDir}/Library/Application Support/FileBreakerApp/filePieces/{name}', 'rb'),
filename=name)
await botChannel.send(file=fileObj)
print("sent file")
await botChannel.send(f'successfully uploaded:{file.fullName}')
await bot.close()
await bot.start('CENSORED')

Discordpy welcome bot

So, i tried to make a bot that send embed to specific channel everytime user join my server.
The code is look like this
import discord
import asyncio
import datetime
from discord.ext import commands
intents = discord.Intents()
intents.members = True
intents.messages = True
intents.presences = True
bot = commands.Bot(command_prefix="a!", intents=intents)
#bot.event
async def on_ready():
print('Bot is ready.')
#bot.event
async def on_member_join(ctx, member):
embed = discord.Embed(colour=0x1abc9c, description=f"Welcome {member.name} to {member.guild.name}!")
embed.set_thumbnail(url=f"{member.avatar_url}")
embed.set_author(name=member.name, icon_url=member.avatar_url)
embed.timestamp = datetime.datetime.utcnow()
channel = guild.get_channel(816353040482566164)
await channel.send(embed=embed)
and i got an error
Ignoring exception in on_member_join
Traceback (most recent call last):
File "C:\Users\Piero\AppData\Roaming\Python\Python39\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "C:\Users\Piero\Documents\Discord\a-chan\achan_bot\main.py", line 24, in on_member_join
channel = guild.get_channel(816353040482566164)
NameError: name 'guild' is not defined
Anyone know what is wrong in my code?
First of all, looking at the discord.py documention, ctx is not passed to the on_member_join event reference. However, you can use the attributes of member which is passed in order to get the values which you need.
#bot.event
async def on_member_join(member):
embed = discord.Embed(
colour=0x1abc9c,
description=f"Welcome {member.name} to {member.guild.name}!"
)
embed.set_thumbnail(url=f"{member.avatar_url}")
embed.set_author(name=member.name, icon_url=member.avatar_url)
embed.timestamp = datetime.datetime.utcnow()
channel = member.guild.get_channel(816353040482566164)
await channel.send(embed=embed)
Interestingly enough, you did this perfectly for getting the guild name, but it seems you forgot to do the same when retrieving channel.
You did not define guild.
To define your guild you can do the following:
guild = bot.get_guild(GuildID)
It's the same method you used to define your channel, just for your guild now.
For further information you can have a look at the docs: https://discordpy.readthedocs.io/en/latest/api.html#discord.Client.get_guild
Also take into account that we do not have a paramter like ctx in an on_member_join event.
The event just has the parameter member in your case:
#bot.event
async def on_member_join(member): #Removed ctx

Send Discord Message from a looped function

So I've run into a wall trying to get this code to work.
I'm writing a fairly simple Discord bot that does two things.
The main part of the bot is to run an API request when the bot initializes to get a base value of a return. I then have a function that I want to run every X seconds, that checks the value again, and if it changes, send a message to a Discord Channel and then update the variable to the new value, so that the loop can report when it changes again.
The second part, which is not as critical but I still want to include it, is that a user can send a bot command to report the current value of the return.
Problem #1: Arises when I start the loop without the code to send the result via discord message. If i don't include the line to send the discord message in the loop code, the loop runs and prints out the results to the console (for test purposes). However, when the loop is running, I can no longer get the bot to respond to the command.
Problem #2: If I include the code to send the discord message, the whole thing immediately fails and gives me the following error:
File "bot.py", line 67, in <module>
asyncio.run(update_status())
File "/usr/lib/python3.7/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
return future.result()
File "bot.py", line 63, in update_status
await channel.send(f'Server status is still: {updatestatus}.')
File "/usr/local/lib/python3.7/dist-packages/discord/abc.py", line 905, in send
nonce=nonce, allowed_mentions=allowed_mentions)
File "/usr/local/lib/python3.7/dist-packages/discord/http.py", line 185, in request
async with self.__session.request(method, url, **kwargs) as r:
File "/usr/local/lib/python3.7/dist-packages/aiohttp/client.py", line 1012, in __aenter__
self._resp = await self._coro
File "/usr/local/lib/python3.7/dist-packages/aiohttp/client.py", line 357, in _request
raise RuntimeError('Session is closed')
RuntimeError: Session is closed
Problem #3: More of a minor nuisance, but if I set my code up so that it works in Problem #1, the loop does not start automatically, but only after I press Ctrl+C (note: I am writing the code on a Raspi 4 via PuTTY.
Any thoughts?
Full Code:
# bot.py
import os
import requests
import discord
import json
import asyncio
from dotenv import load_dotenv
from discord.ext import commands
from discord.ext import tasks
from discord.utils import get
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
CLIENTID = os.getenv('CLIENT_ID')
CLIENTSECRET = os.getenv('CLIENT_SECRET')
bot = commands.Bot(command_prefix='+')
#bot.event
async def on_ready():
print(f'{bot.user.name} has connected to Discord!')
channel = bot.get_channel(795162312112865280)
await channel.send('MurkyBot has connected')
def create_access_token(client_id, client_secret, region = 'us'):
data = { 'grant_type': 'client_credentials' }
response = requests.post('https://%s.battle.net/oauth/token' % region, data=data, auth=(client_id, client_secret))
return response.json()
tokenresponse = create_access_token(CLIENTID, CLIENTSECRET)
accesstoken = tokenresponse["access_token"]
print(accesstoken)
initialrequest = requests.get(f'https://us.api.blizzard.com/data/wow/connected-realm/154?namespace=dynamic-us&locale=en_US&access_token={accesstoken}')
initialrequest = initialrequest.json()
initialstatus = initialrequest['status']['type']
print(f'Initial status: {initialstatus}')
#bot.command(name='status', help='Gets the current server status')
async def manual_status(ctx):
manualrequest = requests.get(f'https://us.api.blizzard.com/data/wow/connected-realm/154?namespace=dynamic-us&locale=en_US&access_token={accesstoken}')
manualrequest = manualrequest.json()
manualstatus = manualrequest['status']['type']
channel = bot.get_channel(795162312112865280)
await ctx.send(f'Current world server status is: {manualstatus}')
bot.run(TOKEN)
async def update_status():
while True:
global initialstatus
channel = bot.get_channel(795162312112865280)
updaterequest = requests.get(f'https://us.api.blizzard.com/data/wow/connected-realm/154?namespace=dynamic-us&locale=en_US&access_token={accesstoken}')
updaterequest = updaterequest.json()
updatestatus = updaterequest['status']['type']
if updatestatus != initialstatus:
await channel.send(f'Server status has changed to: {updatestatus}!')
initialstatus = updatestatus
print('Status Change')
await asyncio.sleep(5)
else:
await channel.send(f'Server status is still: {updatestatus}.')
print('No Change')
await asyncio.sleep(5)
asyncio.run(update_status())
.run is a blocking call, meaning that everything stops and waits for it to complete. Therefore, the second part can only run after you ctrl+c, ending the script however.
You may want to check out discord.ext.tasks, it will have the intended functionality.
For what you're trying to do, here would be the updated code using discord.ext.tasks:
from dotenv import load_dotenv
from discord.ext import commands
from discord.ext import tasks
from discord.utils import get
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
CLIENTID = os.getenv('CLIENT_ID')
CLIENTSECRET = os.getenv('CLIENT_SECRET')
bot = commands.Bot(command_prefix='+')
#tasks.loop(seconds=60.0)
async def update_status():
while True:
global initialstatus
channel = bot.get_channel(795162312112865280)
updaterequest = requests.get(f'https://us.api.blizzard.com/data/wow/connected-realm/154?namespace=dynamic-us&locale=en_US&access_token={accesstoken}')
updaterequest = updaterequest.json()
updatestatus = updaterequest['status']['type']
if updatestatus != initialstatus:
await channel.send(f'Server status has changed to: {updatestatus}!')
initialstatus = updatestatus
print('Status Change')
await asyncio.sleep(5)
else:
await channel.send(f'Server status is still: {updatestatus}.')
print('No Change')
await asyncio.sleep(5)
#bot.event
async def on_ready():
update_status.start()
print(f'{bot.user.name} has connected to Discord!')
channel = bot.get_channel(795162312112865280)
await channel.send('MurkyBot has connected')
def create_access_token(client_id, client_secret, region = 'us'):
data = { 'grant_type': 'client_credentials' }
response = requests.post('https://%s.battle.net/oauth/token' % region, data=data, auth=(client_id, client_secret))
return response.json()
tokenresponse = create_access_token(CLIENTID, CLIENTSECRET)
accesstoken = tokenresponse["access_token"]
print(accesstoken)
initialrequest = requests.get(f'https://us.api.blizzard.com/data/wow/connected-realm/154?namespace=dynamic-us&locale=en_US&access_token={accesstoken}')
initialrequest = initialrequest.json()
initialstatus = initialrequest['status']['type']
print(f'Initial status: {initialstatus}')
#bot.command(name='status', help='Gets the current server status')
async def manual_status(ctx):
manualrequest = requests.get(f'https://us.api.blizzard.com/data/wow/connected-realm/154?namespace=dynamic-us&locale=en_US&access_token={accesstoken}')
manualrequest = manualrequest.json()
manualstatus = manualrequest['status']['type']
channel = bot.get_channel(795162312112865280)
await ctx.send(f'Current world server status is: {manualstatus}')
bot.run(TOKEN)

discord.py temporary voices

I am making my bot for discord, I want to do this, when a user clicks on a certain voice channel, a new voice channel is created for him, which is deleted upon exit. Here is the code:
import discord
from discord.ext import commands
from discord.utils import get
import asyncio
TOKEN = 'xxxx'
bot = commands.Bot(command_prefix='!')
#bot.event
async def on_voice_state_update(member, before, after):
if after.channel != None:
if after.channel.id == 700246237244555338:
for guild in bot.guilds:
maincategory = discord.utils.get(
guild.categories, id=700246237244555336)
channel2 = guild.create_voice_channel(name=f'канал {member.display_name}', category=maincategory)
await channel2.set_permissions(member, connect=True, mute_members=True, manage_channels=True)
await member.move_to(channel2)
def check(x, y, z):
return len(channel2.members) == 0
await bot.wait_for('voice_state_update', check=check)
await channel2.delete()
# RUN
bot.run(TOKEN)
But i have error...
Ignoring exception in on_voice_state_update
Traceback (most recent call last):
File "C:\Users\asus\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\discord\client.py", line 333, in _run_event
await coro(*args, **kwargs)
File "jett.py", line 190, in on_voice_state_update
await member.move_to(channel2)
File "C:\Users\asus\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\discord\member.py", line 626, in move_to
await self.edit(voice_channel=channel, reason=reason)
File "C:\Users\asus\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\discord\member.py", line 592, in edit
payload['channel_id'] = vc and vc.id
AttributeError: 'coroutine' object has no attribute 'id'
C:\Users\asus\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\discord\client.py:340: RuntimeWarning: coroutine 'Guild.create_voice_channel' was never awaited
pass
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Can you help me with this problem or just send me working code for temporary voices
RuntimeWarning: Enable tracemalloc to get the object allocation traceback usually means that you forgot to add an await keyword to an async function. The async function in question is likely create_voice_channel, as the documentation says it is an async function.
To fix this, you'll want to add the await keyword before the function call, similar to this:
channel2 = await guild.create_voice_channel(name=f'канал {member.display_name}', category=maincategory)

Discord.py - passing an argument role functions

I wanted to create a command, like !iknow #user . A normal verification bot I think. Here's my code, I only pasted the important parts:
import discord
from discord.ext import commands
#client.command(pass_context=True)
async def iknow(ctx, arg):
await ctx.send(arg)
unknownrole = discord.utils.get(ctx.guild.roles, name = "Unknown")
await client.remove_roles(arg, unknownrole)
knownrole = discord.utils.get(ctx.guild.roles, name = "Verified")
await client.add_roles(arg, knownrole)
(The Unknown role is automatically passed when a user joins the server.)
The problem is: I get an error on line 6 (and I think I will get on line 9, too).
File
"/home/archit/.local/lib/python3.7/site-packages/discord/ext/commands/core.py",
line 83, in wrapped
ret = await coro(*args, **kwargs) File "mainbot.py", line 6, in iknow
await client.remove_roles(arg, unknownrole) AttributeError: 'Bot' object has no attribute 'remove_roles'
I just started learning python, so please don't bully me!
You're looking for Member.remove_roles and Member.add_roles.
You can also specify that arg must be of type discord.Member, which will automatically resolve your mention into the proper Member object.
Side note, it's no longer needed to specify pass_context=True when creating commands. This is done automatically, and context is always the first variable in the command coroutine.
import discord
from discord.ext import commands
client = commands.Bot(command_prefix='!')
#client.command()
async def iknow(ctx, arg: discord.Member):
await ctx.send(arg)
unknownrole = discord.utils.get(ctx.guild.roles, name="Unknown")
await arg.remove_roles(unknownrole)
knownrole = discord.utils.get(ctx.guild.roles, name="Verified")
await arg.add_roles(knownrole)
client.run('token')

Resources