Is there any way of writing a command to interrupt time.sleep for a discord music bot? - python

I am trying to code a music bot to play music in a discord server. The issue I am having is with the "play" command I am using, when a song is playing from this version of the play command, no other commands (e.g. ".disconnect") work. The code I have written for this is
#bot.command()
async def play(ctx,search):
query_string = urllib.parse.urlencode({'search_query': search})
htm_content = urllib.request.urlopen('http://www.youtube.com/results?'+query_string)
search_results = re.findall(r'/watch\?v=(.{11})',htm_content.read().decode())
URL = ('http://www.youtube.com/watch?v='+search_results[0])
song_queue.append(URL)
if len(song_queue)>1:
await ctx.channel.send("Song queued.")
for index in range(len(song_queue)):
yt = YouTube(song_queue[index])
video_length = yt.length
await play_song(ctx, song_queue[index])
await ctx.channel.send(song_queue[index])
del song_queue[0]
time.sleep(video_length)
I am assuming that the issue comes from the time.sleep(video_length) pausing all of the code and not just pausing the for loop that it's in.

Related

My discord.py bot does not read any messages

I was making another discord.py bot in replit (yes, i know replit isn't optimal, but it's a small bot, and replit can easily do everything i need from it), and followed the exact steps I used when making another bot of mine that works flawlessly. Then, i tested it, and nothing happened. I checked the code, nothing there, then the developer options, everything was fine, and the bot permissions. The bot was on administrator. On top of this, the bot kept showing up as offline in discord, even though the project was running just fine. The token is okay, and I even asked chat GPT multiple times, and nothing worked.
This is my code:
import discord
import os
from keep_alive import keep_alive
from datetime import datetime
import requests, random
time = datetime.now().strftime('%H:%M:%S')
timeSplit = time.split(":")
hoursFiltered = int(timeSplit[0]) - 8
minutesFiltered = int(timeSplit[1])
cstHoursFiltered = hoursFiltered + 2
estHoursFiltered = hoursFiltered + 3
cstHours = int(timeSplit[0]) - 2
intents = discord.Intents.default()
intents.members = True
client = discord.Client(intents=intents)
wyvernID = 688807968120373319
reminding = 0
help = "LIST OF COMMANDS:\n%kat: gives you a random image of a cat\n%opendms: opens dms with the bot (it sends you a message)\n%kathelp: seriously?\n%pick: type this command followed with either 2 or 3 options seperated by spaces, and it will chose one. (ex: %pick kat cat) Make sure you do NOT end your message with a space.\n"
#client.event
async def on_ready():
print ('We have logged in as {0.user}'.format(client))
while True:
if int(cstHours) == 15 and int(minutesFiltered) == 30 and reminding == 1:
for i in range(3):
await client.get_user(wyvernID).send("Wake up pls :)")
#client.event
#client.event
async def on_message(message):
global reminding
if message.author == client.user:
return
if str(message.author) in ["Cheez#5224"]:
await message.channel.send("?")
if message.content.startswith("%kat"):
response = requests.get("https://api.thecatapi.com/v1/images/search")
cat = response.json()
catDict = dict(cat[0])
await message.channel.send(str(catDict['url']))
if message.content.startswith("%opendms"):
await message.author.send("Dms Are Open With Me")
if message.content.startswith("%kathelp"):
print(help)
await message.channel.send(help)
if message.content.startswith("%pick"):
pickrawlist = message.content.split(" ")
if len(pickrawlist) >= 3:
choice = random.randint(1, len(pickrawlist) - 1)
await message.channel.send(pickrawlist[choice])
else:
await message.channel.send("An Error Has Occured. Please make sure you have no less than 2 choices.")
if "kat" in message.content.lower():
await message.add_reaction("<:👍>")
try:
keep_alive()
client.run(os.getenv('TOKEN'))
except discord.errors.HTTPException:
print("\n\n\nBLOCKED BY RATE LIMITS\nRESTARTING NOW\n\n\n")
os.system("python restarter.py")
os.system('kill 1')
"keep_alive" and "restarter" work just fine, as I used the same code that worked in my other bot. To be clear, the project hosting is fine, but the bot appears offline in discord. I gave the bot every permission in discord developers, and there is NO ERROR. I know that the reading is the issue, becuase I made the project print a message when a message is sent, and nothing is printed. I honestly have no idea what's happening, and would like help. If anyone can help me, that would be much appreciated :)

Discord bot command from one bot to another

