Discord Py Sending Live Python Output - discord

I'm currently working on a text-to-image generation bot and I'd like it to send live messages with information about the time remaining in the generation, iteration, and speed at which it's generating, all can be found in the console but I have no idea how to send console output to discord.

I'm assuming you are using print("output") to print into the console.
You can send the message to Discord via Messageable#send()
For example sending to a channel would be
await channel.send(content="content")
You can get the Messageable in a lot of ways
# In a cog
channel = ctx.channel
# In a cog you can directly use ctx.send() however
# In a on_message event
channel = message.channel
Now simply, send whatever you are printing to the console to Discord
# Printing to console
print("hello world")
# Sending to message
await channel.send(content="hello world")
That approach can be a bit "spammy", so a better practice is to edit the message instead
# channel.send() returns the message
# Printing
print("status update")
# Sending message
message = await channel.send(content="status update")
# [Image generation code]
# Printing
print("new status update")
# Editing message
await message.edit(content="new status update")

Related

Can't get my Discord bot to connect to voice channel

I'm messing around with a bot for the first time but can't seem to get it to join a voice channel. I know the $join command is working because my bot will say "Connecting to {channel}" with the correct channel name, but it still never connects. If I'm not in a voice channel, the bot will correctly say "Error".
I've tried every single solution I can find online. Every time the bot will identify the correct channel but it simply won't join the channel. This feels like such a simple problem so I'm not sure how I can use similar code as others and still not have it work. Below is the code I'm using for the command. I left out all the prefix and bot setup code because all the messages I send work fine.
#bot.command()
async def join(ctx):
if (ctx.author.voice):
channel = ctx.message.author.voice.channel
await ctx.send(f"Connecting to {channel}")
await channel.connect()
else: await ctx.send("Error")
asyncio.TimeoutError – Could not connect to the voice channel in time.
discord.ClientException – You are already connected to a voice channel.
discord.OpusNotLoaded – The opus library has not been loaded.
These are the following errors that the bot can raise for VoiceChannel.connect()
If you are not receiving errors when you should, this points towards a poorly made global command handler on_command_error.
You either want to remove that for the time being to get the actual error on the console, or fix it by output the error in a case where the error isn't handled. A common way of doing that is:
import sys
import traceback
#bot.event
async def on_command_error(ctx, error):
if x: # let x, y, z be isinstance check for error
# do this
elif y:
# do that
elif z:
# idk do something other than this and that
else:
print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
After getting your actual error for joining the VC, check through these:
asyncio.TimeoutError: This error is raised when your bot has a
weak internet connection or your ping is absurdly high, high enough
to not make a connection.
discord.ClientException: Might want to check if bot is not in VC when it starts up, and also that you have relevant guild intents and voice intents enabled.
discord.OpusNotLoaded: Your device does not have libopus loaded, you might want to search for that online depending on what you use. If you are on an ubuntu-based linux distro, you can use sudo apt install libopus0 and then try to see if it fixed your issue. I am not sure about windows
If you can't figure it out after you read the above stuff, let me know the EXACT ERROR you are facing, which includes the traceback message (the whole error text that will print onto your console)

How to make a slash command bot do not reply messages

#slash.slash(name='spam', description='I will spam your content for times!', options=optionsspam, guild_ids=[847769978308526090])
async def spam(ctx, text: str, times: int="15"):
if bool(times):
Times = 15
else:
Times = times
for i in range(int(Times)):
await ctx.send(text)
await asyncio.sleep(.7)
And the result is:
It keeps replying to the first message that the bot sent. I don’t want the bot to reply. I want it to just send a normal message. How?
An interaction (slash-command) will always require a direct response towards the user. If you do not use ctx.send(str), the interaction will fail.
You've got 2 options to make it seem, like you are not responding to the slash command
Hide the response
You can post a hidden answer ctx.send('ok', hidden=True) and then send the intented message into the channel ctx.channel.send(str).
This will make the initial 'ok' only visible for the invoking users and all other members of the server will neither see the request, nor the first response.
Delete the response
Your second option is to automatically delete the answer after a very short period (ctx.send('ok', delete_after=1)), followed by a normal message into the channel ctx.channel.send(str).
Defering the response
You might need to defer your response if you can't respond within 3 seconds of the invocation. Defering an interaction (ctx.defer(hidden=True) or ctx.defer()) must be called with the same hidden attribute as your future ctx.send().
If you want to hide your respons ctx.send('ok', hidden=True), you need to defer in the same state ctx.defer(hidden=True).
You could get the channel and send message to the channel directly. However, you then must use something like ctx.defer() so that the interaction doesn't get displayed as failed.
#slash.slash(name='spam', description='I will spam your content for times!', options=optionsspam, guild_ids=[847769978308526090])
async def spam(ctx, text: str, times: int="15"):
channel = ctx.channel
if bool(times):
Times = 15
else:
Times = times
for i in range(int(Times)):
if channel != None:
await channel.send(text)
await asyncio.sleep(.7)
await ctx.send("Done")

