Search discord channels for prefered posting channels - python

I'm trying to search through text channels for preferred channels to post in. Here's my code that works now.
#bot.event
async def on_guild_join(Guild):
channel_names = ['cool-channel', 'awesome-server', 'chatters-be-chatting']
for ch in channel_names:
try:
channel_id = discord.utils.get(Guild.text_channels, name=ch).id
channel = bot.get_channel(channel_id)
#embed stuff
await channel.send(embed=embed)
break
except:
pass
The main problem I have is if someone uses emojis or Unicode in the name, it'll pass right over it. I guess I'm trying to figure out how to search key works in channel names regardless of whatever else is in the name.

I'm not sure if I understood you correctly, but if you want to match channel names with keywords you give and then post a message in those channels, a code like this below would do.
I made it a #bot.command to test it easily, but I'm sure you can rewrite it to a #bot.event without a problem.
#bot.command()
async def test(ctx):
keywords = ['cool', 'awesome', 'chatters'] # there you can put your keywords to match with channel names
text_channel_list = [] # empty list for channel names in the guild
matches = [] # empty list for channels matching keywords
for channel in ctx.message.guild.text_channels:
text_channel_list.append(str(channel)) # adds every channel in the guild to the text_channel_list
for keyword in keywords:
for text_channel in text_channel_list:
if keyword in text_channel: # looks if keyword is in any channel name
matches.append(text_channel) # if there's a match, it adds the matched channel name to matches list
for match in matches:
channel_id = discord.utils.get(ctx.message.guild.text_channels, name=match).id
channel = bot.get_channel(channel_id)
await channel.send('test') # prints a test message in every channel from the matches list

Related

Create embed based of users message

I'm quite new to discord.py so I'm posting this here. How do I make a command so that when the user says !embed (message here) #channelhere it turns the users message into an embed and then posts it into the specified channel? An explanation would be nice to so I can try to understand it.
Based on the 2 answers I see, you can send an embed to a specific channel whatever you want by converting the channel into the channel id.
Here's the code :
#client.command()
async def echoembed(ctx, channel : discord.TextChannel, *, Text : str): # full command (!echoembed #channelhere Title here | Description here)
Title, Description = Text.split(' | ') # You need to split the arguments using |, change to other symbols whatever you want
channel_id = channel.id # this will convert the channel name into the channel id
channel_send = client.get_channel(channel_id) # we will get the channel using the channel id
embed = discord.Embed(title = Title, description = Description, color = 0x2F3136)
embed.timestamp = datetime.datetime.utcnow() # this will tell the time today
embed.set_footer(text = "\u200b", icon_url = ctx.author.avatar_url) # the \u200b is an empty character
await channel_send.send(embed = embed) # and finally, we can send the embed
If you're fairly new to discord.py, I suggest you need to read the docs before going into complicated stuff.
The docs will be linked Here
Thank me later :D
Sample code I have:
Eg: I enter !embed hello / wow
#bot.command() #used before a command
async def embed(ctx, *, content: str): #when the person uses !embed it will do what is under. Ctx is needed to store the channel of the message, author of message and other properties. content is the argument, or what you put after the !embed (in this case hello / wow)- we state that it is a string(str) or plain text if that is more clear
title, description= content.split('/') #the title and description of the embed are separated by "/" In this cas, title is "hello",description is "wow"
embed = discord.Embed(title=title, description=description, color=0x00ff40) #making the embed (MUST HAVE TITLE AND DESCRIPTION)
await ctx.send(embed=embed) #sending the embed in THE SAME CHANNEL YOU SENT THE COMMAND IN (as shown via ctx)
await ctx.message.delete() #deleting the original message by you to clean the channel up(as shown by ctx.message)
In terms of specific channel that might be a bit more complicated: however this should work perfectly
Basiclly you have to create a variable called channel that contains the channel ID, for example: channel = bot.get_channel(636399538650742795). Then you create a command that takes an argument, this argument is the message to be embeded, you store the argument in a variable, make a variable called embed, this variable will be embed = discord.Embed(value=argument), wich we will send with await channel.send(embed=embed). The actual code looks like this:
#bot.command()
async def embed(ctx, argument: str):
channel = bot.get_channel(636399538650742795)
embed = discord.Embed()
embed.add_field(name="Message", value=argument)
await channel.send(embed=embed)
To get the channel ID, just right click the channel you want to get the ID of and click Copy ID. And considering that you're very new to discord.py, I am using #bot.command() because I am defining it with bot = commands.Bot(command_prefix="$")

Discord.py: How to add another multi-word parameter in a command function?

