Counting invites for new members - python

I'm trying to get the possible invite for users that join on discord But it simply doesn't work and I don't know why. I'd expect it to output "couldn't find invite" for bots but instead it says nothing at all, not for users, nor bots. It should work to my knowledge. It scans every invite in the servers and stores them, then it checks if it goes up on the user that joins and outputs the embed.
def __init__(self, bot):
self.bot.loop.create_task(self.get_invites())
async def get_invites(self):
for guild in self.bot.guilds:
try:
guild_invites = {}
invites = await guild.invites()
for invite in invites:
guild_invites[invite.code] = invite
self.invites[guild] = guild_invites
except discord.errors.Forbidden:
pass
async def find_possible_invites(self,guild):
i = 1
while i < 11:
new = await guild.invites()
res = []
for invite in new:
try:
old_uses = self.invites[guild][invite.code].uses
except KeyError:
self.invites[guild][invite.code] = invite
if invite.uses >= 1:
res.append(invite)
continue
new_uses = invite.uses
if old_uses < new_uses :
self.invites[guild][invite.code] = invite
res.append(invite)
if res == []:
await asyncio.sleep(3)
i+=1
else:
break
return res
#commands.Cog.listener()
async def on_member_join(self,member):
server = await self.bot.get_guild(SERVER_ID)
possible_invites = await self.find_possible_invites(server)
channel = self.bot.get_channel(CHANNEL_ID)
whtspace= "** **"
embed = discord.Embed(color=0x21d3f3)
embed.title = "Member Joined!"
embed.add_field(name="Name",value=str(member))
embed.add_field(name="User ID:",value=member.id)
if len(possible_invites) == 1:
embed.add_field(name="Invite used",value=possible_invites[0].url,inline=True)
embed.add_field(name="Invite created by",value=str(possible_invites[0].inviter),inline=True)
embed.add_field(name="Number of uses",value=str(possible_invites[0].uses),inline=True)
elif len(possible_invites) > 1:
embed.add_field(name="Possible invites used:",value=whtspace,inline=False)
for i in possible_invites:
embed.add_field(name=whtspace,value=i.url,inline=False)
else:
await channel.send("Invite used could not be detected")
await channel.send(embed=embed)

Related

How to make cooldowns use the exceptions given

What i am specifically looking for is that i want is for the exception to be thrown on ("you must mention someone to x") and for the exception of if the person isn't in the guild to throw that message instead of using the cooldown.
#tasks.loop(count=1)
async def load_interaction_commands(self):
""" Load interaction commands. """
self.bot.interaction_commands = []
cooldown = commands.Cooldown(1, 600, commands.BucketType.user)
mapping = commands.CooldownMapping(cooldown)
for cmd, aliases in COMMANDS.items():
async def interaction_callback(cls, ctx, user: discord.Member):
if not user:
await ctx.send(f"You must mention someone to {ctx.command}.")
elif user == ctx.author:
await ctx.send("You can't {} yourself, silly!".format(ctx.command.name))
elif not discord.utils.find(lambda m: m.id == user.id, ctx.guild.members):
await ctx.send("you cant {} someone not in the server silly!")
else:
await cls.interact(ctx, user)
interaction_callback = commands.command(name=cmd, aliases=aliases)(interaction_callback)
interaction_callback._buckets = mapping
interaction_callback.__commands_cooldown__ = cooldown
interaction_callback.cog = self
self.bot.add_command(interaction_callback)
self.bot.interaction_commands.append(interaction_callback)

How do I get the content from an old message/command with my discord bot?

