Python Discord.py `time.sleep()` coroutine - python

import discord
import os
import random
import time
import math
client = discord.Client()
with open('admins.conf', 'r') as f:
for line in f.readlines():
exec(line)
with open('bans.conf', 'r') as f:
for line in f.readlines():
exec(line)
with open('coins.conf', 'r') as f:
for line in f.readlines():
exec(line)
random.seed(os.urandom(32))
searchusers = []
#client.event
async def on_ready():
'''Notification on ready.'''
print('Logged in! Bot running.')
await client.change_presence(activity=discord.Game(name='/help'))
def getcoins(uid):
'''Get the amount of coins, if nonexistent set to 0.'''
try:
return coins[uid][0]
except Exception:
coins[uid] = [0, time.time()+20]
return 0
def mention_to_uid(mention):
'''Extract UID from a mention'''
uid = mention[2:-1]
if uid[0] == '!':
uid = uid[1:]
return uid
def setcoins(uid, value):
'''Set the amount of coins someone has.'''
try:
coins[uid][0] = value
except Exception:
coins[uid] = [value, time.time()+20]
with open('coins.conf','w') as f:
f.write('coins = '+repr(coins))
#client.event
async def on_message(message):
'''Main bot code running on message.'''
if message.author == client.user:
return
if message.author.id in bans:
return
if message.content.startswith('/') or message.content.startswith('&'):
user = message.author.id
text = message.content[1:].strip()
command = text.split(' ')[0]
subcommand = text.split(' ')[1:]
if message.author.id in searchusers:
await message.channel.send('<#'+str(message.author.id)+'>, you cannot use bot commands while you are searching.')
return
-------------------- snip --------------------
if command == 'search':
await message.channel.send('<#'+str(user)+'>, you have begun searching! It will take 2 minutes.')
searchusers.append(user)
time.sleep(59.99)
await message.channel.send('<#'+str(user)+'>, there is 1 minute left.')
time.sleep(39.99)
await message.channel.send('<#'+str(user)+'>, there are 20 seconds left!')
time.sleep(14.99)
await message.channel.send('<#'+str(user)+'>, there are 5 seconds left!')
time.sleep(4.99)
found = random.randint(50, 120)
await message.channel.send('<#'+str(user)+'>, you are done searching! You found '+str(found)+' coins!')
setcoins(user, getcoins(user)+found)
searchusers.remove(user)
During the time.sleep() events other bot commands do not register until the sleep function has passed. For example doing a command like /help right after doing /search the bot will not respond until after one minute, when it will respond to /help and message there is 1 minute left. I have tried sticking "await" before each one of the sleep functions but it just spit out runtime warnings at me and stopped the execution (It just says you started searching and then nothing happens).

time.sleep() stops the entire execution of the program. If you just want to delay one async response thread use asyncio.sleep
Example:
import asyncio
async def wait():
await asyncio.sleep(5)
print('I waited 5 seconds')

Related

getting a discord bot to ignore all incoming inputs after detecting spam

im working on a discord bot that responds to keywords with some text and an image, as you can imagine this would create a lot of opportunities to use the bot to spam and I would like to create a system in the code that allows the program to stop accepting all inputs once spamming is detected, I managed to create a system to detect the spam using time stamps but so far it does nothing, as I cannot figure out the code for what I need to do.
this is my code so far:
import os
import discord
import datetime
import time
from keep_on import keep_on
bot_token = os.environ['TOKEN']
client = discord.Client()
#client.event
async def on_ready():
print('{0.user} is online'.format(client))
print('###########################################')
enabled = True
time_storage = int(datetime.datetime.utcnow().timestamp())
#client.event
async def on_message(message):
global enabled
global time_storage
sentWord = message.content
CSentWord = sentWord.upper()
if message.content == "!start":
enabled = True
await message.channel.send("Having Trouble?")
await message.channel.send(file=discord.File('Having trouble.png'))
elif message.content == "!stop":
enabled = False
await message.channel.send("Ok Ill Stop.")
await message.channel.send(file=discord.File('Joey will stop.png'))
elif enabled == True:
if message.author == client.user:
return
if "SORRY" in CSentWord:
time.sleep(0.5)
time_delay = int(datetime.datetime.utcnow().timestamp())
time_difference = time_delay - time_storage
print('time since last stored:',time_storage)
print('time now:',time_delay)
print('time difference:',time_difference)
print('###########################################')
if time_difference < 5:
await message.channel.send("You are moving too fast")
await message.channel.send(file=discord.File('Bruh what.png'))
time.sleep(2)
return
await message.channel.send("'We're Very Sorry' - Joey Tribbiani")
await message.channel.send(file=discord.File('Joey Is Sorry.png'))
time_storage = int(datetime.datetime.utcnow().timestamp())
keep_on()
client.run(os.getenv('TOKEN'))
You can add cooldowns to commands using
#commands.cooldown(1, 10, commands.BucketType.user)
For example here the user can use a command once every 10 seconds

