I have this function here that uses the folder name as a command. Right now the only folders are "hug" and "headpat". So when I send .hug or .headpat to Discord it will go into that folder, grab a random image and post it.
#client.event
async def on_message(message):
async def random_image(dir): #picks a random image from local directory
if message.content.lower() ==f".{dir}":
image = random.choice(os.listdir(dir))
embed = discord.Embed(color = 0xff9e00)
chosen_one = discord.File(f"{dir}\\{image}", filename = image)
embed.set_image(url = f"attachment://{image}")
await message.channel.send (embed = embed, file = chosen_one)
print(f"{image} sent in response to {(message.author)}.")
print(f"----------")
However, I would like to be able to do something like as follows, but I am not sure where to start.
#bot.command()
async def folder_name(ctx):
#code that does the same as the function above
By default with the commands extension, the function's name is the command name. However, the #commands.command decorator have a aliases argument that you can use along with Context.invoked_with:
#bot.command(aliases=['hug', 'headpat'])
async def folder_name(ctx):
image = random.choice(os.listdir(ctx.invoked_with))
embed = discord.Embed(color = 0xff9e00)
chosen_one = discord.File(f"{dir}\\{image}", filename=image)
embed.set_image(url = f"attachment://{image}")
await ctx.send (embed = embed, file=chosen_one)
print(f"{image} sent in response to {ctx.author}.")
print(f"----------")
Note that if someone types !folder_name, the command will execute so you'll have to add a check like this:
if ctx.invoked_with == ctx.command.name:
#Do stuff
Here is a simple bot that implements your code as a command.
import discord
from discord.ext import commands
TOKEN = "your token"
PREFIXES = ['.']
bot = commands.Bot(command_prefix=PREFIXES)
#bot.command(name='image')
async def random_image(ctx, dir):
image = random.choice(os.listdir(dir))
embed = discord.Embed(color = 0xff9e00)
chosen_one = discord.File(f"{dir}\\{image}", filename = image)
embed.set_image(url = f"attachment://{image}")
await ctx.send (embed = embed, file = chosen_one)
print(f"{image} sent in response to {(message.author)}.")
print(f"----------")
#bot.event
async def on_ready():
print(f"Logged in as {bot.user.name} in {len(bot.guilds)} guilds!")
#bot.event
async def on_message(message):
if message.author.bot: # I always do this to make sure it isn't a bot
return
await bot.process_commands(message)
if __name__ == '__main__':
try:
bot.run(TOKEN)
finally:
print("Logging Out")
I'd actually recommend using links instead of files/folders for their unreliable and often produce problems and bugs in your code and they just don't work very well with discord.py.
Here's an example of your desired command.
import discord
from discord.ext import commands
from discord import Embed
import random
client = commands.Bot(command_prefix=".")
headpat_image_links = ['https://media.tenor.com/images/ad8357e58d35c1d63b570ab7e587f212/tenor.gif', 'https://image.myanimelist.net/ui/xUjg9eFRCjwANWb4t4P8QbyfbVhnxa2QGYxoE7Brq1sHDVak6YxhLP3ZbNkkHm6GX9x8FWB1tWCapNyEqSltXa8wxAdwIERdrHGhYPilHJ8']
secret_headpat = random.choice(headpat_image_links)
#client.command()
async def headpat(ctx):
e=Embed(title="Headpats!", description="whatever description you want")
e.set_image(url=secret_headpat)
await ctx.send(embed=e, content=None)
client.run('TOKEN')
I've checked it and it works like a charm ;)
It's the same concept for the hug command. Just choose a random link to embed :)
PS:
Make sure you don't use any imgur links, imgur's API and links has been on the fritz and no longer works with discord.py. Well, it no longer works with embedded messages. Just don't use it. :>
Related
I have had a lot of trouble getting slash commands to work in python 2.0. I found the code below and managed to make it work for me. However, I have no clue how to get it working in cogs. I even found this that uses a similar system.my load_extensions command also doesn't even work via the system below.
import discord
from discord import app_commands
import os, asyncio
class aclient(discord.Client):
def __init__(self):
super().__init__(intents = discord.Intents.default())
self.synced = False #we use this so the bot doesn't sync commands more than once
async def on_ready(self):
await self.wait_until_ready()
if not self.synced: #check if slash commands have been synced
await tree.sync() #guild specific: leave blank if global (global registration can take 1-24 hours)
self.synced = True
print(f"have logged in as {self.user}.")
client = aclient()
tree = app_commands.CommandTree(client)
#tree.command(name = 'hello', description='say hi!')
async def hello(interaction: discord.Interaction):
await interaction.response.send_message(f"Hi there!", ephemeral = True)
client.run("token")
async def load_extensions():
for filename in os.listdir('./cogs'):
if filename.endswith('.py'):
await client.load_extension(f'cogs.{filename[:-3]}')
https://gist.github.com/AbstractUmbra/a9c188797ae194e592efe05fa129c57f
The following gist details how to use slash commands even in cogs and is recommended in the official discord.py server.
I have tried making a bot in discord.py and i cant figure out how to use discord.ext.
The part that doesn't work is this:
bot = commands.Bot(command_prefix='.')
#bot.command()
async def test(ctx, arg):
await ctx.channel.send(arg)
When I type .test whatever in discord nothing happens
Is it because of the api update?
If so what do I need to change
This is the entire code:
import os
import discord
import asyncio
from dotenv import load_dotenv
from discord.ext import commands
from discord.ext import bot
bot = commands.Bot(command_prefix='$')
#bot.command()
async def test(ctx, arg):
await ctx.channel.send(arg)
load_dotenv()
token = os.getenv("TOKEN")
client = discord.Client()
class MyClient(discord.Client):
async def on_ready(self):
print('Logged on as {0}!'.format(self.user))
async def on_message(self, message):
f = open("muteMsg.txt", "r")
muteMsg = f.read()
f.close()
print('Message from {0.author}: {0.content}'.format(message))
id = message.author.id
if id == 0:
await message.delete()
if muteMsg == "1":
await message.channel.send(f"stfu {message.author.mention}")
elif message.content.startswith('good bot') or message.content.startswith('Good bot'):
await message.channel.send(':)')
elif message.content.startswith('bad bot') or message.content.startswith('Bad bot'):
await message.channel.send(':(')
elif message.content.startswith('.rickroll'):
await message.channel.send('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
elif message.content.startswith('.help'):
await message.channel.send('commands list:\n.help: shows this\n.rickroll: well youll have to try yourself\n')
elif message.content.startswith('.togglemutemsgs'):
if muteMsg == "0":
f = open("muteMsg.txt", "w")
f.write("1")
f.close()
await message.channel.send('mute messages on')
elif muteMsg == "1":
f = open("muteMsg.txt", "w")
f.write("0")
f.close()
await message.channel.send('mute messages off')
client = MyClient()
client.run(token)
The core issue here is that while you do instantiate discord.ext.commands.Bot on line 9 you never actually use it, instead calling .run() on your subclass of discord.Client which does not interact with your defined method in any way.
The simplest way of getting your command to work is to change your last line from client.run(token) to
bot.run(token)
but your handlers of on_ready and on_message events will not work anymore - you should learn more to figure out why and how to solve it.
As a side note: you really shouldn't be putting any complex logic in on_message handler and you definitely should not be opening files there.
Well, If you want to take input from user ,you need to use async def test(ctx, *, arg), Try using the code below:
#bot.command()
async def test(ctx, *,arg):
await ctx.channel.send(arg)
And also beware of the command_prefix properly. Thank you :)
To use commands extension, you need to define a bot via commands.Bot because discord.Client doesn't have any infrastructure to make commands.
So you should change your inheritance in class MyClient, it should directly inherit from the commands.Bot. And you should pass the args/kwargs to your MyClient class, then you should run that classes instance rather than discord.Client.
I am making a Discord bot with Python.
I would like to add a feature in where, there is a channel called "chatbot", and any text typed there would be replied to by Cleverbot
I am currently trying with the cleverbotfree library, but it doesn't work and I can't find good documentation on it. My problem is that I am trying to run this async command:
#CleverbotAsync.connect
async def async_chat(bot, message, user_input, bot_prompt):
"""Example code using cleverbotfree async API with decorator."""
reply = await bot.single_exchange(user_input)
await message.channel.send(f'{0}'.format(reply))
await bot.close()
However, I can't do this in on_message, because it needs to be run in this snippet of code:
if chnl == 'chatbot':
#whatever running that command
So, I'm not sure how do it.
And yes, I have looked into and tried Selenuim, but it appears that whenever I click the accept button, nothing loads. (This must be a way for Cleverbot to make people actually pay for their API, which I can't do).
Here is my full code(I know it's not the best):
import discord
from discord import channel
from discord.flags import MessageFlags
from webserver import keep_alive
import os
from discord.ext import commands
import youtube_dl
import time
import asyncio
from cleverbotfree import CleverbotAsync
from cleverbotfree import Cleverbot
requests = []
def send(message,user_input,bot_prompt):
asyncio.run(async_chat(message,user_input,'Cleverbot:'))
client = commands.Bot(command_prefix="!")
#client.command()
async def request(ctx, request: str,member: discord.Member):
requests.append('{0}: {1}'.format(member,request))
await ctx.send(f'Thanks {member.metion} for the request, my dev will (hopefully) get right to work!')
#client.command(description="See what my dev is working on")
async def workingon(ctx):
ctx.send("My Dev is currently working on adding a feature in where, in a channel called 'chatbot', it will respond like a human.(though he has spent hours on it, it still doesn't work)")
#client.event
async def on_ready():
#confirming login
print('Logged on as {0.user}'.format(client))
#client.event
async def on_member_join(member):
print(f'Greetings {member}, welcome to server')
#CleverbotAsync.connect
async def async_chat(bot, message, user_input, bot_prompt):
"""Example code using cleverbotfree async api with decorator."""
reply = await bot.single_exchange(user_input)
await message.channel.send(f'{0}'.format(reply))
await bot.close()
#client.event
async def on_message(message):
#getting variables
usrnm = str(message.author).split('#')[0]
msg = str(message.content)
chnl = str(message.channel.name)
if usrnm != str(client.user).split('#')[0] and msg.lower() == 'hello bot':
await message.channel.send(f'Hello {usrnm}, how do you do?')
return
if str(msg).split(' ')[0].lower() == 'im':
print(str(msg).split(' ')[0].lower())
await message.channel.send(f'Hi, {msg}, im a dad!')
if msg.lower() == '!newfeatures':
await message.channel.send(f'{message.author.mention}, The newest feature added to the bot is that if a new member joins, it will greet them')
if msg.lower() == '!help':
await message.channel.send(f'{message.author.mention}, Type !help for help. \n\n - Typing "Hello Bot" will make the bot answer\n - typing "im" before anything will make the bot answer\n - Typing !newfeatures will tell you the newest feature added to the bot.')
if chnl == 'chatbot':
send(message,str(message.content),'Cleverbot:')
#muting system
keep_alive()
TOKEN = os.environ.get("DISCORD_BOT_SECRET")
client.run(TOKEN)
Any help would be appreciated!
I am trying to replace the avatar image of the bot when a user runs a command. How would I do this? I have managed to replace the nickname but not the image.
await ctx.guild.me.edit(nick=nick)
^ Replaces the nickname.
I tried (nick=nick, avatar=avatar) but it did not work.
EDIT:
#client.command()
async def test(ctx):
if ctx.guild.id == second_server:
await ctx.guild.me.edit(nick=nick, avatar_url=avatar)
pfp_path = "Profile-Picture.png"
with open(pfp_path, "rb") as pfp:
await client.user.edit(password=None, avatar=pfp.read())
print("profile picture changed")
According to the official docs, the following should work:
import discord
client = discord.Client()
token = '...'
#client.event
async def on_ready():
pfp_path = "file.jpg"
with open(pfp_path, "rb") as pfp:
await client.user.edit(password=None, avatar=pfp.read())
print("profile picture changed")
client.run(token)
You cannot directly give a URL to the desired profile picture.
Note: the only formats supported are JPEG and PNG
I am trying to create a python script that would mute the users of a specific voice channel when executed. Till now I am able to mute everyone by typing the command in the discord text channel, but I would like to mute everyone when another python script tells the bot to. Below is my attempt to do that.
bot.py is the python discord bot:
import discord
from discord.ext import commands
from time import sleep
client = commands.Bot(command_prefix="./")
#client.event
async def on_ready():
print("Bot is online. ")
#client.command()
async def play_music_test():
channel = await client.get_channel(voice_channel_number)
voice = await channel.connect()
await voice.play(
discord.FFmpegPCMAudio(executable="C:/Users/USER/ffmpeg/bin/ffmpeg.exe",
source="C:/Users/USER/Desktop/song.mp3"))
client.run(TOKEN)
abc.py is the other python script trying to call a function:
from bot import play_music_test
import asyncio
print("")
asyncio.run(play_music_test())
I ran the bot.py first and then tried to execute the abc.py next, bot came online but when executing abc.py, it didn't print or do anything at all. I just started to learn discord.py so if this is a silly question, forgive me.
Instead of running the python file you can import it, or making it a cog
heres an example:
importing: bot.py
import discord
from discord.ext import commands
from time import sleep
from abc import *
client = commands.Bot(command_prefix="./")
#client.event
async def on_ready():
print("Bot is online. ")
#client.command(name = "play_music", aliases = ["you can asign aliases like this", "multiple ones too"])
async def play_music_test(ctx, channel): # If the python file doesn't know what channel is, it can be asigned when calling the function in discord, think of it like sys.argv or argparse
if channel == None: channel = ctx.channel # This sets the current channel you are in if none was provided when executing it in discord
# channel = await client.get_channel(channel) now you don't need this line
voice = await channel.connect()
await voice.play(
discord.FFmpegPCMAudio(executable="C:/Users/USER/ffmpeg/bin/ffmpeg.exe",
source="C:/Users/USER/Desktop/song.mp3"))
client.run(TOKEN)
importing: abc.py
remove asyncio.run(play_music_test())
use it in discord instead! ex. play_music_test #general
Making it a cog: bot.py
import discord
from discord.ext import commands
from time import sleep
import os
client = commands.Bot(command_prefix="./")
#client.event
async def on_ready():
print("Bot is online. ")
for filename in os.listdir("./"):
if filename == "bot.py": continue
else: client.load_extension(f"cogs.{filename[:-3]}")
client.run(TOKEN)
Making it a cog: abc.py
from bot import play_music_test
import asyncio
class Mycog(commands.Cog):
def __init__(self, client):
self.client = client
#client.command(name = "play_music", aliases = ["you can asign aliases like this",
"multiple ones too"])
async def play_music_test(ctx, channel): # If the python file doesn't know what
channel is, it can be asigned when calling the function in discord, think of it like sys.argv or argparse
if channel == None: channel = ctx.channel # This sets the current channel you are in if none was provided when executing it in discord
# channel = await client.get_channel(channel) now you don't need this line
voice = await channel.connect()
await voice.play(
discord.FFmpegPCMAudio(executable="C:/Users/USER/ffmpeg/bin/ffmpeg.exe",
source="C:/Users/USER/Desktop/song.mp3"))
def setup(client):
client.add_cog(Mycog(client))
This anwser is not the best but hope this helps!
i'm not sure, if i really understand, what you try to do, but to open a script from another script, i use subprocess.Popen()
but i think, in your case it fits more, to change the way you try to solve your problem. maybe you should take a look into the on_message event and use message.content
?
You can importing your client variable (from bot import play_music_test, client) and then client.loop.create_task(play_music_test())
(Although I do not understand why you would do such a thing)