I am currently trying to edit an embed with a command. The edited embed has a clear structure. It is supposed to be an evaluation of the suggestion. For this I need the author and his suggestion from the previous command. Is it possible to take the content of the old embed and transfer it to the edited embed if you have two commands? In addition, it would be good to still count and insert the reactions at the message.
Here are my working approaches, except for the missing parts:
##commands.cooldown(1, 100, BucketType.user)
#commands.command(usage="<text>")
async def suggest(self, ctx, *, text: str = None):
"""This is the command for suggestions."""
if text is None:
await ctx.send("**You need to insert a text.**")
return self.suggest.reset_cooldown(ctx)
channel1 = self.bot.get_channel(812284187427864616)
if channel1 == ctx.channel:
channel = self.bot.get_channel(812283430707920906)
e = discord.Embed(color=discord.Colour.green())
e.description = f "**__submitter:__**\n {ctx.author}"
e.add_field(name="__Suggestion:__", value=f"{text}")
e.set_thumbnail(url=ctx.message.author.avatar_url)
e.timestamp = datetime.utcnow()
e.set_footer(text=f "UID: {ctx.author.id}")
feedback = await channel.send(embed=e)
await feedback.add_reaction("✅")
await feedback.add_reaction("❌")
await ctx.message.add_reaction("✅")
The approve command which actually edits the old embed and should insert "Suggestion", "Submitter" and count the reactions.
#commands.command()
async def approve(self, ctx, msg_id: int = None, channel: discord.TextChannel = None):
if not msg_id:
channel = self.bot.get_channel(812283430707920906) # the message's channel
msg_id = 998877665544332211 # the message's id
elif not channel:
channel = ctx.channel
msg = await channel.fetch_message(msg_id)
embed = discord.Embed()
embed.title = "Suggestion accepted"
embed.description = "*Here are all the important information*"
embed.add_field(name="Results", value="✅: MISSING COUNT / ❌: MISSING COUNT")
embed.add_field(name="Suggestion", value=f"MISSING SUGGESTION TEXT")
embed.add_field(name="Submitter:", value=f"MISSING SUBMITTER")
embed.add_field(name="Approved by:", value=f"{ctx.author.mention}")
await msg.edit(embed=embed)
await msg.clear_reactions()
To count the reactions I would use something like:
total_count = 0
for r in message.reactions:
total_count += r.count
EDIT:
This is the embed right now with showing Suggestion two times in different ways.
#commands.command()
async def approve(self, ctx, channel: discord.TextChannel, msgID: int):
try:
msg = await channel.fetch_message(msgID)
embed = msg.embeds[0]
submitter = embed.description[embed.description.find('\n'):]
suggestion = embed.fields[0].value
embed.title = "Suggestion accepted"
embed.description = "*Here are all the important information*"
embed.add_field(name="Results", value="✅: Test/ ❌: Test", inline=False)
embed.add_field(name="Suggestion", value=f"{suggestion}", inline=False)
embed.add_field(name="Submitter", value=f"{submitter}", inline=False)
embed.add_field(name="Approved by", value=f"{ctx.author.mention}", inline=False)
await msg.edit(embed=embed, reference=msgID)
await msg.clear_reactions()
except:
pass
msg = await channel.fetch_message(msg_id)
You can use the fetched message to get details about it.
try:
msg = await channel.fetch_message(msg_id)
embed = msg.embeds[0]
submitter = embed.description[embed.description.find('/n'):] # get submitter name
suggestion = embed.fields[0].value
# other stuff
except:
pass
# possible exceptions message not found, list index out of range(if wrong msg id with no embeds passed as arg)
References:
fetch_message
embeds
message

Find Guild ID in on_ready() - Discord.py

