Cant get values from channel.members (discord.py) - python

I can currently print a list of users from a specific voice channel whenever someone joins/leaves, but then when I try to check if a specific member is in that list it doesn't work. I never get the test print, even though that user is in the voice channel and I can see their id (174934831444583193) in the channel.members print. (I also do have the bot token at the bottom, but didn't include it for obvious reasons)
import discord
client = discord.Client()
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
#client.event
async def on_voice_state_update(member, before, after):
channel = client.get_channel(957709914111057873)
print(channel.members)
if "174934831444583193" in channel.members:
print('test')
intents = discord.Intents().all()

You never get the test print because you're checking if a string is in a list of discord.Member instances, which is always false. Id's are also ints, not strings, so even if you had just the id's it still wouldn't work.
Loop over the list of members and check if any of their id's are equal to the one you want to check, as an int.
if any(member.id == int(your_id_here) for member in channel.members):
....

Related

Python discord bot deletes every message except specific ones

it's my first time coding something and making a discord bot. But I've been trying to figure it out what is wrong with this code for a long time. I want one channel to only able to type specific words and delete everything else, but in this code no matter what i do it deletes everything, cheers
import os
import discord
client = discord.Client(intents=discord.Intents.default())
intents = discord.Intents(message_content=True)
#client.event
async def on_ready():
print('BOTCADO ONLINE')
#client.event
async def on_message(message):
if (message.channel.id == 'id'):
if (message.content != "cado"):
await message.delete()
client.run(os.getenv('TOKEN'))
i tried basically everything
to get it to work I changed:
client = discord.Client(intents=discord.Intents.default())
intents = discord.Intents(message_content=True)
to:
intents = discord.Intents().all()
client = commands.Bot(command_prefix="$", intents=intents)
I would only use the intents you need to use however. I only put all for testing proposes.
Also in this line of code, if (message.channel.id == 'id'):, message.channel.id is being set equal to a string. Message ID's are integers. You have to remove that conditional / replace it. You could set the message ID that corresponds with the channel you want to delete messages in by declaring the id as a variable first at the top of your code. id = < THE CHANNEL ID >

I want to make the discord bot to not react to a certain user when they edit a message, but it doesn't work

I am trying to make it so a discord bot won't react to a certain user when they make say an edit on a message, however it doesn't quite work. My current code is
from discord.ext import commands
id = id
bot = commands.Bot(command_prefix='!')
#bot.event
async def on_message(message):
if message.author.id == id:
pass
else:
#bot.event
async def on_message_edit( before, after):
await before.channel.send(
f'Before: {before.content}\n' f'After: {after.content}'
bot.run('token')
I have to do this because I just put if message.author.id == id in the on_message_edit it would return as false. I also tried to also use variables like if message.author.id == id then something = True, but it just won't work. But this doesn't quite work either, any suggestions as to how I could fix this?
First of all, avoid naming strings like id since it's used by discord.py already and can be confusing for python. I've changed it for banned_id just for this example.
Secondly, message.author.id is an integer value, so make sure you compare it to another integer value. If you make something like:
banned_id = 142345353455345
python treats it just like a string of characters, not a number. You can make it a number using int().
For me this code works just fine:
#bot.event
async def on_message_edit(before, after):
banned_id = int(YOUR_ID_HERE)
if before.author.id == banned_id:
await before.channel.send("you wish")
else:
await before.channel.send(f'Before: {before.content}\n' f'After: {after.content}')

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.

Change discord bot nickname (discord.py)

I have a simple issue that I'm yet to figure out, how can I change my bot's own nickname, because I want to display information instead of its nickname continuously.
tried this :
await bot.user.edit(username=price)
but this actually changes the username which is not allowed to be done multiple times.
async def status_update():
while True:
price = str(get_BTC_price(ws))
await bot.user.edit(username=price)
await asyncio.sleep (2)
#bot.event
async def on_ready():
bot.loop.create_task(status_update())
Thanks
Nicknames are tied to Guild Members. In order to change your bot's nickname, you must
Get the Guild Member object for your bot
Use <Member>.edit(nick=nick)
Note that this will only change the nickname for one server. If you wanted it to be global you would have to loop through each guild and perform the operation (expensive). You may want to use the status box instead to convey this information.

invite.uses returning None

I'm trying to figure out who invited a user when they join, but when I try to get the number of times an invite has been used I keep getting None. Here's my code:
#client.event
async def on_ready(self):
global before_invites
before_invites = []
for guild in self.client.guilds:
for invite in await guild.invites():
invite = await self.client.get_invite(invite)
x = [invite.url, invite.uses, invite.inviter.id]
before_invites.append(x)
print(before_invites)
which then prints out [['http://discord.gg/xxxxxxx', None, 01234567890123456789], ['http://discord.gg/xxxxxxx', None, 01234567890123456789], ...]
So far I've double checked and the bot has all permissions in the server and made sure that there are invites for the server that have been used. Can only self accounts see invite uses?
The problem is that you're calling get_invite instead of using the Invite from Guild.invites. get_invite uses the GET Invite endpoint of the Discord API, which only returns the invite object, not including it's metadata. By contrast, Guild.invites uses the GET Channel Invites endpoint, which does return the metadata objects.
Just use the invites from Guild.invites directly:
#client.event
async def on_ready(self):
global before_invites
before_invites = []
for guild in self.client.guilds:
for invite in await guild.invites():
x = [invite.url, invite.uses, invite.inviter.id]
before_invites.append(x)
print(before_invites)

Categories