Why am I unable to call this function? Discord.py and ASYNC/AWAIT issues

I'm creating a discord bot friends but am having trouble in calling this function dice_roll(). When I don't await it, I get this error: RuntimeWarning: coroutine 'dice_roll' was never awaited
However, after adding "await" and trying to use the command, it never runs at all. I've read a bunch of documentation and tried several things, but I'm just not familiar enough with async/await to know what I'm really doing. I have another command which works fine aka "ich liebe.." in which the bot replies "die Kuche!"
Code is below
import random
import discord
import asyncio
bot = discord.Client()
#bot.event
async def dice_roll():
try:
dice_type = int(input())
await message.channel.send("Roll which dice: ")
num_dice = int(input())
await message.channel.send("How many times: ")
except ValueError:
await message.channel.send("Please use an integer like '4' or '20'.")
await dice_roll()
output = []
for i in range(num_dice):
rolls = random.randint(1,dice_type)
output.append(rolls)
formatted_output = str(output)[1:-1]
await message.channel.send("You rolled {}".format(formatted_output))
print("Task result: Dice was {} and num times was ()".format(dice_type, num_dice))
#bot.event
async def on_message(message):
if message.content == "rolling":
dice_roll()
elif message.content == "ich liebe":
await message.channel.send("die Kuche!")
print("cake command complete")
#bot.event
async def on_ready():
print("The bot is ready!")
bot.run(TOKEN)
You have set the function to:
#bot.event
async def dice_roll():
But you want:
#bot.command()
async def dice_roll():
Because event is when an event happens such as
#bot.event
async def on_ready():
And a command is like a function, which is callable
I made it work
import random
import discord
import asyncio
from discord.ext.commands import Bot
bot = discord.Client()
#bot.event
async def dice_roll(message):
try:
await message.channel.send("Roll which dice: ")
msg = await bot.wait_for('message')
dice_type = int(msg.content)
await message.channel.send("How many times: ")
msg2 = await bot.wait_for('message')
num_dice = int(msg2.content)
except ValueError:
await message.channel.send("Please use an integer like '4' or '20'.")
await dice_roll(message)
output = []
for i in range(num_dice):
rolls = random.randint(1,dice_type)
output.append(rolls)
formatted_output = str(output)[1:-1]
await message.channel.send("You rolled {}".format(formatted_output))
print("Task result: Dice was {} and num times was {}".format(dice_type, num_dice))
#bot.event
async def on_message(message):
if message.content == "rolling":
await dice_roll(message)
print("dice rolled successfully")
elif message.content == "ich liebe":
await message.channel.send("der Kuchen!")
print("cake command complete")
#bot.event
async def on_ready():
print("The bot is ready!")
bot.run(TOKEN)

Running two loops at the same time on my discord bot

So my discord bot in python has the task to send an embed as soon as the user sends the message "$start". After this the code is starting a while loop and checking if a user has reacted. Simultaneously I want to edit the message of the bot every second so I can display some kind of timer to show the users how much time they have left to react but I dont know how to implement a timer running at the same time. Would it be useful to use multiprocessing for this one?
Here is my very specific code if anyone needs it to answer my question :)
#client.command()
async def start(ctx):
timer = 120
remTime = timer
seedCapital = 10000
sessionEmpty = True
players[ctx.author] = seedCapital
em = discord.Embed(title = "New Poker Session", description = "Waiting for players to join ...\nreact with 💸 to join or ▶️ to start (only host)", color = discord.Color.green())
#em.add_field(name = "2 minutes remaining from now on!", value = "")
botMsg = await ctx.send(embed = em)
await botMsg.add_reaction("💸")
await botMsg.add_reaction("▶️")
while True:
mom0 = time.perf_counter()
try:
reaction, user = await client.wait_for('reaction_add', timeout = remTime, check = lambda reaction, user: reaction.emoji in ["💸", "▶️"])
#msg = await client.wait_for('message', timeout = remTime, check = lambda m: m.channel == ctx.channel)
#print(f"receiving message: {msg.content}")
except asyncio.TimeoutError:
break
if reaction.emoji == "💸":
sessionEmpty = False
await ctx.send(f"{user.mention} joined the session!")
players[user] = seedCapital
elif user == ctx.author and reaction.emoji == "▶️":
break
mom1 = time.perf_counter()
neededTime = mom1 - mom0
remTime -= neededTime
print(remTime)
if not sessionEmpty:
await ctx.send("starting session ...")
#start session
else:
await ctx.send("Noone joined your session :(")
We can use asyncio.gather to run coroutines concurrently.
#after command
from datetime import datetime, timedelta
end_time = datetime.now() + timedelta(seconds=30) #30 seconds to react
async def coro():
for i in range(30, 0, 5):
await botMsg.edit(f'time remaining: {i}') #send another message if you don't want old content to be erased
await asyncio.sleep(5)
async def coro2():
while datetime.now() <= endtime:
#do stuff
await asyncio.gather(coro(), coro2())
Note: There might be a small delay between the completion of the two coroutines.
References:
asyncio.gather
Edit: Here is the small delay I mentioned