I'm trying to make a random fact send at certain times. It worked before I implemented a dict to hold the random facts and it should work now, the only problem I'm running into is I can't access the guild id inside the on_ready() event. Here is what I have:
async def func(self):
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
------> guild_id = NEED
channel = bot.get_channel(guild_settings[guild_id]["random_facts_channel_id"])
random_messages = guild_settings[guild_id]["random_facts"].value()
if random_messages is None:
await channel.send(f"You need to add a fact first.")
else:
random_messages = random.choice(random_messages)
await channel.send(random_messages)
#commands.Cog.listener()
async def on_ready(self):
print("Bot is ready.")
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
# Initializing scheduler
scheduler = AsyncIOScheduler()
hour = 12
minute = 0
for guild in bot.guilds:
guild_id = str(guild.id)
try:
hour = guild_settings[str(guild_id)]["random_facts_send_time"]["hour"]
minute = guild_settings[str(guild_id)]["random_facts_send_time"]["minute"]
except KeyError:
print(f"{guild_id} has a KeyError with the random fact feature.")
continue
# Sends "Your Message" at 12PM and 18PM (Local Time)
scheduler.add_job(self.func, CronTrigger(hour=hour, minute=minute, second="0"))
# Starting the scheduler
scheduler.start()
If I were able to somehow get the guild id then I'm sure it would work correctly, I just can't figure out how.
EDIT: This is what I have working so far
async def func(self):
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
for guild_id in guild_settings:
channel = bot.get_channel(guild_settings[guild_id]["random_facts_channel_id"])
if guild_settings[guild_id]["random_facts"] is not None:
random_facts = []
for values in guild_settings[guild_id]["random_facts"].values():
random_fact = values.split("\n")
random_facts += random_fact
try:
random_choice = random.choice(random_facts)
except IndexError:
continue
await channel.send(random_choice)
#commands.Cog.listener()
async def on_ready(self):
print("Bot is ready.")
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
# Initializing scheduler
scheduler = AsyncIOScheduler()
hour = 12
minute = 0
for guild in bot.guilds:
guild_id = str(guild.id)
try:
hour = guild_settings[str(guild_id)]["random_facts_send_time"]["hour"]
minute = guild_settings[str(guild_id)]["random_facts_send_time"]["minute"]
except KeyError:
print(f"{guild_id} has a KeyError with the random fact feature.")
continue
# Sends "Your Message" at 12PM and 18PM (Local Time)
scheduler.add_job(self.func, CronTrigger(hour=hour, minute=minute, second="0"))
# Starting the scheduler
scheduler.start()
If your bot is on only one server, you can simply get the guild object by using discord.utils.get.
async def func(self):
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
guild = discord.utils.get(bot.guilds, name="guild's name")
channel = bot.get_channel(guild_settings[guild.id]["random_facts_channel_id"])
random_messages = guild_settings[guild.id]["random_facts"].value()
if random_messages is None:
await channel.send(f"You need to add a fact first.")
else:
random_messages = random.choice(random_messages)
await channel.send(random_messages)
If it's on multiple servers, you have to loop through the guilds and send the messages one by one.
async def func(self):
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
for guild in bot.guilds:
channel = bot.get_channel(guild_settings[guild.id]["random_facts_channel_id"])
random_messages = guild_settings[guild.id]["random_facts"].value()
if random_messages is None:
await channel.send(f"You need to add a fact first.")
else:
random_messages = random.choice(random_messages)
await channel.send(random_messages)
As #ŁukaszKwieciński said, you can just do this:
async def func():
for guild in client.guilds:
print(guild.id) #Do something with each ID
Or you can waste time and memory doing this (which was my initial approach 😔):
guildIDs = set()
#client.event
async def on_ready():
for guild in client.guilds:
guildIDs.add(guild.id)
#client.event
async def on_guild_join(guild):
guildIDs.add(guild.id)
#client.event
async def on_guild_remove(guild):
guildIDs.remove(guild.id)
guildIDs is an integer set that will contain all the IDs of all the guilds that the bot is present in. The reason I used a set is to prevent repetition of IDs.
You can then use each ID in a function like this:
async def func():
for guildID in guildIDs:
print(guildID) #Do something with each ID
I recommend using tasks extension of discord.py
If you'd like to get the code, here it is.
#tasks.loop(hours=10)
async def func():
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
for guild_id in guild_settings:
channel = bot.get_channel(guild_settings[guild_id]["random_facts_channel_id"])
if guild_settings[guild_id]["random_facts"] is not None:
random_facts = []
for values in guild_settings[guild_id]["random_facts"].values():
random_fact = values.split("\n")
random_facts += random_fact
try:
random_choice = random.choice(random_facts)
except IndexError:
continue
await channel.send(random_choice)
#tasks.loop(minutes=15)
async def update_interval():
hour = 12
minute = 0
for guild in bot.guilds:
guild_id = str(guild.id)
try:
hour = guild_settings[str(guild_id)]["random_facts_send_time"]["hour"]
minute = guild_settings[str(guild_id)]["random_facts_send_time"]["minute"]
except KeyError:
print(f"{guild_id} has a KeyError with the random fact feature.")
continue
func.cancel()
func.change_interval(hours=hour, minutes=minute)
I hope this is helpful.

How can I make top 10 command work, where it grabs the top 10 in the JSON?

