I am almost new to telegram bots development and have basic experience in using the telethon python library.
I am working on a project where I need to schedule the message using telethon and then delete the messages if needed. I am using following code to schedule the messages on telegram channels and groups.
await client.send_message(receiver, message_text, schedule=schedule_time)
And when I need to get the scheduled messages for that channels I am using the following code to get the messages from the telegram servers.
result = await self.client(functions.messages.GetScheduledHistoryRequest(
peer=target,
hash=0
))
And I want to delete the messages I have scheduled. I have gone through the documentation of telethon library and found the following code to do delete the messages.
group = await client.get_entity(group_id)
list_of_messages = await get_scheduled_messages(group_id)
for message in list_of_messages:
status = await client.delete_messages(group, [message.id])
And this is not working. I have also tried the delete method for the message object like this.
group = await client.get_entity(group_id)
list_of_messages = await get_scheduled_messages(group_id)
for message in list_of_messages:
await message.delete()
Is there something I am missing or there is nothing to delete the scheduled messages which has not been sen yet!
In order to delete your scheduled messages you'll have first to get them using GetScheduledHistoryRequest and then you can delete all the messages you'd prefer to, using DeleteScheduledMessagesRequest.
chat = await client.get_input_entity('yourchat')
result = await client(functions.messages.GetScheduledHistoryRequest(
peer=chat,
hash=0
))
# use result.messages to access the list of scheduled messages
msg = result.messages[0] # or any other message you want to delete
await client(functions.messages.DeleteScheduledMessagesRequest(
peer=chat,
id=[msg.id] # !imporant you need to pass a list here
))
In order to edit a scheduled message, since the edit_messages() and edit() functions seems not working properly with scheduled messages, you'll need to use the Telegram raw APIs messages.editMessage.
chat = await client.get_input_entity('yourchat')
result = await client(functions.messages.GetScheduledHistoryRequest(
peer=chat,
hash=0
))
msg = result.messages[0] # or any other message you want to edit
await client(functions.messages.EditMessageRequest(
peer = msg.to_id,
id = msg.id,
message = 'new text',
no_webpage= False, # didn't find anything into the Message object to refer to
entities= msg.entities,
media= msg.media,
reply_markup = msg.reply_markup,
schedule_date= msg.date
))
Keep in mind that I've refer to the older message to fill the all parameters into the EditMessageRequest() function, but you should be able to edit no_webpage, media, schedule_date without any problem.
Related
I have a discord.py command that makes feeds of memes, but when I use the command, all the images sent by the bot with exception of the first one have a "tap to see attachment" as if they were a response to the first image, is it possible to make it not appear? If so, how?
#bot.hybrid_command(name = "command", with_app_command = True, description = "description")
async def test1(ctx, number: int):
i = 0
while i < number:
await ctx.send("Message")
i = i+1
await asyncio.sleep(5)
ctx.send will either try to respond to the interaction, followups to the response if a response has been given, or just send if the interaction has expired. I believe the behaviour you're seeing is expected.
If you change your ctx.send to ctx.channel.send then that should act like a more regular send.
We can also add a defer to prevent discord saying we didn't respond whilst we sent lots of messages, and then use ctx.send at the end to finally respond and tell the user we're done. Using ephemeral=True means that we only send the message to the user that initiated the command.
async def test1(ctx, number: int):
# defer the interaction to prevent "bot didn't respond" messages
await ctx.defer(ephemeral=True)
# do the rest of your code here
# once you're done sending lots of messages
await ctx.send("Finished meme feed", ephemeral=True)
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
I've been learning how to code a discord bot recently and I've hit a wall. I am wanting to create a method that allows me to move messages from one channel to another. I created a solution that works but not how I would like it to. Idealy I would want to just have the bot almost do a reddit repost where it takes the exact message and just has it embedded. Currently what I have for the method is
#client.event
async def on_message(message):
author = message.author
content = message.context
channel = message.channel
if message.content.startswith('!move'):
#code to process the input, nothing special or important to this
for i in fetchedMessages:
embededMessage = discord.Embed()
embededMessage.description = i.context
embededMessage.set_author(name=i.author, icon_url=i.author.avatar_url)
await channelToPostIn.send(embeded=embededMessage)
# delete the old message
i.delete()
Now this works perfectly for text messages but not for images or for example if the post was embedded in the first place. If someone has a more elegant solution or is able to point me in the right direction in the documentation I would be much appreciative. Thanks.
This would be a lot easier if you were to use the commands.Bot extension: discord.ext.commands.Bot() (check out a basic bot)
bot = commands.Bot(...)
#bot.command()
async def move(ctx, channel: discord.TextChannel, *message_ids: int): # Typehint to a Messageable
'''Moves the message content to another channel'''
# Loop over each message id provided
for message_id in message_ids:
# Holds the Message instance that should be moved
message = await ctx.channel.fetch_message(message_id)
# It is possible the bot fails to fetch the message, if it has been deleted for example
if not message:
return
# Check if message has an embed (only webhooks can send multiple in one message)
if message.embeds:
# Just copy the embed including all its properties
embed = message.embeds[0]
# Overwrite their title
embed.title = f'Embed by: {message.author}'
else:
embed = discord.Embed(
title=f'Message by: {message.author}',
description=message.content
)
# send the message to specified channel
await channel.send(embed=embed)
# delete the original
await message.delete()
Possible problems:
The message has both an embed and content (easy fix, just add the message.content to the embed.description, and check whether the length isn't over the limit)
The command isn't used in the channel where the message resides, so it can't find the message (can be fixed by specifying a channel to search the message in, instead of using Context)
A video being in an embed, I am unsure what would happen with for example a youtube link that is embedded, as a bot can't embed videos afaik
#Buster's solution worked correctly except for when a user uploaded a picture as a file attached to the message. To fix this I ended up setting the image of the embedded message with the proxy_url of the image attached. my whole move command is as followed.
# Move: !move {channel to move to} {number of messages}
# Used to move messages from one channel to another.
#client.command(name='move')
async def move(context):
if "mod" not in [y.name.lower() for y in context.message.author.roles]:
await context.message.delete()
await context.channel.send("{} you do not have the permissions to move messages.".format(context.message.author))
return
# get the content of the message
content = context.message.content.split(' ')
# if the length is not three the command was used incorrectly
if len(content) != 3 or not content[2].isnumeric():
await context.message.channel.send("Incorrect usage of !move. Example: !move {channel to move to} {number of messages}.")
return
# channel that it is going to be posted to
channelTo = content[1]
# get the number of messages to be moved (including the command message)
numberOfMessages = int(content[2]) + 1
# get a list of the messages
fetchedMessages = await context.channel.history(limit=numberOfMessages).flatten()
# delete all of those messages from the channel
for i in fetchedMessages:
await i.delete()
# invert the list and remove the last message (gets rid of the command message)
fetchedMessages = fetchedMessages[::-1]
fetchedMessages = fetchedMessages[:-1]
# Loop over the messages fetched
for messages in fetchedMessages:
# get the channel object for the server to send to
channelTo = discord.utils.get(messages.guild.channels, name=channelTo)
# if the message is embeded already
if messages.embeds:
# set the embed message to the old embed object
embedMessage = messages.embeds[0]
# else
else:
# Create embed message object and set content to original
embedMessage = discord.Embed(
description = messages.content
)
# set the embed message author to original author
embedMessage.set_author(name=messages.author, icon_url=messages.author.avatar_url)
# if message has attachments add them
if messages.attachments:
for i in messages.attachments:
embedMessage.set_image(url = i.proxy_url)
# Send to the desired channel
await channelTo.send(embed=embedMessage)
Thanks to everyone that helped with this problem.
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.
I am trying to make a command called '!clear' that just removes the bots messages in a specific channel. This seems pretty straight forward using:
Client = discord.Client()
client = commands.Bot(command_prefix = "!")
#client.event
async def on_message(message):
if message.content.lower().startswith("!clear"):
async for x in client.logs_from(message.channel, limit = 100):
if str(message.author.id) == 'insert bots client id':
await client.delete_message(x)
client.run(token)
However, i found it wasn't deleting any messages. So before checking message.author.id i added a simple print(message.author.id) to see what ids it is picking up. It appears that it is spitting out only my client id and not seeing its own messages. The weird thing though is that if i change the author.id check to my client id then it will delete my messages and also the bots messages. So for some reason the bot thinks that its client id is the same as mine so when it is in a server that isn't my test server, it doesn't have the right permissions to delete my messages so it fails, even though I only want it to delete its own messages.
Any help would be great.
Don't you mean
if x.author.id == "bot id here":
await client.delete_message(x)
After all, each message is being put in x each iteration. Using message refers to the message in the async def on_message(message) function. Which would be the person who used the command. aka, you.