Python If its same output dont send msg

I made a discord bot that sends every 120 sec transactions from one ethereum addrs but i dont want to send same stuff over and over so if it send USDT token and Again in 120 sec try USDT to just skip it until it got new thing is that possble or not?
code:
import requests
import sys
import json
import discord
import time
btoken = "mytoken"
result=requests.get('https://api.ethplorer.io/getAddressHistory/0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be?apiKey=freekey&type=transfer')
result.status_code
result.text
result.json()
results = "soon:tm:"
def price_of_gas(inp):
def recursive_function(inp):
if type(inp) is list:
for i in inp:
ans = recursive_function(i)
if ans!=None: return ans
elif type(inp) is dict:
if 'name' in inp: return inp['name']
for i in inp:
ans = recursive_function(inp[i])
if ans!=None: return ans
else: return None
ans = recursive_function(inp)
return ans if ans else "Could NOT find the new token tx"
print (price_of_gas(result.json()))
class MyClient(discord.Client):
async def on_ready(self):
print('Logged on as', self.user)
async def on_message(self, message):
# don't respond to ourselves
if message.author == self.user:
return
if message.content == '.get':
await message.channel.send('Alert! Alert! Buy')
await message.channel.send(result.json()['operations'][0]['tokenInfo']['symbol'])
await message.channel.send(result.json()['operations'][0]['tokenInfo']['address'])
print ('get command was tryed')
else:
print ('comand not found')
if message.content == '.help':
await message.channel.send("try .get")
print ('help command was tryed')
if message.content == '.stop':
await message.channel.send('Bye...')
print('bye')
sys.exit()
if message.content == '.start':
while True:
# Code executed here
print ('done')
price_of_gas(result.json())
print (price_of_gas(result.json()))
await message.channel.send(price_of_gas(result.json()))
time.sleep(120)
#print(result.json()['operations'][0]['tokenInfo']['name'])
#print(result.json()['operations'][0]['tokenInfo']['symbol'])
#print(result.json()['operations'][0]['tokenInfo']['address'])
#print (result.json()['tokenSymbol'])
#print (result.text)
print ('done no errors')
print ('done no errors with check data')
client = MyClient()
client.run(btoken)
print ('done no errors 2')
so if user type .start bot will start while loop that sends newest tx from selected addrs but problem is it will send same stuff i just need some if statment in while loop or something.
You shoudn't use the requests module (it's blocking), you should use aiohttp instead
You also shoudn't use time.sleep as it also blocks the whole thread. You should use asyncio.sleep. (If you still stay with time.sleep you're not going to be able to use the bot when it's "sleeping")
Answering your question, you can simply have a variable with the value of the previous price/value and check if the new message is the same, if it's not - send it
while True:
price = price_of_gas(result.json())
# Checking if the `previous_price` var exists
if hasattr(self, "previous_price"):
# If yes, comparing the values
if self.previous_price != price:
# If they're not the same, send the message
await message.channel.send(f"Current price: {price}")
self.previous_price = price # Updating the variable
else:
# If the `previous_price` var doesn't exists, creating it
self.previous_price = price
await asyncio.sleep(120) # Remember to import asyncio
Making HTTP requests with aiohttp
import aiohttp
async def main():
async with aiohttp.ClientSession() as session:
async with session.get("URL") as resp:
data = await resp.json()
# Note: you should create ONE session per application
EDIT:
If you want to use blocking functions (like the price_of_gas) you can use the next method
await self.loop.run_in_executor(None, price_of_gas, result.json())
More info here

How to make discord bot mute user after they say nsfw word 3 times in a row

I'm trying to make a discord bot to mute someone when they say an NSFW word three times in a row. I want the message which contains the word to get deleted every-time and on the 3rd try they get muted. I can't seem to do this.
Here is my cog:
import discord
from discord.ext import commands
import asyncio
with open('nsfw.txt') as file:
file = file.read().split(',')
class BadWords(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_message(self, msg):
for word in file:
if word in msg.content.lower():
await msg.delete()
attempts = 0
attempts += 1
if attempts == 3:
muted_role = msg.author.guild.get_role(783622936837226529)
await msg.author.add_roles(muted_role)
embed = discord.Embed(
title='',
description='',
colour=discord.Colour.red()
)
embed.add_field(name=f"✅ {msg.author.display_name} has been muted.", value='Reason: Toxicity')
await msg.channel.send(embed=embed)
await asyncio.sleep(1800)
await msg.author.remove_roles(muted_role)
break
else:
pass
def setup(client):
client.add_cog(BadWords(client))
You are setting attempts back to 0 every time someone says a word. Remove that and put self.client.attempts = {} under __init__ and change attempts += 1 to this:
try: self.client.attempts[msg.author.id] += 1
except KeyError:
self.client.attempts[msg.author.id] = 1 #this will only happen the first time
then,
if self.client.attempts[msg.author.id] == 3:
pass # do your other stuff

Categories