how do I get the name of a channel so that this bot will work on any server its put on with no changes to code necessary? ( in the code where I put "what do I put here" is where I want the name to be in a variable)Thanks
from discord.ext.commands import Bot
import time, asyncio
TOKEN = 'Its a secret'
BOT_PREFIX = ["!"]
client = Bot(command_prefix=BOT_PREFIX)
#client.event
async def on_message(message):
if message.author == client.user:
return
#client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
await start()
while True:
currentTime = time.strftime("%M%S", time.gmtime(time.time()))
if currentTime == "30:00":
await start()
await asyncio.sleep(1)
async def start():
mainChannel = #What do i put here?
print(mainChannel.name)
await client.send_message(mainChannel, "Starting countdown", tts = True)
client.run(TOKEN)
Getting channel from ID (Recommended)
First, get the ID of the channel
(Right click the channel and select "Copy ID")
Second, put the ID in the following code:
client.get_channel("ID")
For example:
client.get_channel("182583972662")
Note: The channel ID is a string in discord.py async, and an integer in rewrite
(Thanks to Ari24 for pointing this out)
Getting channel from name (Not reccomended)
First, get the server using either:
server = client.get_server("ID")
OR
for server in client.servers:
if server.name == "Server name":
break
Second, get the channel:
for channel in server.channels:
if channel.name == "Channel name":
break
What not to do
Try to always use the ID for each server, as it is much faster and more efficient.
Try to avoid using discord.utils.get, such as:
discord.utils.get(guild.text_channels, name="Channel name")
Although it does work, it is bad practise as it has to iterate through the entire list of channels. This can be slow and take much more time than using the ID.
From the discord API docs:
discord.utils.get is a helper that returns the first element in the
iterable that meets all the traits passed in attrs
Now in rewrite there's a method called discord.utils.get where you can actually getting discord objects with specific parameters
In your case with a channel name:
import discord
channel = discord.utils.get(guild.text_channels, name="Name of channel")
Should be None if discord couldn't find a textchannel with that name
It is actually really easy:
You can simply do message.channel.name
Example:
print(message.channel.name)
Well, it's simple to do. However your code can be improved by several way. It will be easier to read your code.
to get the channel and send a message to it, use this
ch = client.get_channel(channel id)
await ch.send("message")
few optimizations to your code
from discord.ext import commands
import discord
import time
TOKEN = "token"
client = commands.Client(command_prefix="!")
#client.event
async def on_ready():
print(f"Logged in as {client.user}")
print(f"ID: {client.id}")
Related
it's my first time coding something and making a discord bot. But I've been trying to figure it out what is wrong with this code for a long time. I want one channel to only able to type specific words and delete everything else, but in this code no matter what i do it deletes everything, cheers
import os
import discord
client = discord.Client(intents=discord.Intents.default())
intents = discord.Intents(message_content=True)
#client.event
async def on_ready():
print('BOTCADO ONLINE')
#client.event
async def on_message(message):
if (message.channel.id == 'id'):
if (message.content != "cado"):
await message.delete()
client.run(os.getenv('TOKEN'))
i tried basically everything
to get it to work I changed:
client = discord.Client(intents=discord.Intents.default())
intents = discord.Intents(message_content=True)
to:
intents = discord.Intents().all()
client = commands.Bot(command_prefix="$", intents=intents)
I would only use the intents you need to use however. I only put all for testing proposes.
Also in this line of code, if (message.channel.id == 'id'):, message.channel.id is being set equal to a string. Message ID's are integers. You have to remove that conditional / replace it. You could set the message ID that corresponds with the channel you want to delete messages in by declaring the id as a variable first at the top of your code. id = < THE CHANNEL ID >
Outcome
To snipe messages sent in X channel instead of all the channels within the Discord guild. That is, it should only track message deletions in that one channel (identified by its ID), and only respond to the !snipe command in that same channel. The current code I have here snipes every message sent within the Discord guild.
Question
How can I snipe messages sent in X channel instead of the entire guild?
I mostly intend to run this bot in one guild. However, it would be nice if it could scale to multiple guilds if needed.
The code I have so far is below.
import discord
from discord.ext import commands
from tokens import token
client = commands.Bot(command_prefix="!", self_bot=False)
client.sniped_messages = {}
#client.event
async def on_ready():
print("Your bot is ready.")
#client.event
async def on_message_delete(message):
print(f'sniped message {message}')
client.sniped_messages[message.guild.id] = (
message.content, message.author, message.channel.name, message.created_at)
#client.command()
async def snipe(ctx):
try:
contents, author, channel_name, time = client.sniped_messages[ctx.guild.id]
except:
await ctx.channel.send("Couldn't find a message to snipe!")
return
embed = discord.Embed(description=contents,
color=discord.Color.purple(), timestamp=time)
embed.set_author(
name=f"{author.name}#{author.discriminator}", icon_url=author.avatar_url)
embed.set_footer(text=f"Deleted in : #{channel_name}")
await ctx.channel.send(embed=embed)
client.run(token, bot=True)
I'm going to suggest two slightly different solutions, because the code can be simpler if you're only running this bot on one guild. What's common to both is that you need to check in what channel messages are deleted, and in what channel the !snipe command is sent.
Single-Guild Version
If you're only monitoring/sniping one channel on one guild, then you can only ever have one deleted message to keep track of. Thus, you don't need a dictionary like in your posted code; you can just keep a single message or None.
You're already importing your token from a separate file, so you might as well put the channel ID (which is an int, unlike the bot token) there too for convenience. Note that, by convention, constants (variables you don't intend to change) are usually named in all caps in Python. tokens.py would look something like this:
TOKEN = 'string of characters here'
CHANNEL_ID = 123456789 # actually a 17- or 18-digit integer
And the bot itself:
import discord
from discord.ext import commands
from tokens import TOKEN, CHANNEL_ID
client = commands.Bot(command_prefix='!')
client.sniped_message = None
#client.event
async def on_ready():
print("Your bot is ready.")
#client.event
async def on_message_delete(message):
# Make sure it's in the watched channel, and not one of the bot's own
# messages.
if message.channel.id == CHANNEL_ID and message.author != client.user:
print(f'sniped message: {message}')
client.sniped_message = message
#client.command()
async def snipe(ctx):
# Only respond to the command in the watched channel.
if ctx.channel.id != CHANNEL_ID:
return
if client.sniped_message is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
message = client.sniped_message
embed = discord.Embed(
description=message.content,
color=discord.Color.purple(),
timestamp=message.created_at
)
embed.set_author(
name=f"{message.author.name}#{message.author.discriminator}",
icon_url=message.author.avatar_url
)
embed.set_footer(text=f"Deleted in: #{message.channel.name}")
await ctx.channel.send(embed=embed)
client.run(TOKEN)
Multi-Guild Version
If you're monitoring one channel each in multiple guilds, then you need to keep track of them separately. Handily, channel IDs are globally unique, not just within a single guild. So you can keep track of them by ID alone, without having to include the guild ID as well.
You could keep them in a list, but I recommend a set, because checking whether something is in a set or not is faster. Comments to help yourself remember which one is which are probably also a good idea.
TOKEN = 'string of characters here'
# Not a dictionary, even though it uses {}
CHANNEL_IDS = {
# That one guild
123456789,
# The other guild
987654322,
}
Then instead of checking against the single channel ID, you check if it's in the set of multiple IDs.
import discord
from discord.ext import commands
from tokens import TOKEN, CHANNEL_IDS
client = commands.Bot(command_prefix='!')
client.sniped_messages = {}
#client.event
async def on_ready():
print("Your bot is ready.")
#client.event
async def on_message_delete(message):
# Make sure it's in a watched channel, and not one of the bot's own
# messages.
if message.channel.id in CHANNEL_IDS and message.author != client.user:
print(f'sniped message: {message}')
client.sniped_messages[message.channel.id] = message
#client.command()
async def snipe(ctx):
# Only respond to the command in a watched channel.
if ctx.channel.id not in CHANNEL_IDS:
return
try:
message = client.sniped_messages[ctx.channel.id]
# See note below
except KeyError:
await ctx.channel.send("Couldn't find a message to snipe!")
return
embed = discord.Embed(
description=message.content,
color=discord.Color.purple(),
timestamp=message.created_at
)
embed.set_author(
name=f"{message.author.name}#{message.author.discriminator}",
icon_url=message.author.avatar_url
)
embed.set_footer(text=f"Deleted in: #{message.channel.name}")
await ctx.channel.send(embed=embed)
client.run(TOKEN)
Note: bare except clauses, like in your original code, are not generally a good idea. Here we only want to catch KeyError, which is what is raised if the requested key isn't in the dictionary.
You could, optionally, implement the same logic in a different way:
message = client.sniped_messages.get(ctx.channel.id)
if message is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
A dictionary's .get() method returns the corresponding item just like normal indexing. However, if there is no such key, instead of raising an exception, it returns a default value (which is None if you don't specify one in the call to get).
If you're using Python 3.8+, the first two lines could also be combined using an assignment expression (using the "walrus operator"), which assigns and checks all at once:
if (message := client.sniped_messages.get(ctx.channel.id)) is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
These alternative options are mentioned for completeness; all of these ways of doing it are perfectly fine.
how do I get the name of a channel so that this bot will work on any server its put on with no changes to code necessary? ( in the code where I put "what do I put here" is where I want the name to be in a variable)Thanks
from discord.ext.commands import Bot
import time, asyncio
TOKEN = 'Its a secret'
BOT_PREFIX = ["!"]
client = Bot(command_prefix=BOT_PREFIX)
#client.event
async def on_message(message):
if message.author == client.user:
return
#client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
await start()
while True:
currentTime = time.strftime("%M%S", time.gmtime(time.time()))
if currentTime == "30:00":
await start()
await asyncio.sleep(1)
async def start():
mainChannel = #What do i put here?
print(mainChannel.name)
await client.send_message(mainChannel, "Starting countdown", tts = True)
client.run(TOKEN)
Getting channel from ID (Recommended)
First, get the ID of the channel
(Right click the channel and select "Copy ID")
Second, put the ID in the following code:
client.get_channel("ID")
For example:
client.get_channel("182583972662")
Note: The channel ID is a string in discord.py async, and an integer in rewrite
(Thanks to Ari24 for pointing this out)
Getting channel from name (Not reccomended)
First, get the server using either:
server = client.get_server("ID")
OR
for server in client.servers:
if server.name == "Server name":
break
Second, get the channel:
for channel in server.channels:
if channel.name == "Channel name":
break
What not to do
Try to always use the ID for each server, as it is much faster and more efficient.
Try to avoid using discord.utils.get, such as:
discord.utils.get(guild.text_channels, name="Channel name")
Although it does work, it is bad practise as it has to iterate through the entire list of channels. This can be slow and take much more time than using the ID.
From the discord API docs:
discord.utils.get is a helper that returns the first element in the
iterable that meets all the traits passed in attrs
Now in rewrite there's a method called discord.utils.get where you can actually getting discord objects with specific parameters
In your case with a channel name:
import discord
channel = discord.utils.get(guild.text_channels, name="Name of channel")
Should be None if discord couldn't find a textchannel with that name
It is actually really easy:
You can simply do message.channel.name
Example:
print(message.channel.name)
Well, it's simple to do. However your code can be improved by several way. It will be easier to read your code.
to get the channel and send a message to it, use this
ch = client.get_channel(channel id)
await ch.send("message")
few optimizations to your code
from discord.ext import commands
import discord
import time
TOKEN = "token"
client = commands.Client(command_prefix="!")
#client.event
async def on_ready():
print(f"Logged in as {client.user}")
print(f"ID: {client.id}")
I have been trying to create a bot for Discord using the discord.py library however when I am running the program it is not sending the message as expected. It is a simple bot that is suppose to send a message every 10 minutes to a channel. I am not getting any error messages in the command line and can't seem to see any obvious errors? Any help would be greatly appreciated.
import asyncio
client = discord.Client()
async def my_background_task():
await client.wait_until_ready()
counter = 0
channel = discord.Object(id='my channel ID goes here')
while not client.is_closed:
counter += 1
await message.channel.send("TEST")
await asyncio.sleep(5) # task runs every 60 seconds
#client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
client.loop.create_task(my_background_task())
client.run('My token goes here')
Although creating a task could work, I wouldn't recommend it when there are better options. Dpy's command.ext addon has a task based system built in directly, so let's look into using that instead.
import discord
from discord.ext import tasks
client = discord.Client()
#tasks.loop(minutes=10)
async def my_background_task():
"""A background task that gets invoked every 10 minutes."""
channel = client.get_channel(754904403710050375) # Get the channel, the id has to be an int
await channel.send('TEST!')
#my_background_task.before_loop
async def my_background_task_before_loop():
await client.wait_until_ready()
my_background_task.start()
client.run('Your token goes here')
The time until which the above loop will run is dependent upon human psychology, laws of energy and cosmos.
That is:
• You get bored of it
• The power goes down and your script stops working
• The universe explodes
Read more about it here:
https://discordpy.readthedocs.io/en/latest/ext/tasks/
If the channel ID is a string, It should be an integer.
Turn this:
channel = discord.Object(id='my channel ID goes here')
into:
channel = discord.Object(id=101029321892839) # an example ID
If that doesn't solve your problem, try setting 'message.channel.send' to 'channel.send' and client.is_closed should be
client.is_closed() # put brackets
Instead of client.loop.create_task(my_background_task()), just put the background function in the on_ready event. And take out await client.wait_until_ready() and just put the function in the on_ready event as well.
I have also changed the client variable, taken out unnecessary code, and added some modules.
import asyncio, discord
from discord.ext import commands
client = commands.Bot(command_prefix = ".") # If you don't want a prefix just take out the parameter.
async def my_background_task():
channel = discord.Object(id='my channel ID goes here')
while True:
await channel.send("TEST")
await asyncio.sleep(5) # task runs every 60 seconds
#client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
my_background_task()
client.run('My token goes here')
Also in future when posting a question, please tag the python version as I don't know which version it is and then this and other answers could be wrong.
I'm trying to make a bot for a discord server that simply listens for specific messages, deletes them and then refers the user to a different text channel (in a clickable link by mentioning it)
Here's what I have now:
import Discord
import asyncio
client = discord.Client()
#client.event
async def on_message(message):
msg = '{0.author.mention}\nWrong text channel\nUse '.format(message)
if message.content.startswith('!p'):
await client.delete_message(message)
await client.send_message(message.channel, msg)
return
client.run('')
Ideally, I'd also want to search through a list with startswith() instead of just ('!p') & to ignore all messages from a specific text channel as well but I'm not sure how to do those either
Don't know if your problem is solved or not, but for all future developers looking on this thread. A super simple way to get a bot to mention a channel is like this...
<#channelID>
were the channel ID is the discord ID of the specific channel you wish to mention.
and in an example
await message.channel.send("Please go to <#channelID>")
I'm honestly a bit confused that no one on this thread has already mentioned this, so I feel like I'm missing something that y'all already know lol.
Sure, just add text_channel = client.get_channel('1234567890') and reference its mention with text_channel.mention (where 1234567890 is the id of the channel you want to link to)
So the code would end up looking something like this
#client.event
async def on_message(message):
text_channel = client.get_channel('1234567890')
msg = '{0.author.mention}\nWrong text channel\nUse {1.mention}'.format(message,text_channel)
if message.content.startswith('!p'):
await client.delete_message(message)
await client.send_message(message.channel, msg)
return
Regarding your second question, you could do something like this
arr = ['!p','!a','!b']
for a in arr:
if message.content.startswith(a):
break
else:
return
and remove the if message.content.startswith('!p'): altogether
To ignore a specific channel just do if message.channel.id == "9876543210": at the top of the function (9876543210 is the id of the channel you want to ignore commands from)
With those changes the code looks like this
#client.event
async def on_message(message):
if message.channel.id == "9876543210":
return
arr = ['!p','!a','!b']
for a in arr:
if message.content.startswith(a):
break
else:
return
text_channel = client.get_channel('1234567890')
msg = '{0.author.mention}\nWrong text channel\nUse {1.mention}'.format(message,text_channel)
await client.delete_message(message)
await client.send_message(message.channel, msg)
return
import discord
from discord.ext import commands
client = commands.Bot(command_prefix='>')
#client.event
async def on_ready():
print("Log : "+str(client.user))
#client.command()
async def mention(ctx):
ch = await client.fetch_channel(enter channel id)
await ctx.send(f"Mention your channel -> {ch}")
client.run("token")