why directly to the bot the test command test1 my real telegram id, and if it’s the same but already in the group, it displays a completely different id. bot has all rights
#dp.message_handler(lambda message: message.text == 'test1', content_types=['text'])
async def cmd_test(message: Message):
print('id_telegram =', message.from_user.id)
Related
I'm making a Telegram bot for work purposes. For confidentiality reasons I need to implement a identification check upon starting the bot.
I'm getting a list of employees IDs who are given access to the bot from a database. It is updated every day.
When a user starts the bot, it asks for the ID. The user is supposed to reply with their ID.
The bot then is supposed to check if this ID is in the list downloaded from the database.
If it is, the bot gives access to the information. If not, it replies with something like "Sorry, you are not a verified user. For getting access, please contact Kate or try again."
Also, it needs to check this information every day - since a list of employees can change every day (for example, in case one of the employees on the list quit their job).
I haven't been able to find suitable solutions on the Internet or come up with one that works for me.
Please help me out with ideas - I'm still new to Python and only just started learning about aiogram.
Things I have tried:
I managed to make the bot respond to 'XXXXXXX' with "verification succeeded" lol:
dp.register_message_handler(handlers.start, commands=["start"])
dp.register_message_handler(handlers.verify_user, text=["XXXXXXX"])
user_id_list = ['YYYYYYY', 'XXXXXXX', 'ZZZZZZ']
# start
async def start(message: types.Message):
await message.answer("Hi there! For verification please write your ID in this format: XXXXXXX.")
# verification
async def verify_user(message: types.Message):
if message.text in user_id_list:
keyboard = types.InlineKeyboardMarkup()
keyboard.add(types.InlineKeyboardButton(text="get report", callback_data="get_report"))
await message.answer("You have been identified!", reply_markup=keyboard)
else:
keyboard = types.InlineKeyboardMarkup()
keyboard.add(types.InlineKeyboardButton(text="try again", callback_data="/start"))
await message.answer("Sorry, you are not a verified user. For getting access, please contact Kate or try again.", reply_markup=keyboard)
I also tried using Text filter like this:
id_list = ['XXXXX', 'YYYYY']
dp.register_message_handler(handlers.verification, filters.Text(contains=id_list, ignore_case=True))
async def verification(message: types.Message):
keyboard = types.InlineKeyboardMarkup()
keyboard.add(types.InlineKeyboardButton(text="get report", callback_data="get_report"))
await message.answer("You have been identified!", reply_markup=keyboard)
but this only works if you include the whole id_list in your message (i.e. XXXXX YYYYY).
Another thing I tried is IDFilter but seems like it only works with numbers - and our IDs are str.
Finally, I tried creating a State and using FSM but I can't quite understand what this is doing: it checks my ID fine, but after that the bot does nothing. Moreover, if I restart the bot, it doesn't ask me for my ID, it tells me straight away that my ID is wrong (it sees '/start' as an ID input, I guess). The code is below:
id_list = ['XXXXX', 'YYYYY']
class Form(StatesGroup):
work_id = State()
dp.register_message_handler(handlers.set_work_id, state=Form.work_id)
async def start(message: types.Message):
await bot.send_message(message.chat.id, 'Hi there! What is your ID?')
await Form.work_id.set()
async def set_work_id(message: types.Message, state: FSMContext):
if message.text in id_list:
keyboard = types.InlineKeyboardMarkup()
keyboard.add(types.InlineKeyboardButton(text="get report", callback_data="get_report"))
await message.answer("You have been verified!", reply_markup=keyboard)
else:
keyboard = types.InlineKeyboardMarkup()
keyboard.add(types.InlineKeyboardButton(text="Try again", callback_data="/start"))
await message.answer("Sorry, you are not a verified user. For getting access, please contact Kate or try again.", reply_markup=keyboard)
I use on_raw_message_delete and i want to print the name of the person who deleted the message.
I can print the deleted message and user who writed the message but I don't know how to print the name of the person who deleted the deleted message.
#commands.Cog.listener()
async def on_raw_message_delete(self, payload):
channel = self.bot.get_channel(channel_id)
embed = nextcord.Embed(title="message deleted",
description=f"Message deleted in <#{payload.cached_message.channel.id}>\n Message deleted by {payload.name}")
It is not part of the message delete payload, so you'll need to check the audit logs.
You also may want to check the date of the entry since a user deleting their own message is not logged to audit logs.
#commands.Cog.listener()
async def on_message_delete(self, message: nextcord.Message):
async for entry in message.guild.audit_logs(action=nextcord.AuditLogAction.message_delete, limit=1):
log = entry
# if deleted more than a second ago, use the author instead
deleter = log.user if (datetime.now() - log.created_at).seconds > 1 else message.author
Note that this may be unreliable if you have messages deleted frequently in your guild.
This is untested, but you can modify as needed.
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
So I need my bot to print in which channel it sents a message. This is my code:
#client.command()
async def test(ctx):
await ctx.reply('hello', mention_author=True)
channel = #what goes here????
print('i said hello in', (channel))
I've tried all of these:
user = bot.get_user(user_id)
message.author.username
username = client.get_user(user_id)
The things you tried are referencing user id. I have not used discord.py in a while, but you will have to reference the channel id and not the user id. Maybe try:
ctx.message.channel.id
I can't remember if this returns an object or a string so you may have to format further to get the string.
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.