How to prevent bot from spamming embeds? Discord.py - discord

I have discord bot and it checks whether streamer is live or not. And I have a function that prevents it from spamming when someone is live:
if status is True:
async for message in channel.history(limit=2000):
if message.content == f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`":
break
else:
async for member in guild.fetch_members(limit=None):
if member.id == int(user_id):
await member.add_roles(role)
await channel.send(f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`")
print(f"{user} started streaming. Sending a notification.")
break
And I wonder if there is the way to do same with embeds, but idk what to use. I though I can use if embed = twitch_embed: break instead of if message.content.
I want to send both message and embed when someone is live one time per stream each like mee6 does:
And I want to combine message and embed antispam function in the code. Please help! All my code here if you need:
import os
import json
import discord
import requests
from discord.ext import tasks, commands
from server import ping
from twitchAPI.twitch import Twitch
from discord.utils import get
intents = discord.Intents.all()
bot = commands.Bot(command_prefix='$', intents=intents)
# Authentication with Twitch API.
client_id = os.getenv('client_id')
client_secret = os.getenv('Dweller_token')
twitch = Twitch(client_id, client_secret)
twitch.authenticate_app([])
TWITCH_STREAM_API_ENDPOINT_V5 = "https://api.twitch.tv/kraken/streams/{}"
API_HEADERS = {
'Client-ID': client_id,
'Accept': 'application/vnd.twitchtv.v5+json',
}
user_info = twitch.get_users(logins=['turb4ik'])
user_id = user_info['data'][0]['id']
print(user_info)
# Returns true if online, false if not.
def checkuser(user):
try:
userid = twitch.get_users(logins=[user])['data'][0]['id']
url = TWITCH_STREAM_API_ENDPOINT_V5.format(userid)
try:
req = requests.Session().get(url, headers=API_HEADERS)
jsondata = req.json()
if 'stream' in jsondata:
if jsondata['stream'] is not None:
return True
else:
return False
except Exception as e:
print("Error checking user: ", e)
return False
except IndexError:
return False
# Executes when bot is started
#bot.event
async def on_ready():
# Defines a loop that will run every 10 seconds (checks for live users every 10 seconds).
#tasks.loop(seconds=10)
async def live_notifs_loop():
# Opens and reads the json file
with open('streamers.json', 'r') as file:
streamers = json.loads(file.read())
# Makes sure the json isn't empty before continuing.
if streamers is not None:
# Gets the guild, 'twitch streams' channel, and streaming role.
guild = bot.get_guild(690995360411156531)
channel = bot.get_channel(785523710362124298)
role = get(guild.roles, id=835581408272580649)
# Loops through the json and gets the key,value which in this case is the user_id and twitch_name of
# every item in the json.
for user_id, twitch_name in streamers.items():
print("checking" + " " + str(twitch_name))
# Takes the given twitch_name and checks it using the checkuser function to see if they're live.
# Returns either true or false.
status = checkuser(twitch_name)
# Gets the user using the collected user_id in the json
user = bot.get_user(int(user_id))
# Makes sure they're live
if status is True:
# Checks to see if the live message has already been sent.
#limit = 0
#limit += 1
async for message in channel.history(limit=2000):
# If it has, break the loop (do nothing).
if message.content == f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`":
break
# If it hasn't, assign them the streaming role and send the message.
else:
# Gets all the members in your guild.
async for member in guild.fetch_members(limit=None):
# If one of the id's of the members in your guild matches the one from the json and
# they're live, give them the streaming role.
if member.id == int(user_id):
await member.add_roles(role)
# Sends the live notification to the 'twitch streams' channel then breaks the loop.
'''twitch_embed = discord.Embed(
title=f":red_circle: **LIVE**\n{user.name} is now streaming on Twitch!",
color=0xac1efb
)'''
#twitch_embed.add_field(name='**Game**', value='add value',inline=True)
#twitch_embed.add_field(name='**Viewers**', value='add value',inline=True)
#twitch_embed.set_image(url=f'\nhttps://www.twitch.tv/{twitch_name}')
await channel.send(f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`")
#await channel.send(embed=twitch_embed)
print(f"{user} started streaming. Sending a notification.")
break
# If they aren't live do this:
else:
# Gets all the members in your guild.
async for member in guild.fetch_members(limit=None):
# If one of the id's of the members in your guild matches the one from the json and they're not
# live, remove the streaming role.
if member.id == int(user_id):
await member.remove_roles(role)
# Checks to see if the live notification was sent.
async for message in channel.history(limit=200):
# If it was, delete it.
if str(user.mention) in message.content and "is now streaming" in message.content:
await message.delete()
# Start your loop.
live_notifs_loop.start()
# Command to add Twitch usernames to the json.
#bot.command(name='addtwitch', help='Adds your Twitch to the live notifs.', pass_context=True)
#commands.has_permissions(administrator = True)
async def addtwitch(ctx, twitch_name):
# Opens and reads the json file.
with open('streamers.json', 'r') as file:
streamers = json.loads(file.read())
# Gets the users id that called the command.
user_id = ctx.author.id
# Assigns their given twitch_name to their discord id and adds it to the streamers.json.
streamers[user_id] = twitch_name
# Adds the changes we made to the json file.
with open('streamers.json', 'w') as file:
file.write(json.dumps(streamers))
# Tells the user it worked.
await ctx.send(f"Added {twitch_name} for {ctx.author} to the notifications list.")
ping()
bot.run(os.getenv('token'))
Edit:
Please say if something is not right or you don't understand the question instead of just closing it.

