Why is my discord bot only executing my command one time and one time only? - python

I'm in the process of finishing a simple sound clip Discord bot I made by recycling a basic music bot example in python. All I want the bot to do is enter the voice channel of the user who called the command (!womble), play a random sound clip from a folder of sound clips, then leave the voice channel.
"Simple, right?" Of course it isn't, not with this API apparently.
After a BUNCH of trial and error, looking to at least 3 API revisions I got the bot to actually perform the command.....one time. Any further prompts of the command are met with crickets. I can do a !summon and it bring the bot into the channel, but the !womble command no longer works.
def bot_leave(self, ctx):
state = self.get_voice_state(ctx.message.server)
coro = state.voice.disconnect()
fut = asyncio.run_coroutine_threadsafe(coro, state.voice.loop)
try:
fut.result()
except:
# an error happened sending the message
pass
#commands.command(pass_context=True, no_pm=True)
async def womble(self, ctx):
state = self.get_voice_state(ctx.message.server)
opts = {
'default_search': 'auto',
'quiet': True,
}
if state.voice is None:
success = await ctx.invoke(self.summon)
if not success:
return
try:
random_clip = clip_dir + "\\" + random.choice(os.listdir(clip_dir))
player = state.voice.create_ffmpeg_player(random_clip, after=lambda: self.bot_leave(ctx))
player.start()
except Exception as e:
fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```'
await self.bot.send_message(ctx.message.channel, fmt.format(type(e).__name__, e))
I have tried going into the python chat is the Discord API server, but much like my bot, I'm met with crickets. (Guess that's what I get trying to seek support from a chat that already has 4 conversations going.)

I'm guessing you might not need help anymore but just in case you should try removing the coroutine.result() and run it directly. I.e change:
def bot_leave(self, ctx):
state = self.get_voice_state(ctx.message.server)
coro = state.voice.disconnect()
fut = asyncio.run_coroutine_threadsafe(coro, state.voice.loop)
try:
fut.result()
except:
# an error happened sending the message
pass
to:
def bot_leave(self, ctx):
state = self.get_voice_state(ctx.message.server)
coro = state.voice.disconnect()
try:
asyncio.run_coroutine_threadsafe(coro, state.voice.loop)
except:
# an error happened sending the message
pass
That's the only thing I could think of seeing your code snippet, but the problem may lie elsewhere in the code.

Related

How to run discord.py interactively in jupyter notebook?

In discord.py, you can initialize a Client, and run it with client.run(). But the function never returns.
What if I just want to just retrieve some message history then use it in jupyter notebook?
#bot.command()
async def history(ctx):
counter = 0
messageList=[]
async for message in ctx.channel.history(limit = 100):
print(message.author, message.content)
counter += 1
messageList.append(message)
await ctx.send(f'total **{counter}** messages in this channel.')
# How to return the messageList to my jupyter notebook cell and I start playing with the messageList?
How to return the messageList?
There isn't a good way to do this. discord.py is designed to be started once and run until your program terminates since all of the internal objects are destroyed upon closure of the bot, which makes starting the bot back up again nearly impossible. And it is not possible to "pause" discord.py and run your code then resumes discord.py afterwards, because Discord API communicates via web sockets, which relies on a constant stream of heartbeats and acks.
A possible workaround would be to:
Use a single event loop through out the code.
Use loop.run_until_complete() to start the bot's internal loop.
Create a new Client every time you need to run the bot.
Create an on_ready event handle that fetches the information.
Downside is that you won't be able to interact with the Message objects (ie. delete, edit, etc), they will be view only.
Example:
import asyncio
import discord
TOKEN = "YOURBOTTOKEN"
loop = asyncio.get_event_loop()
def get_message_list(token, channel_id):
client = discord.Client(loop=loop)
message_list = []
#client.event
async def on_ready():
nonlocal message_list
channel = client.get_channel(channel_id)
if not channel: # incase the channel cache isn't fully populated yet
channel = await client.fetch_channel(channel_id)
async for message in channel.history(limit=100):
message_list.append(message)
await client.close()
async def runner():
try:
await client.start(token)
finally:
if not client.is_closed():
# Incase the bot was terminated abruptly (ie. KeyboardInterrupt)
await client.close()
loop.run_until_complete(runner())
return message_list
message_list = get_message_list(TOKEN, channel_id=747699344911712345)
print("Message list for channel #1:", message_list)
# ... do stuff with message_list
message_list = get_message_list(TOKEN, channel_id=747699344911754321)
print("Message list for channel #2:", message_list)
# ... do more stuff with message_list
# Finally closes the event loop when everything's done
loop.close()
Instead of doing this, I'd recommend you to find another solution to the task you're trying to accomplish.

Sending message to all user (dm off failure)

The issue im having here is when the bot is cycling through the members and its sending the message, when a user has the dms off it just stops going through them.
I would like it so it keeps going through the members after hitting a member with dms off.
CODE:
#client.command()
async def pd(ctx):
try:
for m in client.get_all_members():
await m.send(ad)
print(f"received the ad")
except:
print(f" has [DM] `off`")
This is what it looks like after hitting a member with dms off.
TERMINAL:
bot is now online
received the ad
received the ad
received the ad
received the ad
has [DM] `off`
Thank you!
The reason your code is not working is because whenever you get an error, the loop is broken, and execution continues from the except: block.
You can fix this simply by putting the try: and except: inside the loop.
#client.command()
async def pd(ctx):
# Iterates over each member
for m in client.get_all_members():
# Try sending the message to the member
try:
await m.send(ad)
print(f"received the ad")
# If an error is produced, log it.
# After this, the loop will continue onto
# the next member.
except:
print(f" has [DM] `off`")
OK! So I figured out the issue I had. I needed to move
try:
Down a line to make it inside the loop
EXAMPLE:
#client.command()
async def pd(ctx):
for m in client.get_all_members():
try:
await m.send(ad)
print(f"received the ad")
except:
print(f" has [DM] `off`")
print("finished sending ads")

Exception mechanism doesn't work after some times

I've created a Discord bot in Python using the discord.py library. This is the pseudo-code of my bot :
import discord
client = discord.Client()
async def test_func():
return "This is a test"
async def error_func():
raise Exception
text_to_func = {"test": test_func, "error": error_func}
#client.event
async def on_message(message):
if message.content.startswith("$"):
command = message.content[1:]
try:
func = text_to_func[command]
answer = await func()
except Exception:
answer = "Error !"
await message.reply(answer)
client.run("<TOKEN>")
When a message is received by the bot, the on_message function is executed. It checks if the message starts with "$", which is the prefix of my bot, and execute the function associated with the command in a try/except block.
I run this script on a ubuntu server using an ssh connection.
Now if I send "$test" on my discord server, the bot will reply with "This is a test" and if I send "$error", it will reply with "Error !". The thing is, after a certain amount of time (can be from 6 hours to 2 days), the "$error" command won't work anymore. The bot doesn't reply to the command, while it still does for "$test".
Does someone know what's going on there ? Thank you for your help.
I'm not 100% sure what you're trying to accomplish, but if you're actually just trying to react to errors, I recommend just using:
#client.event
async def on_error(event, args, kwargs):
#what you want to happen in the event of an error
instead of putting everything you do in a try...except block.
Here are the official docs for the on_error event listener.

Discord.py || How to loop a part of Program infinite times without crashing it eventually?

I am Trying to Make a 24/7 Streaming Discord Bot using while(True): Infinite Loop,
But I think that the while(True): will get crashed by the OS (Using Heroku for Hosting) or Some kind of threads problem
#commands.command(name='play',aliases=["p"])
async def _play(self, ctx: commands.Context):
i = 0
while(True): #Infinite While Loop
search = URL[i] #URL is the List of Musics I What to Stream 24/7
if not ctx.voice_state.voice:
await ctx.invoke(self._join) #joins the voice channel if not in voice channel previously
async with ctx.typing():
try:
source = await YTDLSource.create_source(ctx, search, loop=self.bot.loop)
#Sets the Status of the Bot to What Music it is Playing Currently
await self.bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=f"{source.title}"))
except YTDLError as e:
await ctx.send('An error occurred while processing this request: {}'.format(str(e)))
else:
song = Song(source)
await ctx.voice_state.songs.put(song) #Enqueue the Song
i = (i + 1) % len(URL) # Updates the Index of URL
await asyncio.sleep(source.DURATION) #Gets the Lenght of the Music in int and Sleeps
I thought of using Asyncio Event Loops but Can't Figure it Out How to Use it Because my Function Has Parameters/Arguments like context (ctx)
I have Also thought of Cleaning the Memory stack after each songs play... But is it possible ??? If yes can we use it to fix my problem
So Is there anyway to Loop The above Mentioned Block or Code Infinitely without crashing it eventually
Thanks in advance !!!
I have a Out of Topic Query... Can we Use the ability of Context (ctx) without passing it as a Argument.
If we Can then I might be able to use asyncio_event_loop, I suppose

Ignoring exception in command None: discord.ext.commands.errors.CommandNotFound: Command "memechannel" is not found error (discord.py)

Alright, so I have made a Discord Bot that takes memes from reddit and sends it to a user specified channel every 5 minutes, to do this i had to make a cog
#imports
import discord
...
...
#Automemer
class Automemer(commands.Cog):
def __init__(self, client):
....
#used to loop the task of posting memes
#tasks.loop(minutes=5.0)
async def automemer(self):
all_subs = []
...
#start of formating embed for task loop
name = random_sub.title
url = random_sub.url
em = discord.Embed(title = name)
em.set_image(url = url)
em.color = 0xff0000
em.set_footer(text=f'Memes from r/AmongUsMemes')
#used to post the memes where the user does the command am.memechannel
#commands.command()
async def memechannel(self, ctx):
channel_id = ctx.channel.id
await ctx.send('Memes will now be sent in this channel')
memeChannel = ctx.guild.get_channel(channel_id)
if memeChannel:
emoji1 = ':arrow_up:'
emoji2 = ':arrow_down:'
msg = await ctx.send(embed=em)
await msg.add_reaction(emoji1)
await msg.add_reaction(emoji2)
await ctx.send(embed=em)
Error = Ignoring exception in command None: discord.ext.commands.errors.CommandNotFound: Command "memechannel" is not found
The error happens whenever I run the command am.memechannel.
It would be great if somebody told me how to fix this error.
This is the last feature of the bot, and it will be my first ever Discord Bot released to the public! :)
You defined the commands inside of the task loop, your indentation is messed up. You're supposed to put the command underneath the loop, not inside of it.
#tasks.loop(minutes=5.0)
...
#commands.command()
...
Keep in mind that your em variable is only visible within your task so you'll have to find a way to work around that, otherwise you can't use it in your command. An example is adding it as a class variable instead.
def __init__(self, client):
self.em = discord.Embed()
And then just overwriting that variable as you go along.
self.em = discord.Embed(title=name)
...

Categories