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)
Related
this is the code. if you want any other info i can provide you with it, but as for right now this is the only place that i had hopes on. im literally stuck.
import os, json
from dotenv import load_dotenv, find_dotenv
import discord
from discord.ext import commands, tasks
import logging
import binance
from discord import Member
from discord.ext.commands import has_permissions, MissingPermissions
from binance import Client, ThreadedWebsocketManager, ThreadedDepthCacheManager
logger = logging.getLogger('discord')
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler(filename = 'discord.log', encoding='utf-8', mode='w')
handler.setFormatter(logging.Formatter('%(asctime)s:%(name)s:&(message)s'))
logger.addHandler(handler)
load_dotenv(find_dotenv())
token = os.getenv("DISCORD_TOKEN")
channel_id = os.getenv('CHANNEL_ID')
binance_api_key = os.getenv('BINANCE_API_KEY')
binance_api_secret = os.getenv('BINANCE_API_SECRET')
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents, command_prefix='..')
binanceClient = Client(binance_api_key, binance_api_secret)
FAV_LIST = {}
with open('FAV_LIST.json') as f:
FAV_LIST = json.load(f)
def get_future_position(symbol):
position = None
positions = list(filter(lambda f:(f['symbol']==symbol), binanceClient.futures_account()['positions']))
if positions:
position = positions[0]
return position
#client.event
async def on_ready():
print(f'We have logged in as {client.user}')
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('hello'):
await message.channel.send('Hello!')
#commands.command()
async def add_fav(ctx, account, symbol):
FUT_SYMBOLS = [sym['symbol'] for sym in binanceClient.futures_exchange_info()['symbols']]
SPOT_SYMBOLS = [sym['symbol'] for sym in binanceClient.get_all_tickers()]
if account.upper() == "FUT":
if symbol in FUT_SYMBOLS:
FAV_LIST['FUTURES'][symbol] = {}
else:
await ctx.send("Provided SYMBOL or CRYPTO is not available in Futures")
elif account.upper() == "SPOT":
if symbol in SPOT_SYMBOLS:
FAV_LIST['SPOT'][symbol] = {}
else:
await ctx.send("Provided SYMBOL or CRYPTO is not available in SPOT")
else:
await ctx.send('Provided Account Type is not valid. Please use FUT for Futures and SPOT for spot')
with open('FAV_LIST.json','w') as f:
json.dump(FAV_LIST, f)
#commands.command()
async def favs(ctx):
message = "FUTURES FAVOURITE LIST\n"
for i, symbol in enumerate(FAV_LIST['FUTURES'].keys()):
message += str(i+1) + ". " + symbol + "--> Last Price: "+ binanceClient.get_ticker(symbol=symbol)['lastPrice']+"\n"
message += "\n\nSPOT FAVOURITE LIST"
for i, symbol in enumerate(FAV_LIST['SPOT'].keys()):
message += str(i+1) + ". " + symbol + "--> Last Price: "+ binanceClient.get_ticker(symbol=symbol)['lastPrice']+ "\n"
await ctx.send(message)
#commands.command()
async def fubln(ctx):
balance_list = binanceClient.futures_account_balance()
message = "-"*35 + "\n"
message += "-"*3 + "ACCOUNT BALANCE" + "-"*3 + "\n"
message += "-"*35 +"\n"
for balance in balance_list:
message += balance['asset']+" : "+balance['balance']+"\n"
message += "-"*35
await ctx.send(message)
#tasks.loop(seconds=60)
async def futures_position_alerts():
futures_info = binanceClient.futures_account()
positions_info = binanceClient.futures_position_information()
positions = futures_info['positions']
message_channel = await client.fetch_channel(channel_id)
print(f"Got channel {message_channel} for {channel_id}")
if float(futures_info['totalMaintMargin'])/float(futures_info['totalMarginBalance']) > 40.0:
await message_channel.send("Your positions' Margin Ratio is greater than 40%. Please consider taking a look at it.")
for position in positions:
symbol = position['symbol']
alert = False
message = "------"+symbol+" POSITION ALERT!------\n"
position_info = list(filter(lambda f:(f['symbol']==symbol),positions_info))[0]
if float(position_info['positionAmt']) != 0.0:
if float(position['unrealizedProfit']) < -1.0 :
message += "Unrealized Profit is going down! LOSS : "+ str(position['unrealizedProfit']) +"\n"
alert = True
if (float(position_info['markPrice'])-float(position_info['liquidationPrice']))/(float(position_info['entryPrice'])-float(position_info['liquidationPrice'])) <= 0.4:
message += "Mark price is moving closer to Liquidation Price. Your position may be liquidated soon.\n Mark Price:"+ str(position_info['markPrice']) +"\n Liquidation Price:"+str(position_info['liquidationPrice'])+"\n"
alert = True
if alert:
await message_channel.send(message)
#futures_position_alerts.before_loop
async def before():
await client.wait_until_ready()
print("Finished waiting")
futures_position_alerts.start()
##tasks.loop(seconds=60)
#async def favs_info():
# message = "INFO of Favourite Crytos\n\n"
# message += "FUTURES\n"
# for i, symbol in enumerate(FAV_LIST['FUTURES'].keys()):
# position = get_future_position(symbol)
# message += str(i)+". "+position['symbol']+" --> unrealizedProfit : "+position['unrealizedProfit']
# message_channel = await client.fetch_channel(channel_id)
# print(f"Got channel {message_channel} for {channel_id}")
# await message_channel.send(message)
##favs_info.before_loop
#async def before():
# await client.wait_until_ready()
# print("Finished waiting")
#favs_info.start()
# MODERATION COMMANDS #
#commands.command()
#has_permissions(kick_members=True, administrator=True)
async def kick(ctx, member:discord.Member,*,reason=None):
guild = ctx.guild
memberKick = discord.Embed(title='Kicked', description = f'You have been kicked from {guild.name} for {reason}')
await member.kick(reason=reason)
await ctx.send(f'User {member} has been kicked.')
#commands.command()
#has_permissions(ban_members=True, administrator=True)
async def ban(ctx, member:discord.Member,*,reason=None,):
guild = ctx.guild
memberBan = discord.Embed(title = 'Banned', description=f'You were banned from {guild.name} for {reason}')
await member.ban(reason=reason)
await ctx.send(f'User {member} has been banned.')
await member.send(embed=memberBan)
#commands.command()
#has_permissions(ban_members=True, administrator=True)
async def unban(self, ctx, *, member:discord.Member):
banned_users = await ctx.guild.bans()
member_name, member_discriminator = member.split('#')
for ban_entry in banned_users:
user = ban_entry.user
if (user.name, user.discriminator) == (member_name, member_discriminator):
await ctx.guild.unban(user)
await ctx.send(f'{user.name}#{user.discriminator} has been unbanned.')
return
#commands.command(pass_context=True)
#has_permissions(manage_messages=True)
async def mute(ctx,member:discord.Member, reason = None):
guild = ctx.guild
mutedRole = discord.utils.get(guild.roles, name='Muted')
memberMute = discord.Embed(title = 'Muted', description=f'You have been muted from {guild.name} for {reason}')
if mutedRole not in guild.roles:
perms = discord.Permissions(send_messages=False, speak=False)
await guild.create_role(name='Muted', permissions=perms)
await member.add_roles(mutedRole)
await ctx.send('Succesfuly created the [Muted] role and properly assigned it to the user.')
await ctx.add_role(member, mutedRole)
embed=discord.Embed(title='User muted!', description=f'**{0}** was muted by **{1}**!'.format(member, ctx.message.author, color=0xff00f6))
#commands.command(pass_context=True)
#has_permissions(manage_messages=True)
async def unmute(ctx, member:discord.Member, *, reason=None):
guild = ctx.guild
mutedRole = discord.utils.get(guild.roles, name = 'Muted')
memberUnmute = discord.Embed(title = 'Unmuted', description = f'You were unmuted from {guild.name} for {reason}')
await member.remove_roles(mutedRole)
await ctx.send(f'Unmuted {member.mention} for {reason}')
await member.send(embed=memberUnmute)
client.run(token)
#TRACEBACK#
Traceback (most recent call last):
File "c:\Users\arlit\Desktop\iceC-main\main.py", line 122, in <module>
futures_position_alerts.start()
File "C:\Users\arlit\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\tasks\__init__.py", line 398, in start
self._task = asyncio.create_task(self._loop(*args, **kwargs))
File "C:\Users\arlit\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 336, in create_task
loop = events.get_running_loop()
RuntimeError: no running event loop
sys:1: RuntimeWarning: coroutine 'Loop._loop' was never awaited
well i tried closing the loop but i didnt get it to work exactly how i wanted it to.
Edit: this is the whole code and the traceback below it. since it was too short. now ill have to add some random text since stack doesnt let me post too much code without adding some words.
You're calling futures_position_alerts.start() at the top level of your file. As the error message is telling you, you have to call it in an async context. There's an official example that you could look at.
Im currently trying to make the reaction in the bot response to turn off an moderation event which listens to messages being deleted. This will work as a filter so if the user dont want to log deleted messages they can turn it off by reacting. I've heard of the wait for and the dispatch function but i currently dont know how it works and couldnt find a tutorial. Any feedback would be amazing as i am a bit stuck and willing to listen to anyone.
Current Code;
import discord
from discord.ext import commands
import datetime
class log(commands.Cog):
def __init__(self,bot):
self.bot = bot
mess = False
#commands.Cog.listener()
async def on_reaction_add(self, reaction, user):
channel = reaction.message.channel
guild = channel.guild
message = reaction.message
if user != self.bot.user:
if reaction.emoji == '1οΈβ£':
check = discord.utils.get(guild.categories, name="Chilly Logs") # Find the Category
if not check: # If category doesnt exist create the logs
cat = await channel.guild.create_category_channel('Chilly Logs')
await channel.guild.create_text_channel("Message Logs", category=cat) # Message Logs
await channel.guild.create_text_channel("Other Logs", category=cat) # Other Logs
await channel.send('**Log Channels Were Succesfully Added!**')
await message.remove_reaction('1οΈβ£', user)
return
else:
await channel.send('**Log Channel Already Exists**')
await message.remove_reaction('1οΈβ£', user)
if reaction.emoji == '2οΈβ£':
channel1 = discord.utils.get(guild.channels, name="other-logs") # Other Logs
channel2 = discord.utils.get(guild.channels, name="message-logs") # Message Logs
category = discord.utils.get(guild.categories, name="Chilly Logs") # Category/Parent
if category is not None: # Deletes All The Channels
await channel1.delete()
await channel2.delete()
await category.delete()
await channel.send('**Logging Channels Have Been Removed**')
await message.remove_reaction('2οΈβ£', user)
else:
await channel.send('**Channels Either Dont Exist Or Have Been Renamed**')
await message.remove_reaction('2οΈβ£', user)
if reaction.emoji == 'β':
#commands.command()
async def test(self, ctx):
embed = discord.Embed(title = "Chilly Logging", description = "Chilly Will Log Any Edited, Deleted Messages. More Features & Flexibility Coming Soon", colour = discord.Color.blurple())
embed.add_field(name="βββLogging Commands βββ", value="1οΈβ£ - Turn On Server Logging", inline=True)
embed.add_field(name="β β", value= "2οΈβ£ - Delete & Turn Off Logging", inline=False)
msg = await ctx.send(embed=embed)
emoji = ['1οΈβ£', '2οΈβ£', 'β']
response = 3
for i in range(response):
await msg.add_reaction(emoji[i])
#commands.Cog.listener()
async def on_message_delete(self, message):
if x == True:
if not message.author.bot: # Checks for bot message
channel = message.channel.name # Channel the deleted message is from
logchannel = discord.utils.get(message.guild.channels, name='message-logs') # Finds the log channel
embed = discord.Embed(title="Message Log", description="", color= discord.Color.red()) # Embeds
embed.add_field(name="Message sent by {} has been deleted in `{}`" .format(message.author.display_name, channel), value=message.content, inline=True,)
embed.set_footer(text='User ID: {} | Message ID: {}' .format(message.author.id, message.id))
await logchannel.send(embed=embed) # Finally sends the embed to log channel
#commands.Cog.listener()
async def on_message_edit(self, before, after):
if not after.author.bot:
if before.content != after.content:
channel = after.channel.name # Channel the edited message is from
logchannel = discord.utils.get(after.guild.channels, name='message-logs') # Finds the log channel
embed = discord.Embed(title="Message Log", description="Message edited in `{}` by {}" .format(channel, after.author.display_name), color= discord.Color.red()) # Embeds
embed.add_field(name="Before", value=before.content, inline=True,)
embed.add_field(name="After", value=after.content, inline=False,)
embed.set_footer(text='User ID: {} | Message ID: {}' .format(after.author.id, before.id))
await logchannel.send(embed=embed) # Finally sends the embed to log channel
def setup(bot):
bot.add_cog(log(bot))```
You could use a global variable. Simply:
Toggle = True
#bot.command()
async def toggle(ctx):
global Toggle
# Toggles the variable.
Toggle = !Toggle
# Example event
#bot.event
async def on_message(message):
if Toggle:
# Event's code
else:
await message.channel.send("Function disabled")
fotddict = {}
#client.event
async def on_ready():
global fotddict
with open("factoftheday.json", "r") as f:
fotddict = json.load(f)
#client.command()
#commands.has_permissions(administrator=True)
async def fotd(ctx, channel : discord.TextChannel=None):
if channel is None:
embe=discord.Embed(title="<:redcross:781952086454960138>Error", description="**Please pass in all required arguments!**\nNeed help?** https://dsc.gg/otaysupport**", color=0x7289da)
await ctx.send(embed=embe)
else:
#are you sure embed
msg = await ctx.send(embed=embed)
def checkifnotbotfact(reaction, user):
return user != client.user
await msg.add_reaction('π‘')
reaction, user = await client.wait_for("reaction_add", timeout=60.0, check=checkifnotbotfact)
if str(reaction.emoji) == "π‘":
#confirm embed
global fotddict
fotddict[str(ctx.guild.id)] = channel.id
with open("factoftheday.json", "w") as f:
json.dump(fotddict, f)
#tasks.loop(seconds=10)
async def factsend(member):
x = randfacts.getFact()
channel_id = fotddict[str(member.guild.id)]
embed = discord.Embed(title="π‘Fact of the day!", description=x, color=0x7289da)
await client.get_channel(channel_id).send(embed=embed)
#factsend.before_loop
async def before():
factsend.start()
await client.wait_until_ready()
Problem: This is my fact of the day command, it adds the channel id + guild id in a json file (so that isnt the problem). I think the problem is the loop, since that is the part that im not sure of if thats correct.
Goal: Bot sends a message with a fact every 24 hours (Task is set to 10 seconds for test purposes)
Firstly your #factsend.before_loop function is called just before the loop execution, so you have to start the loop in other place, not in the function. So you have to deplace factsend.start() outside of this function.
The corriged code will be:
#client.event
async def on_ready():
global fotddict
with open("factoftheday.json", "r") as f:
fotddict = json.load(f)
#client.command()
#commands.has_permissions(administrator=True)
async def fotd(ctx, channel : discord.TextChannel=None):
if channel is None:
embe=discord.Embed(title="<:redcross:781952086454960138>Error", description="**Please pass in all required arguments!**\nNeed help?** https://dsc.gg/otaysupport**", color=0x7289da)
await ctx.send(embed=embe)
else:
#are you sure embed
msg = await ctx.send(embed=embed)
def checkifnotbotfact(reaction, user):
return user != client.user
await msg.add_reaction('π‘')
reaction, user = await client.wait_for("reaction_add", timeout=60.0, check=checkifnotbotfact)
if str(reaction.emoji) == "π‘":
#confirm embed
global fotddict
fotddict[str(ctx.guild.id)] = channel.id
with open("factoftheday.json", "w") as f:
json.dump(fotddict, f)
#tasks.loop(seconds=10)
async def factsend(member):
x = randfacts.getFact()
channel_id = fotddict[str(member.guild.id)]
embed = discord.Embed(title="π‘Fact of the day!", description=x, color=0x7289da)
await client.get_channel(channel_id).send(embed=embed)
#factsend.before_loop
async def before():
await client.wait_until_ready()
factsend.start() #deplaced outside of the function
Have a nice day!
If you are going to leave the bot running locally (or host it somewhere), then you should use Advance Python Scheduler
from apscheduler.schedulers.blocking import BlockingScheduler
#Code goes Here
scheduler = BlockingScheduler()
scheduler.add_job(function_name_you_want_to_run(), 'interval', hours=24)
scheduler.start()
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
I'm trying to get my background task to send in different channels using random.choice(). When I turn the bot on, it will send in only one random channel and that channel only. Is there a way to send in a different channel each time it loops?
async def test_loop():
await client.wait_until_ready()
channels = ['550528972226', '5149003563352', '514900351233', '5799132312340']
channel = client.get_channel(random.choice(channels))
while not client.is_closed:
time = random.randint(1,5)+random.random()
monies = random.randint(100,250)
emojigrab = dollar
emojimsg = await client.send_message(channel, emojigrab)
await client.add_reaction(emojimsg, hand)
pay = await client.wait_for_reaction(emoji=hand, message=emojimsg, timeout=1800,
check=lambda reaction, user: user != client.user)
if pay:
await client.delete_message(emojimsg)
await client.send_message(channel, "{} collects {:,} dollars".format(pay.user.mention, monies))
add_dollars(pay.user, monies)
await asyncio.sleep(int(time))
Currently, channel = client.get_channel(random.choice(channels)) is outside of your while loop, meaning that variable channel never changes. Move it to inside your while loop to change it every time a new message is going to be sent.
async def test_loop():
await client.wait_until_ready()
channels = ['550528972226', '5149003563352', '514900351233', '5799132312340']
while not client.is_closed:
channel = client.get_channel(random.choice(channels))
time = random.randint(1,5)+random.random()
monies = random.randint(100,250)
emojigrab = 'π΅'
emojimsg = await client.send_message(channel, emojigrab)
await client.add_reaction(emojimsg, "π°")
pay = await client.wait_for_reaction(emoji="π°", message=emojimsg, timeout=1800,
check=lambda reaction, user: user != client.user)
if pay:
await client.delete_message(emojimsg)
await client.send_message(channel, "{} secures the bag for ${:,}".format(pay.user.mention, monies))
add_dollars(pay.user, monies)
await asyncio.sleep(int(time))