To send the embed in the same message you can use content an example would be:
await channel.send(content=f"`Hey #everyone, {twitch_name} is live! Check it out:" + f"https://www.twitch.tv/{twitch_name}`", embed=twitch_embed)
There would be no change in your check function as the message.content would be the same text message just with an embed as well.
You should really check if the stream has finished either by web scraping or some API instead of checking message content. If the stream finishes 3 hours ago but you only check for 2 streamers there won't be a change unless you have thought of this already.

Related

tokens.txt not found. Please create the file and add your tokens

import discord
import asyncio
import aiohttp
client = discord.Client()
# Open the tokens.txt file and read the tokens into a list
try:
with open("tokens.txt", "r") as f:
tokens = f.read().split("\n")
except FileNotFoundError:
print("tokens.txt not found. Please create the file and add your tokens.")
exit()
# Keep track of the current token index
token_index = 0
# Webhook URL
webhook_url = "https://discord.com/api/webhooks/ENTERURWEBHOOK"
#client.event
async def on_ready():
# Read the file containing the Discord IDs
try:
with open("newusers.txt", "r") as f:
newusers = f.read().split("\n")
except FileNotFoundError:
print("newusers.txt not found. Please create the file and add the Discord IDs.")
exit()
try:
with open("defaultpfp.txt", "r") as f:
defaultpfp = f.read().split("\n")
except FileNotFoundError:
print("defaultpfp.txt not found. Please create the file and add the Discord IDs.")
exit()
try:
with open("both.txt", "r") as f:
both = f.read().split("\n")
except FileNotFoundError:
print("both.txt not found. Please create the file and add the Discord IDS.")
# Iterate through the IDs
for id in newusers:
if not id.strip():
continue
# Get the member from the Discord API
try:
member = await client.fetch_user(int(id))
except ValueError:
print(f"Invalid ID: {id}. Skipping...")
continue
while True:
try:
#Use the current token
await client.start(tokens[token_index])
#check if the member dms are open
if member.dm_channel is not None:
async with aiohttp.ClientSession() as session:
webhook = discord.Webhook.from_url(webhook_url,adapter=discord.AsyncWebhookAdapter(session))
await webhook.send(f":date: {member.name}#{member.discriminator} ({member.id})")
break
except discord.errors.HTTPException as e:
if e.status == 429:
token_index = (token_index+1) % len(tokens)
await asyncio.sleep(e.response.headers["Retry-After"])
else:
raise e
for id in defaultpfp:
if not id.strip():
continue
try:
member = await client.fetch_user(int(id))
except ValueError:
print(f"Invalid ID: {id}. Skipping...")
continue
while True:
try:
await client.start(tokens[token_index])
if member.dm_channel is not None:
async with aiohttp.ClientSession() as session:
webhook = discord.Webhook.from_url(webhook_url, adapter=discord.AsyncWebhookAdapter(session))
await webhook.send(f":new: {member.name}#{member.discriminator} ({member.id})")
break
except discord.errors.HTTPException as e:
if e.status == 429:
token_index = (token_index+1)% len (tokens)
await asyncio.sleep(e.response.headers["Retry-After"])
else:
raise e
for id in both.txt:
if not id.strip():
continue
try:
member = await client.fetch_user(int(id))
except ValueError:
print(f"Invalid ID: {id}. Skipping...")
continue
while True:
try:
await client.start(tokens[token_index])
if member.dm_channel is not None:
async with aiohttp.ClientSession() as session:
webhook = discord.Webhook.from_url(webhook_url, adapter=discord.AsyncWebhookAdapter(session))
await webhook.send(f":date: + :new: {member.name}#{member.discriminator} ({member.id})")
break
except discord.errors.HTTPException as e:
if e.status == 429:
token_index = (token_index+1)%len (tokens)
await asyncio.sleep(e.response.headers["Retry-After"])
else:
raise e
I am making a Discord Self-Bot (I know they are against Discord TOS) that checks through 3 files containing Discord IDs (1 Line is 1 ID on every one of them)
newusers.txt
defaultpfp.txt
both.txt
Now then I also have tokens.txt (1 Line is 1 Token).
I use 1 token a time from 'tokens.txt' to check if the users from the 3 text files have their Discord DMs open. If a user from newusers.txt have their DMs open then I would like the script to send me that users information (username&tag ID) and an special emoji so I know it's coming from newusers.py, the :date: emoji from Discord to my webhook. The same way as before, I want for every user from defaultpfp.txt who has their DMs open for his information to be sent to me in my webhook with the :new: emoji this time. Lastly for both.txt for people with dms open from there , I want to be sent their information with ':new: and :date:' emojis. Also I have a added a feature where, every time the token seems to get rate limited the script will go on with the next token from tokens.txt.
The error am getting here is tokens.txt not found. Please create the file and add your tokens.
I am honestly not sure what is wrong, the tokens.txt file does exist in path and tokens are also added in it (Tokens are also in the guild server the other users from the 3 text files are). Any help is appreciated, I pray that someone can help me out as I have been struggling for quite some time with this.

