(Discord.py) Send the same message to all guilds - python

I'm developing a small bot for a few groups of friends that sends the same message at a specific time every Friday using Cron.
It has a "CHANNEL_ID" variable with the channel ID to send the message in, but this only works with a specific guild.
Is there any way to send the same message to multiple IDs, like a list or an array?
The method to send the message in the first text channel available to the bot in the guild can't work, because it depends from server to server.

The nearest example of what you want seems like this
async def SendMsgAll(self, ctx):
count = 0
for guild in self.client.guilds:
channel_in_guild = []
for channel in guild.text_channels:
channel_in_guild.append(channel)
send_channel = channel_in_guild[count]
try:
await send_channel.send("test")
except:
count += 1

Related

How does a Discord Bot that is added to someone else's server get the channel id of where they want it to send messages?

I currently have a discord bot that works well on my own private server. I would like the bot to have the ability to join other people's servers if they choose to add them, but one of the parts of my bot is that it automatically sends messages to a specific channel every so often. To do this, I used the channel id of the channel that I use on my server:
async def esportsdiscussions():
await client.wait_until_ready()
channel = client.get_channel([Channel Id])
await channel.send('Hi!')
If my bot was added to another server my server's channel id obviously wouldn't work so is there a way to automatically get the general channel id from a server that it's on? Or is there another way to create a list of channel ids for different servers?
you can use the on_guild_join event to get the guild with all his channels
or also find the guild, by id or name, and then find a channel by id or name
But if you want to keep the channel ids stored, even after restart you need to save them to a database or something like that
You can either use MongoDB (as I do), or if it is just a smaler projekt JSON, SQLite, or just a .txt would also work
I will use a python dict as example, which is pretty similar to JSON or the MongoDB output
database = { # if this is in a .json file, only use everyting
"guilds": [ # behind "database = ", starting with the first { and the last }
{
"guild_id": 1111, # you can already set this to your guild
"channel_id": 1112 # and channel id
}
]
}
#bot.event
async def on_guild_join(guild):
"""
You can try to find a channel with a specific name here and use this,
but maybe the name does not exists, so i don't recommend this
"""
# for example
for channel in guild.text_channels:
if channel.name == "something":
...
#bot.command()
async def set_channel(ctx, channel: discord.TextChannel):
"""You can use a command so server admins kann set the channel by themself"""
current_guild_ids = [guild["guild_id"] for guild in database["guild"]]
if ctx.guild.id in current_guild_ids: # check if the guild already exists in the db
await ctx.send("You already set a channel")
else: # add the guild and channel to the db
new_guild_data = {"guild_id": ctx.guild.id, "channel_id": channel.id}
database["guilds"].append(new_guild_data)
await ctx.send("Channel was added")
# and if you want to get the guild without a event or command you can do this:
async def some_function():
guild_data = database["guilds"][0] # the first guild in the db for example
# prints: {"guild_id": 1111, "channel_id": 1112}
guild = bot.get_guild(guild_data["guild_id"])
channel = bot.get_channel(guild_data["channel_id"])
# then you can send messages to it
await channel.send("Hi!")
# or find the by name with discord.utils.find()
guild = discord.utils.find(lambda g: g.name == 'GuildName', client.guilds)
channel = discord.utils.find(lambda c: c.name == 'ChannelName', guild.channels)
discord.utils.find docs
Assuming that every server your bot joins would have #general, the simplest way would be to use discord.utils' get function. It would iterate through the guild's channels, and return a channel object if a #general channel is found.
channel = discord.utils.get(ctx.guild.channels, name="general")
Other questions like this:
Is it possible to get channel ID by name in discord.py
get the name of a channel using discord.py
Send a message when the bot joins a server (a bit old, would need tweaking)
Sometimes, a server may have their general labeled as #ā•šgeneral, #generalšŸ¤£, etc, which would be able to bypass the above code. You could use this slightly longer method in this case.
for channel in ctx.guild.channels: # for every channel in this guild,
if "general" in channel.name.lower() # if the word 'general' is in this channel's name (lowercase)
channel = channel # the channel variable is now this channel
break # stop the loop
Overall, the only main issue would be if the guild does not have a #general of any sort, such as a change of font or not having the channel to begin with.
for channel in ctx.guild.channels:
print(channel.id)
this code will list all the channel in that server.
if you want the name use channel.name
or you can use bot.get_all_channels() which will return all the channels in list

how to send albums as new messages in telethon

I want to send every new message from one channel to another.
But when there are albums in the message, they are sent separately to the destination channel.
How can I send them as an album (as one message)?
#client.on(events.NewMessage('your_channel'))
async def my_event_handler(event):
await client.send_message('my_channel', event.message)
And I used events.album instead of events.NewMessage, but it didn't work!
I find a simple way, maybe someone will use it
#client.on(events.Album(chats="your_channel"))
async def handler(event):
await client.send_message(
"my_channel", #output channel
file=event.messages, #list of messages
message=event.original_update.message.message, #caption
)

How to send welcome message in first text channel discord.py

I've got a pretty decent bot that I'm planning to launch soon. It's called moderator and it's supposed to be the perfect moderator bot so that a server won't need actual moderators anymore. So I want it to send a welcome message when it joins the server, but because all servers have different channel names and channels, I can't get a generic channel name to send the welcome message too.
channel = find(lambda x: x.name == 'general', guild.text_channels)
if channel and channel.permissions_for(guild.me).send_messages:
await channel.send(embed=embedvar)
This is what I have right now and as you can see it finds a channel named general and sends the welcome embed message to the general channel. But since not every server has a general channel, I want to have the bot find the 1st channel where it has permission to send messages. Is there a way to do that?
Thanks!
You can get the first channel of the guild with this way:
channel = client.get_guild(guild id).text_channels[0]
So with this code, you can do something like that:
#client.event
async def on_guild_join(guild):
channel = guild.text_channels[0]
embed = discord.Embed(title=guild.name, description="Hello, I'm here")
await channel.send(embed=embed)