Hello guys I have simple discord bot on server and have a music bot. My simple bot just open txt file with links on music and send command in chat m!play link. But music bot doesn't see commands which are sent by my bot.
Music bot Jockie music
import discord
from random import randint
from conf import TOKEN
class DSClient(discord.Client):
async def on_ready(self):
print(self.guilds)
async def on_message(self, message):
if message.content == "Грязюка":
with open("songs.txt", "rt") as f:
tracks = [song.strip().split() for song in f.readlines()]
for _ in range(len(tracks)):
index = randint(0, len(tracks) - 1)
track = tracks.pop(index)
print(track)
await message.channel.send(f"m!play {track[0]}")
client = DSClient(intents=discord.Intents.default().all())
client.run(TOKEN)
my code
if the bot you want to give commands is not from you you can't fix this ... because they set it in their code so that is will only respond to user accounts.
If the Bot you want to give the command (music bot) is from you, you need to go to the code and make the command triggered by an on_message event, because the build in command handler from discord.py will only listen to user messages and ignore commands send by other bots
#bot.listen
async def on_message(message):
if message.content.startswith('m!play'):
await play_song(song_name=message.content.split(' ')[1])
the play_song function is here the async function that will play the song and I just passed here an example parameter.

Nextcord (discord.py fork) - How can I return to Discord that a command is currently running?

I have a Discord bot with a command that take some time to be completed (about 5 seconds).
When the command take too much time to complete without any responses to Discord servers, I've seen that Discord make the command crash, and delete the Context, so when I send a response, it raises an error to say that the Context does not exists.
MEE6 by example, when executing a command, Discord returns:
Sending command...
And when MEE6 receive the command, it says:
MEE6 is thinking...
But my bot does not.
How can I do like MEE6, return to Discord that my command is still running ?
Im using Nextcord (a discord.py fork) so discord.py anwsers will works too.
Minimal reproductible code:
import requests, nextcord
from nextcord.ext import commands
bot = commands.Bot(intents=nextcord.Intents.all()) # be sure to enable every discord intents in the discord dev portal
#bot.slash_command(name='axolotl')
async def axolotl(ctx): # do things that take a lot of time
r = requests.get('https://axolotlapi-test.kirondevcoder.repl.co/reddit?nsfw=0', timeout=2)
if r.status_code == 200:
data = r.json()['data'][0]
embed = nextcord.Embed(title='Axolotl !', description=data['text'] or 'No text content.')
medias = ""
for media in data['media']:
embed.set_image(media)
await ctx.send('', embed=embed)
else:
await ctx.send('', embed=nextcord.Embed(title='Axolotl !', description='There was an error while contacting the API.'))
bot.run('your token here')
I know that the ctx object is not a nextcord.Context object but a nextcord.interaction.Interaction object and I think the API docs of this class can be found at https://docs.nextcord.dev/en/stable/api.html#nextcord.Interaction but im not sure.
I allready tried to make the bot typing on the channel, but it does not change anything:
#bot.slash_command(name='axolotl')
async def axolotl(ctx):
async with ctx.channel.typing():
r = requests.get('https://axolotlapi-test.kirondevcoder.repl.co/reddit?nsfw=0', timeout=2)
if r.status_code == 200:
data = r.json()['data'][0]
embed = nextcord.Embed(title='Axolotl !', description=data['text'] or 'No text content.')
medias = ""
for media in data['media']:
embed.set_image(media)
await ctx.send('', embed=embed)
else:
await ctx.send('', embed=nextcord.Embed(title='Axolotl !', description='There was an error while contacting the API.'))
(I only provided the command code, else its the same code as the minimal reproductible code)
Can someone help please ?
If your commands take a while to execute then you can defer() to tell discord that your commands are going to take a while (and get the "Bot is thinking..." message).
So add await ctx.response.defer() at the start of your command. You can then use the followup attribute to send a message and "respond" to the slash command.
#bot.slash_command(name='axolotl')
async def axolotl(ctx):
await ctx.response.defer()
async with ctx.channel.typing():
r = requests.get('https://axolotlapi-test.kirondevcoder.repl.co/reddit?nsfw=0', timeout=2)
if r.status_code == 200:
data = r.json()['data'][0]
embed = nextcord.Embed(title='Axolotl !', description=data['text'] or 'No text content.')
medias = ""
for media in data['media']:
embed.set_image(media)
await ctx.followup.send('', embed=embed)
else:
await ctx.followup.send('', embed=nextcord.Embed(title='Axolotl !', description='There was an error while contacting the API.'))

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.

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

Categories