discord.py bot detecting username changes

trying to make a small bot for my server. it kinda works but if the user removes the nickname, the bot returns a None value. how can i fix this?
the goal is to have a bot detect if a user has TBU in the nickname or username, if it does, add him to a role in discord (havnt added this yet, need to look into it) but if the change removes TBU in the name, remove the role.
#client.event
async def on_member_update(before, after):
if before.nick != after.nick: # to only run on status
embed = discord.Embed(title=f"Changed nick")
embed.add_field(name='User', value=before.mention)
embed.add_field(name='Before', value=before.nick)
embed.add_field(name='After', value=after.nick)
# send to admin or channel you choose
channel = client.get_channel(526517582963146762) # notification channel
await channel.send(embed=embed)
admin = client.get_user(174749290902716417) # admin to notify
await admin.send(embed=embed)
if "TBU" in after.nick:
admin = client.get_user(174749290902716417) # admin to notify
await admin.send(embed=embed)
if "TBU" in before.nick and not "TBU" in after.nick:
admin = client.get_user(174749290902716417) # admin to notify
await admin.send(embed=embed)
When a user has no nickname nick returns None.
Change if before.nick != after.nick to if before.nick != after.nick and after.nick is not None, this should work.

discord.py bot repeating messages

hi i am trying to make a discord.py bot so i can have a gif chat channel but when someone types a message in that channel then my bot starts repeating his message, pls help if u know.
my code:
#client.event
async def on_message(message):
with open("gif_chater.json", "r+") as file:
data=json.load(file)
if str(message.channel.id) in data:
if message.content.startswith("https://tenor.com/"):
print("wOw")
if not message.content.startswith("https://tenor.com/") and not message.author.id == "760083241133932544":
await message.channel.send("lol pls use gifs in this channel : )")
await message.delete()
exit
The on_message event
The issue is that the bot is constantly responding to itself, and it's because the on_message event triggers not just when users send a message but also when the bot sends a message. As such, once it tells the user that they must only post tenor gifs, it reacts to its own message and goes into an infinite loop, posting and deleting its responses.
Preventing the bot from responding to itself
To prevent the bot from responding to it's own messages, you should add a check at the start of the event like in the discord.py docs:
#client.event
async def on_message(message):
if message.author == client.user:
return
...
Also, the ID check at the end
The last condition in your code before it decides to send a message is checking the ID of the messenger (not message.author.id == "760083241133932544"). I don't know whether it's meant to avoid deleting you or the bot's messages but regardless, the check itself is bugged. message.author.id returns an integer but is then being compared to a string, and due to the conflicting types, will always return False.
To fix it, change your ID to an integer by removing the quotes: not message.author.id == 760083241133932544. As well, you should use the not-equals operator != instead of not to improve readability: message.author.id != 760083241133932544.
Also, since you already checked if the message starts with the website link, you can use an elif statement instead of rechecking the condition, since else/elif guarantees that the previous condition was false (aka, that the message didn't start with the website link):
if message.content.startswith("https://tenor.com/"):
print("wOw")
elif message.author.id != 760083241133932544:
await message.channel.send("lol pls use gifs in this channel : )")
await message.delete()
Fixes combined
With the new changes, your function could look something like this:
#client.event
async def on_message(message):
# Don't respond to the bot's own messages
if message.author == client.user:
return
with open("gif_chater.json") as file:
data = json.load(file)
if str(message.channel.id) in data:
if message.content.startswith("https://tenor.com/"):
print("wOw")
elif message.author.id != 760083241133932544:
await message.channel.send("lol pls use gifs in this channel : )")
await message.delete()

How can I get this command to check for messages sent globally not only in one channel

I've been trying to get this command to check for all the messages sent by the user in the whole server for ages. I came up with a command that checks for the messages sent by a user in 1 channel only.
#client.command(aliases=["m"])
async def messages(ctx, user: discord.Member):
channel = ctx.message.channel
counter = 0
async for message in channel.history():
if message.author == user:
counter += 1
Note that no matter how you do this, if you're using the ENTIRE server history, it will take some time for the bot to go through it all. With that being known, if this is something you still wish to proceed with here is a solution.
#client.command(aliases=["m"])
async def messages(ctx, user: discord.Member):
counter = 0
for channel in guild.channels:
if isinstance(channel, discord.TextChannel):
async for message in channel.history(limit=None):
if message.author.id == user.id:
counter += 1
Also note I'm comparing IDs instead of the member object itself, this is because that object can change, this simply prevents that.

Trouble with bot.wait_for() Discord.py

So modern documentation on the bot.wait_for() coroutine is not super detailed, and I'm having trouble getting it to work with reactions. Would appreciate feedback.
Python 3 with Discord.py
## Test Role Add
#kelutralBot.command(name='testreaction')
async def testReaction(ctx):
member = ctx.message.author
message = await ctx.send("This is a test message.")
emojis = ['\u2642','\u2640','\u2716']
for emoji in emojis:
await message.add_reaction(emoji)
def check(reaction, user):
return user == message.author and str(reaction.emoji) == '\u2642'
try:
reaction, user = await kelutral.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await ctx.send("Window has passed to self-assign pronouns. Please DM a mod if you would still like to do so.")
else:
print(reaction)
male = get(member.guild.roles, name="He/Him")
await member.add_roles(male)
print("Assigned " + member.name + " He/Him pronouns.")
Two things were wrong.
First, don't use Client and Bot in the same command. Bot is sufficient for both.
Second, Unicode Emoji for Discord are treated as '\U000#####', which was the biggest problem.
Once we solved that, everything worked as intended.
The problem here is that you are checking if the person who sent the reaction is the author of the message that contains the message, which is only satisfied by the bot that reacted. Also consider using a role ID instead (server settings > roles > left click role > right click role > copy ID). You also want to be consistent with kelutral or kelutral Bot throughout the command.
#kelutralBot.command(name='testreaction')
async def testReaction(ctx):
member = ctx.message.author
message = await ctx.send("This is a test message.")
emojis = ['\u2642','\u2640','\u2716']
for emoji in emojis:
await message.add_reaction(emoji)
def check(reaction, user):
return user == member and str(reaction.emoji) == '\u2642' # check against the member who sent the command, not the author of the message
try:
reaction, user = await kelutralBot.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await ctx.send("Window has passed to self-assign pronouns. Please DM a mod if you would still like to do so.")
else:
print(reaction)
male = ctx.message.guild.get_role(ROLE_ID_GOES_HERE) # put your role ID here #
await member.add_roles(male)
print(f"Assigned {member.name} He/Him pronouns.")
Keep in mind your code only works for the "male" role, you have to implement a different check function to use it for everything.

Resources