How to prevent bot from spamming embeds? Discord.py

I have discord bot and it checks whether streamer is live or not. And I have a function that prevents it from spamming when someone is live:
if status is True:
async for message in channel.history(limit=2000):
if message.content == f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`":
break
else:
async for member in guild.fetch_members(limit=None):
if member.id == int(user_id):
await member.add_roles(role)
await channel.send(f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`")
print(f"{user} started streaming. Sending a notification.")
break
And I wonder if there is the way to do same with embeds, but idk what to use. I though I can use if embed = twitch_embed: break instead of if message.content.
I want to send both message and embed when someone is live one time per stream each like mee6 does:
And I want to combine message and embed antispam function in the code. Please help! All my code here if you need:
import os
import json
import discord
import requests
from discord.ext import tasks, commands
from server import ping
from twitchAPI.twitch import Twitch
from discord.utils import get
intents = discord.Intents.all()
bot = commands.Bot(command_prefix='$', intents=intents)
# Authentication with Twitch API.
client_id = os.getenv('client_id')
client_secret = os.getenv('Dweller_token')
twitch = Twitch(client_id, client_secret)
twitch.authenticate_app([])
TWITCH_STREAM_API_ENDPOINT_V5 = "https://api.twitch.tv/kraken/streams/{}"
API_HEADERS = {
'Client-ID': client_id,
'Accept': 'application/vnd.twitchtv.v5+json',
}
user_info = twitch.get_users(logins=['turb4ik'])
user_id = user_info['data'][0]['id']
print(user_info)
# Returns true if online, false if not.
def checkuser(user):
try:
userid = twitch.get_users(logins=[user])['data'][0]['id']
url = TWITCH_STREAM_API_ENDPOINT_V5.format(userid)
try:
req = requests.Session().get(url, headers=API_HEADERS)
jsondata = req.json()
if 'stream' in jsondata:
if jsondata['stream'] is not None:
return True
else:
return False
except Exception as e:
print("Error checking user: ", e)
return False
except IndexError:
return False
# Executes when bot is started
#bot.event
async def on_ready():
# Defines a loop that will run every 10 seconds (checks for live users every 10 seconds).
#tasks.loop(seconds=10)
async def live_notifs_loop():
# Opens and reads the json file
with open('streamers.json', 'r') as file:
streamers = json.loads(file.read())
# Makes sure the json isn't empty before continuing.
if streamers is not None:
# Gets the guild, 'twitch streams' channel, and streaming role.
guild = bot.get_guild(690995360411156531)
channel = bot.get_channel(785523710362124298)
role = get(guild.roles, id=835581408272580649)
# Loops through the json and gets the key,value which in this case is the user_id and twitch_name of
# every item in the json.
for user_id, twitch_name in streamers.items():
print("checking" + " " + str(twitch_name))
# Takes the given twitch_name and checks it using the checkuser function to see if they're live.
# Returns either true or false.
status = checkuser(twitch_name)
# Gets the user using the collected user_id in the json
user = bot.get_user(int(user_id))
# Makes sure they're live
if status is True:
# Checks to see if the live message has already been sent.
#limit = 0
#limit += 1
async for message in channel.history(limit=2000):
# If it has, break the loop (do nothing).
if message.content == f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`":
break
# If it hasn't, assign them the streaming role and send the message.
else:
# Gets all the members in your guild.
async for member in guild.fetch_members(limit=None):
# If one of the id's of the members in your guild matches the one from the json and
# they're live, give them the streaming role.
if member.id == int(user_id):
await member.add_roles(role)
# Sends the live notification to the 'twitch streams' channel then breaks the loop.
'''twitch_embed = discord.Embed(
title=f":red_circle: **LIVE**\n{user.name} is now streaming on Twitch!",
color=0xac1efb
)'''
#twitch_embed.add_field(name='**Game**', value='add value',inline=True)
#twitch_embed.add_field(name='**Viewers**', value='add value',inline=True)
#twitch_embed.set_image(url=f'\nhttps://www.twitch.tv/{twitch_name}')
await channel.send(f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`")
#await channel.send(embed=twitch_embed)
print(f"{user} started streaming. Sending a notification.")
break
# If they aren't live do this:
else:
# Gets all the members in your guild.
async for member in guild.fetch_members(limit=None):
# If one of the id's of the members in your guild matches the one from the json and they're not
# live, remove the streaming role.
if member.id == int(user_id):
await member.remove_roles(role)
# Checks to see if the live notification was sent.
async for message in channel.history(limit=200):
# If it was, delete it.
if str(user.mention) in message.content and "is now streaming" in message.content:
await message.delete()
# Start your loop.
live_notifs_loop.start()
# Command to add Twitch usernames to the json.
#bot.command(name='addtwitch', help='Adds your Twitch to the live notifs.', pass_context=True)
#commands.has_permissions(administrator = True)
async def addtwitch(ctx, twitch_name):
# Opens and reads the json file.
with open('streamers.json', 'r') as file:
streamers = json.loads(file.read())
# Gets the users id that called the command.
user_id = ctx.author.id
# Assigns their given twitch_name to their discord id and adds it to the streamers.json.
streamers[user_id] = twitch_name
# Adds the changes we made to the json file.
with open('streamers.json', 'w') as file:
file.write(json.dumps(streamers))
# Tells the user it worked.
await ctx.send(f"Added {twitch_name} for {ctx.author} to the notifications list.")
ping()
bot.run(os.getenv('token'))
Edit:
Please say if something is not right or you don't understand the question instead of just closing it.
To send the embed in the same message you can use content an example would be:
await channel.send(content=f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`", embed=twitch_embed)
There would be no change in your check function as the message.content would be the same text message just with an embed as well.
You should really check if the stream has finished either by web scraping or some API instead of checking message content. If the stream finishes 3 hours ago but you only check for 2 streamers there won't be a change unless you have thought of this already.

Saving message content to a list discord.py

I'm trying to create a playlist which the bot can send but I can't figure it out, here's the code I've tried although it only takes the text to trigger the command and I can't access the list anywhere.
Code:
#client.event
async def on_message(message):
if message.content == "=playlist":
playlist = message.content
await message.channel.send(playlist)
Any ideas?
I don't understand what the problem is, by the looks of it, when a users message is =playlist, your code will send all the contents of that message essentially back to them. Is that not what you want?

How can I delete a message in discord.py after a command was executed by an user?

I am trying to program a function that deletes the message with the command sent by the user.
E.g.:
User sends command /delmes
Bot sends a response to the command
Bot deletes the message sent by the user
Everything I was able to find until now (that would help me) was this Stack Overflow Thread: discord.py delete author message after executing command
But when I used the code, as described in the solutions, I only received AttributeError: 'Bot' object has no attribute 'delete_message'.
The discord.py API Reference (https://discordpy.readthedocs.io/en/latest/migrating.html#models-are-stateful / https://discordpy.readthedocs.io/en/latest/api.html?highlight=delete%20message#message) only revealed that some of the code had changed with newer versions.
So to speak client.delete_message would change to Message.delete(), if I interpreted it correctly!
After changing the code to what I thought it must be I received: NameError: name 'Message' is not defined
This is my code at the very last moment. (I am relatively new to discord.py and only used Python in school)
import discord
from discord.ext import commands
import random
import json
client = commands.Bot(command_prefix = '/', case_insensitive=True)
#client.command(pass_context=True)
async def delcommes(ctx):
await ctx.send("This is the response to the users command!")
await Message.delete(ctx.message, delay=3)
I couldn't understand your question very good but as far as I understand, when command executed, then after bot sends message, you want to delete the command message, in that case it's /delmes. You can use await ctx.message.delete().
#client.command(pass_context=True)
async def delcommes(ctx):
await ctx.send("This is the response to the users command!")
await ctx.message.delete()

Resources