I am trying to create a announcement function which would send the necessary embed into the channel I specified. I have added the text that has to be sent but now I want it to include images as well.
I can't send links of images inside the embed as they won't work.
I want to send another parameter that will include the image link and send it separately into the chat. I have tried it but it was only for one single image. But when we wanted to enter one more it wouldn't work. I am trying to understand on how to do it.
The following is my code for the command I am working on.
#nd.command()
async def announce(ctx, channel, *, message):
townannouncerole = discord.utils.get(ctx.guild.roles, id=834684222441521193)
townannouncementchannel = nd.get_channel(834675349409234974)
if str(channel.lower()) == 'town':
if townannouncerole in ctx.author.roles:
townannouncementembed = discord.Embed(title="Town Announcement", description=message, color=discord.Colour.random())
townannouncementembed.set_author(name=ctx.author)
townannouncementembed.set_footer(text=datetime.datetime.now())
await townannouncementchannel.send(embed=townannouncementembed)
else:
await ctx.channel.send(f"Hi {ctx.author.mention}, you lack necessary permissions for this command!")
This is the code I used to send a single image and it worked fine.
#nd.command()
async def announce(ctx, channel, images, *, message):
townannouncerole = discord.utils.get(ctx.guild.roles, id=834684222441521193)
townannouncementchannel = nd.get_channel(834675349409234974)
if str(channel.lower()) == 'town':
if townannouncerole in ctx.author.roles:
townannouncementembed = discord.Embed(title="Town Announcement", description=message, color=discord.Colour.random())
townannouncementembed.set_author(name=ctx.author)
townannouncementembed.set_footer(text=datetime.datetime.now())
await townannouncementchannel.send(embed=townannouncementembed)
await townannouncementchannel.send(images)
else:
await ctx.channel.send(f"Hi {ctx.author.mention}, you lack necessary permissions for this command!")
Now the issue is I am not able to understand on How can I send multiple links into the chat by adding another multi-word parameter like the message one that will include every text after it. It is not necessary that you keep the parameters in the order. You can shuffle it in any way you want, to explain and solve.
Thank You! :)
You can't add another multiword parameter into a discord bot command, but what you can do is wait for another response, let me explain:
You can do like this:
#nd.command(name = "sending_links", aliases = ["send_link", "links_send"])
async def sending_links(ctx, channel):
channel = channel or ctx.channel
ctx.send("what links do you want to send? if you want to send many just seperate with \",
\" ex. www.image1.com, www.image2.com")
response = await nd.wait_for("message", timeout=60)
response = response.content.split(", ")
for link in response:
embed = discord.Embed(title = "title", description = "description")
embed.set_image(url = link)
channel.send(embed = embed)
I can't send links of images inside the embed as they won't work.
Yes you can send an image into an embed if you do embed.set_image(url = image_url)
discord.py does not allow to use keyword arguments. So you need to make a argument parser.
Example: !announce --message 'your message' --image 'url'
So you can to it with splitting content from "--" and user arguments as you want.

How to snipe messages from a specific channel (discord.py)

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.

(Discord.py) Send a message to all guilds

I've been trying to send the same message to all the guilds where my bot is, but nothing has worked and I haven't been able to find something similar in the docs. Is this posible? How can I do It?
Edit: ok, lets's imagine that what my bot does is checking a news webpage like BBC or any other. The bot would be checking the web every five minutes and sending links of the news to those guilds where .start (for example) was executed. The way I've implemented this, was by a command that generates a single loop for each guild, the idea was to have just one loop for every guild.
for guild in bot.guilds:
await guild.text_channels[0].send(<message>)
This will get the first text channel found in the guild and send a message to it.
You can get the bot from a context variable by typing ctx.bot.
class send_all(commands.Cog):
def __init__(self,bot):
self.bot = bot
#commands.command()
async def send(self,ctx,*message_to_send):
guild = ctx.message.guild
output = ' '
author = ctx.message.author
for word in message_to_send:
output += word
output += ' '
for member in self.bot.get_all_members():
try:
embed = discord.Embed(title="",colour = discord.Colour.green())
embed.add_field(name="**From server:**",value= guild.name)
embed.add_field(name = "**From Mod/Admin:**",value = author.name)
embed.add_field(name="**Message:**",value = output)
# await ctx.send(embed=embed)
await member.send(embed=embed)
except (discord.HTTPException, discord.Forbidden,AttributeError):
continue

How to set channel object using the channel id in discord.py?

Hello guys I'm trying to make a function that checks if few channels in the my discord server are full but I didn't know how to specify them.
The best idea I had was using the idea but couldn't set it to a channel object so how can I do that or is there any other good ideas?
def full(channel):
while True:
if len(channel.voice_members) == channel.user_limit:
print("Wooo There is a full channel.")
asyncio.sleep(10)
But I didn't know what to set the channel parameter.
Thanks in advance for your help.
You can get the channel object either by ID or by name.
Use discord.utils.get() to return a channel by its name (example for voice channels):
channel = discord.utils.get(server.channels, name="Channel_name_here", type="ChannelType.voice")
Or you can use discord.get_channel(id) if you have the ID of the channel.
So for example if you have a list of channel names you want to check:
channel_names = ['channel1', 'channel2', 'channel3']
for ch in channel_names:
channel = discord.utils.get(server.channels, name=ch, type="ChannelType.voice")
full(channel)
See more in the documentation:
discord.get_channel()
discord.utils.get()
If you want this to be a command, you can also take advantage of converters:
#bot.command(pass_context=True)
async def full(ctx, channel: discord.Channel):
if len(channel.voice_members) == channel.user_limit:
await bot.say("{} is full".format(channel.name))
else:
await bot.say("{} is not full".format(channel.name))

Categories