I'm trying to forward a message with a group of photos. But they are redirected by separate messages in destination channel. How can I redirect with one message?
#client.on(events.NewMessage(incoming=True, chats=FROM))
async def handler_frw(event):
await client.forward_messages(CHANNEL_TO, event.message)
If I use "send_message" it send as one message.
But TO_NAME - doesn't work with id Channel, only name :(
And i need "Forward from" in destination channel.
#client.on(events.Album(chats=FROM))
async def handler_snd(event):
print("Have album.")
caps = []
for item in event.messages:
caps.append(item.message)
await client.send_message(TO_NAME, file=event.messages, message=caps)
If you want to forward the whole album, utilize the corresponding Album event instead of the New Message event. Telethon supposedly does the necessary hacks to collect the single messages together.
See the docu
from telethon import events
#events.register(events.Album)
async def albumHandler(self, event):
# Forwarding the album as a whole to some chat
event.forward_to(chat)
Related
I am currently making a music bot and I don't know how to send a message when track has started. I have tried this, but doesn't work.
async def on_wavelink_track_start(player: wavelink.Player, track: wavelink.Track):
Ctx = player.ctx
AttributeError: 'Player' object has no attribute 'ctx'.
I don't know what to do because the documentation doesn't say anything about this.
Version of llibraries I am using: Wavelink(1.3.5) and Discord.py(2.0).
Player only has guild as an attribute, which isn't enough to send a message.
You can grab a channel object using its id and use that to send your message. The below assumes that your bot client is named client in your code.
async def on_wavelink_track_start(player: wavelink.Player, track: wavelink.Track):
channel = client.get_channel(123456789) # replace with correct channel id
await channel.send('Message to send')
This is only if you want to use the on_wavelink_tract_start event. You could always just send the message from the command that starts the process using ctx like normal.
I have a function that pulls a random embed placed in a channel by a bot and reposts the embed the channel where the function is called. I would like to extend the functionality here and allow the user to call the function with a filter for the user of their choosing -
!bestpost - selects a random embed from specific channel and posts it to the channel where the function is called
!bestpost $user - selects a random embed from a specific user in a specific channel and posts the embed to the channel where the function is called
My issue here is that the embeds that the bot has created are posted by the bot NOT the user so I can't check by author. However the user's name is always present in the title field of the bot-created embed.
#client.command()
async def bestpost(ctx, poster: Optional[str.lower]):
channel = client.get_channel(123)
best_posts = []
# if poster is specified, then only pulls messages from that user
if poster is not None:
async for message in channel.history(limit=None):
if poster in message.embeds[0].title:
best_posts.append(message)
# if poster is not specified, then pulls all messages
else:
async for message in channel.history(limit=None):
best_posts.append(message)
random_bestpost = random.choice(best_posts).embeds[0]
await ctx.send(embed=random_bestpost)
Here is the most recent error it's been spitting at me
TypeError: argument of type 'NoneType' is not iterable
I've been dinking with this for a few hours and I'm pretty confident I'm just misunderstanding something simple or or just developing brain lesions from staring at such a simple problem for so long. I appreciate any feedback or assistance anyone can provide.
Here is the simple solution for the thing that you want to achieve:
import discord
import random
import re
def bestpost(user, channel):
all_posts = []
for post in channel.history(limit=1000):
if user in post.title:
all_posts.append(post)
if all_posts:
return random.choice(all_posts)
Async Solution
I wrote a solution for you that uses the content of the message title to decide which user to filter by. If a message title has the text $user in it, it means you want to filter the posts by that user.
Feel free to ask for clarifications in the comments.
import discord
import random
TOKEN = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
client = discord.Client()
#client.event
async def on_message(message):
if message.content.startswith('!bestpost'):
args = message.content.split()
if len(args) >= 1:
user = args[1]
else:
# Here you can handle the error of not specifying a user
continue
# This line gets all the messages from the channel
async for message in message.channel.history():
# This line checks if the author of the message is the bot. You can replace this condition with any other condition you like to filter the messages by.
if message.author.id != client.user.id:
continue
if user in message.title:
# This line gets the embeds from all the messages
embeds = message.embeds
# This line checks if there are any embeds in the message. You can replace this condition with any other condition you like to filter the messages by.
if embeds:
# This line gets a random embed from the message
embed = random.choice(embeds)
# This line sends the embed to the channel where the command was used
await message.channel.send(embed=embed)
client.run(TOKEN)
When I receive a slash command with my bot, I send a modal to a user asking for information.
All this works, however, as part of that, I would also like to send a message to a specific channel on a specific server (guild) to say that a request has been made.
I am having trouble with that second part.
import discord
bot = discord.Bot()
client = discord.Client()
#bot.slash_command(name = "create-trial-request", description = "Create a new trial request from a bbcode template.")
async def trial_request(ctx):
modal = my_modal(title="Fill this in please")
await ctx.send_modal(modal)
class my_modal(discord.ui.Modal):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.add_item(discord.ui.InputText(label="Some Label Name"))
async def callback(self, interaction: discord.Interaction):
request_number = request_number_generator()
# here is where I want to send my message to a specific channel.
# I know the ID of that channel, I just don't know how to send the message there.
code_snippet = format_to_code(bbcode)
request_response = "Created request #" + str(request_number)
await interaction.response.send_message(request_response, ephemeral=True)
I have tried the following (placed where my comments are in the code above):
channel = client.get_channel(6648250855168XXXXX)
await channel.send("Created trial request #" + str(request_number))
...but I get: AttributeError: 'NoneType' object has no attribute 'send'
Obviously the bot has access to the channel, and if I write to it as part of the response to the slash command, it successfully writes there, but I can't seem to make it work on its own.
Is there any way to do what I am trying to?
Thanks for any help.
To do so, 1. import commands aka from discord.ext import commands
Then remove your bot = discord.Bot and edit client do it is client = commands.Bot()
thats what i do
Thanks to #3nws, I now know the answer to this question.
The issue in my case was that I was using both client and bot and should have only been using one of the two (in this instance, bot, since I am using slash commands.
There is a usable bot.getchannel command that does what I wanted.
I hope this helps anyone else with the same issue.
First of all: Don't create 2 bot/client instances. Just use commands.Bot once. That's the reason you made a mistake. You used client instead of bot. Replace it and it should work.
Otherwise, if it would be still None, here are some possible reasons why:
• Not subscribed to the relevant intent.
• Wrong key
◦ Remember, IDs are ints, not strings
◦ If you're trying to copy an emoji ID, right-clicking the emoji in a message will copy message ID
• Bot not logged in, and trying to grab objects from cache
◦ Subvairant, using two client objects.
• Bot cannot "see" the object.
◦ It has to be on the server, share a server with the member, etc
◦ If you're sharded on separate processes, each process will only have objects for that shard.
• Objects retuned by fetch_x will not have a populated cache.
It doesn't make a huge difference if you do bot.get_channel() or bot.get_guild().get_channel(). You can use both.
How do I make a discord.py bot not react to commands from the bot's DMs? I only want the bot to respond to messages if they are on a specific channel on a specific server.
If you wanted to only respond to messages on a specific channel and you know the name of the channel, you could do this:
channel = discord.utils.get(ctx.guild.channels, name="channel name")
channel_id = channel.id
Then you would check if the id matched the one channel you wanted it to be in. To get a channel or server's id, you need to enable discord developer mode. After than you could just right click on the server or channel and copy the id.
To get a server's id you need to add this piece of code as a command:
#client.command(pass_context=True)
async def getguild(ctx):
id = ctx.message.guild.id # the guild is the server
# do something with the id (print it out)
After you get the server id, you can delete the method.
And to check if a message is sent by a person or a bot, you could do this in the on_message method:
def on_message(self, message):
if (message.author.bot):
# is a bot
pass
You Can Use Simplest And Best Way
#bot.command()
async def check(ctx):
if not isinstance(ctx.channel, discord.channel.DMChannel):
Your Work...
So just to make the bot not respond to DMs, add this code after each command:
if message.guild:
# Message comes from a server.
else:
# Message comes from a DM.
This makes it better to separate DM from server messages. You just now have to move the "await message.channel.send" function.
I assume that you are asking for a bot that only listens to your commands. Well, in that case, you can create a check to see if the message is sent by you or not. It can be done using,
#client.event
async def on_message(message):
if message.author.id == <#your user id>:
await message.channel.send('message detected')
...#your code
I have set up a script to download messages from thousands of Telegram supergroups (named chatlist). This works fine when I use a few groups, however, allowing 1000< groups seems to break it and no messages are collected at all. Is this the correct way to approach it, or is there a more efficient way? I don't think it's a network issue my end as I have GB internet
from telethon import TelegramClient, events, sync
client = TelegramClient('test', api_id, api_hash)
#client.on(events.NewMessage(chats=chatlist))
async def my_event_handler(event):
message = event.message.to_dict()
print(message['message'])
await client.start()
await client.run_until_disconnected()
A simple workaround is to remove the NewMessage event filter i.e.:
#client.on(events.NewMessage())
and filter messages inside the method yourself:
async def my_event_handler(event):
message = event.message.to_dict()
input_chat = await event.get_input_chat()
if input_chat in chatlist:
print(message['message'])
You didn't mention what's inside chatlist, so I assumed it's a list of InputChat objects.
Note that if your chat list is a list of #username strings, you'll soon reach Telegram username resolve limits. Always have InputChat or long ids in the chatlist.