Is it possible to make a command !messages #user

I'm trying to make a command where when someone types for example '!messages #mee6' it shows how many messages that person has said in the server. So say if I typed "a" "b" "c" then did "!messages"
the bot would reply with "#user has sent 3 messages in this server. Does anyone know if this would be possible and if so how would I go about doing it in discord.py?
You could do this by iterating over each text channel in your server and then iterating over every message in the channel. Then count the amount of messages that are sent by the user.
#bot.command()
async def message(ctx, user: discord.User):
count = 0
for channel in ctx.message.guild.text_channels:
async for message in channel.history(limit=None):
if message.author.id == user.id:
count += 1
await ctx.channel.send(('{} has sent {} messages').format(user.mention, count))
This is my method but i believe it will take some time to do so especially if the server is big because you are checking every channel.
#bot.command()
async def messages(ctx, user: discord.Member = None):
if user is None:
# no user selected author is the user now
user = ctx.author
counter = 0
for channel in ctx.guild.text_channels:
async for message in channel.history(limit=None):
if message.author == user:
counter += 1
await ctx.send(f'{user.mention} has sent {counter} messages')
The principle is the following:
You'd have to take advantage of the Message Received event. Once a message is received, update the counts for the person, and when the !messages command is ran, display the amount.
Looping through every channel in the server and filtering all messages from the user is extremely inefficient and time taking, as well as it can get your bot ratelimited.
Use the event on_message() so you can count messages from every user from now on.
Create a dictionary containing the users as keys and the amount of messages as values
users_msg = {}
#client.event
async def on_message(message):
#If this is the first user messsage
if message.author not in users_msg:
users_msg[message.author] = 1
else:
users_msg[message.author] += 1
if message.content.startswith("!message"):
#Return a list containing all user mentions on the message
user = message.mentions
#Send the first user mention amount of messages
await message.channel.send(f'{user[0].mention} has sent {users_msg[user[0]]}'
#If you want so you can loop through every user mention on the command !message
#for mentions in user:
#await message.channel.send(f'{mentions.mention} has sent {users_msg[mentions]}')

Pass context to background task discord.py

A server owner on discord asked me to Add a custom currency system to my bot for him (the bot is only on this server). To encourage activity we did not go with the dailies system and instead my idea was to have a message pop up every 7min with an image and users have to add a "Reaction" to the image. To make it easy and control the inputs i want to add a reaction to my image so users can just click the reaction and it will add the amount.
With all that context out of the way, here is the problem as a background task. I have no clue how to pass context to the reaction!
async def my_background_task():
await bot.wait_until_ready()
counter = 0
channel = discord.Object(id='446782521070321664')
while not bot.is_closed:
counter += 1
with open('vbuck.png', 'rb') as f:
await bot.send_file(channel, f) #sends a png of a vbuck
await bot.add_reaction(discord.message,'<:vbuck:441740047897591809>') #This is the Reaction part
await asyncio.sleep(420) # task runs every 7 min seconds
bot.loop.create_task(my_background_task())
If you could give me advice that would be great and if you are feeling extra generous Code + explanation would be appreciated I am learning python from this project.
You need to save the Message object that send_file returns (the message that was sent). Then you can use that Message object in Client.wait_for_reaction to wait for a reaction.
async def task():
await bot.wait_until_ready()
channel = discord.Object(id='446782521070321664')
vbuck = discord.utils.get(bot.get_all_emojis(), name='vbuck')
check = lambda reaction, user: user != bot.user
while not bot.is_closed:
msg = await bot.send_file(channel, 'vbuck.png') #sends a png of a vbuck
await bot.add_reaction(msg, vbuck)
for _ in range(7*60):
res = await bot.wait_for_reaction(message=msg, emoji=vbuck, check=check, timeout=1)
if res:
# res.user reacted to the message
If I understood the question correctly, you want to actually wait for users to react to the file you posted and then award them on that reaction. I'm gonna assume you are using discord.py version 1.0 or higher.
The background task itself can not, as far as I know, be handed any context - because it isn't invoked like a comment in a specific context.
However, the API (https://discordpy.readthedocs.io/en/rewrite/api.html#discord.on_reaction_add) states that there is a listener to reactions on messages, which means you can simply use
#bot.event
async def on_reaction_add(reaction, user):
#here do some checks on reaction.message and to check that it is actually
#the correct message being reacted too and avoid multiple reactions of one user.
The API will also tell you what kind of checks you can do on the message. You could give the message a certain signature when you let the bot post it (a timestamp like time.time() seems good enough) and then access reaction.message.content and compare it to the current time. To do that, I would modify your background task to something like this:
async def my_background_task():
await bot.wait_until_ready()
counter = 0
channel = bot.get_channel(id='446782521070321664')
while not bot.is_closed:
counter += 1
mess = "maybe a timestamp"
e = discord.Embed()
e.set_image(url = "some cool image maybe randomly chosen, please no vbucks")
await channel.send(mess, embed = e) #sends a message with embed picture
await asyncio.sleep(420) # task runs every 7 min
the message content will then simply be message.
Hope this helps.

Categories

Resources