Here's my current code:
import discord
from discord.ext import commands
class Fun:
def __init__(self, client):
self.client = client
#commands.command(aliases=["lb"], pass_context=True)
async def leaderboard(self, ctx):
experience = self.getexperience()
with open('users.json') as f:
data = json.load(f)
lines = sorted(data.items(), key=operator.itemgetter(1), reverse=True) # sorts lines by balance
lb = [] # leaderboard
for line in lines: # each line in file
user = self.bot.get_user(id=int(line[0])) # grab user object from ID
if not user:
user = await self.bot.fetch_user(int(line[0]))
lb.append(f"{user.name} | {format(line[1], ',d')} {experience}\n") # add username and balance to leaderboard
limitedMsgs = [] # array for each page
pageCount = math.ceil(len(lb) / 10) # pageCount = number of users / 10 users per page
for x in range(0, pageCount): # for every page
limitedMsgs.append("".join(lb[x*10:x*10+10])) # add those 10 users to 1 page
currPage = 0
embed = discord.Embed(color=0xdfe324, description=limitedMsgs[0])
embed.set_footer(text=f"Page: {currPage + 1} of {pageCount}")
msg = await self.client.say(embed=embed)
def check(reaction, user):
return (user == ctx.message.author) and (str(reaction.emoji) == '⬅' or str(reaction.emoji) == '➡')
while(True):
if pageCount > 1:
if currPage == 0: # if first page
await msg.add_reaction("➡")
print(pageCount)
elif (currPage + 1) == pageCount: # if last page
await msg.add_reaction("⬅")
else: # if not first nor last
await msg.add_reaction("➡")
await msg.add_reaction("⬅")
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=check) # wait for user reaction
except asyncio.TimeoutError:
break # end while loop if no user reaction
if str(reaction.emoji) == '⬅':
currPage = currPage - 1
if str(reaction.emoji) == '➡':
currPage = currPage + 1
embed = discord.Embed(color=0xdfe324, description=limitedMsgs[currPage])
embed.set_footer(text=f"Page: {currPage + 1} of {pageCount}")
await msg.clear_reactions()
await msg.edit(embed=embed)
await msg.clear_reactions() # clear reactions after while loop
def setup(client):
client.add_cog(Fun(client))
heres the error:
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'Fun' object has no attribute 'getexperience'
Like I said before, I want it to grab the top 10 in the JSON, then order it into first to last but I'm stuck on this part. Most of this code is done; it's just the grabbing of the xp from the JSON. Any help would be appreciated. Thanks.
The error seems to be because of the line experience = self.getexperience(), as the full traceback should have pointed out. Your class/cog doesn't have the method, getexperience.

Background Loop with Reactions Discord.Py

I have a background loop that will spit out an emoji every X amount of minutes with a reaction attached to it. I want for when someone presses on the reaction of the message, it will delete the message and then send another message saying "messageauthor has grabbed the loot" and then add the amount to the cash json file.
Right now, my code is making the background loop work, but I am not sure how to grab the message.author.id in regards to the background loop so I can reference it in on_reaction_add. The current code is making the bot react once when it spits out the background loop and then again in on_reaction_add. I'm trying to make it wait for a user to react to the background loop message with the same emoji and not the bot.
client = discord.Client()
emoji_msg_grab = {}
try:
with open("cash.json") as fp:
cash = json.load(fp)
except Exception:
cash = {}
def save_cash():
with open("cash.json", "w+") as fp:
json.dump(cash, fp, sort_keys=True, indent=4)
def add_dollars(user: discord.User, dollars: int):
id = user.id
if id not in cash:
cash[id] = {}
cash[id]["dollars"] = cash[id].get("dollars", 0) + dollars
print("{} now has {} dollars".format(user.name, cash[id]["dollars"]))
save_cash()
async def background_loop():
await client.wait_until_ready()
while not client.is_closed:
channel = client.get_channel("479919577279758340")
emojigrab = '💰'
emojimsgid = await client.send_message(channel, emojigrab)
await client.add_reaction(emojimsgid, "💵")
user_id = emojimsgid.author.id
emoji_msg_grab[user_id] = {"emoji_msg_id": emojimsgid.id, "emoji_user_id": user_id}
await asyncio.sleep(600)
#client.event
async def on_reaction_add(reaction, user):
msgid = reaction.message.id
chat = reaction.message.channel
if reaction.emoji == "💵" and msgid == emoji_msg_grab[user.id]["emoji_msg_id"] and user.id == emoji_msg_grab[user.id]["emoji_user_id"]:
emoji_msg_grab[user.id]["emoji_msg_id"] = None
await client.send_message(chat, "{} has grabbed the loot!".format(user.mention))
await client.delete_message(reaction.message)
add_dollars(user, 250)
client.loop.create_task(background_loop())
I would use Client.wait_for_reaction instead of on_reaction_add:
async def background_loop():
await client.wait_until_ready()
channel = client.get_channel("479919577279758340")
while not client.is_closed:
emojigrab = '💰'
emojimsg = await client.send_message(channel, emojigrab)
await client.add_reaction(emojimsg, "💵")
res = await client.wait_for_reaction(emoji="💵", message=emojimsg, timeout=600,
check=lambda reaction, user: user != client.user)
if res: # not None
await client.delete_message(emojimsg)
await client.send_message(channel, "{} has grabbed the loot!".format(res.user.mention))
await asyncio.sleep(1)
add_dollars(res.user, 250)

Categories

Resources