Call function outside of #client.event (discord.py) - discord.py

I'm making a discord bot to play a game, using direct and open message commands to register player actions.
The message event handler is here. When getting a message to start the game, it'll start doing various things to get the game running.
import discord
import random
import datetime
client = discord.Client()
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
#client.event
async def on_message(message):
if message.author == client.user:
return
if "!ww start" == message.content.lower() and not(discord.ChannelType == "private"):
# Do stuff
night(message)
if "!ww save " in message.content.lower() and discord.ChannelType == "private":
save(message)
if "!ww kill " in message.content.lower() and discord.ChannelType == "private":
kill(message)
if "!ww vote " in message.content.lower() and discord.ChannelType == "private":
vote(message)
if "!ww view " in message.content.lower() and discord.ChannelType == "private":
view(message)
if "!ww remove" == message.content.lower():
remove(message)
if ("!ww add" == message.content.lower()):
add(message)
if (message.content == '!ww count'):
count(message)
if discord.ChannelType == "private":
night(message)
day(message)
'''
One example of a function I want to call is here, like others it makes use of the message and channel. This is in line with the above block of code.
'''
async def night(message):
with open("GameData.txt", mode="r", encoding="utf-8") as my_file:
gameData = my_file.read().replace('\n', '')
if gameData[-1] != " ":
gameData = gameData + " "
gameData = list(gameData.split(", "))
if gameData[4] != "Night":
return
for i in range(len(gameData)):
if gameData[i] == ".":
return
# Read who was killed
channel = client.get_channel(int(gameData[1]))
if gameData[2] == gameData[0]:
await channel.send("Someone was saved in the night")
else:
killed = gameData[2]
await channel.send(killed + " was killed in the night")
remove(message)
await message.channel.send("It is now day, the game will continue when you all make your decision")
nightClean()
client.run("No")
Eventually there is a loop between two functions that call each other once run. They will only run when other inputs have been made through additional message inputs handled in other functions.
Currently I'm getting an error saying: RuntimeWarning: "coroutine 'day' was never awaited day(message)" as with another function.
How can I work around this?
Thanks!

To create a command use:
#client.command()
async def command_name(ctx, commands args like user to ban etc):
Do stuff
And this is a command. Don't use the on_message function.

Related

Wait for time, then DM with bot, with multiple users at once

I have a discord bot I am working on for practice that reminds the users with a dm after a certain amount of time. My issue is that I am using time.sleep() to handle the delay. This stops other users from setting up reminders because it is waiting for the previous time.sleep() to end. I want to have multiple requests for a reminder happen simultaneously, rather than wait for each request to end before the next one starts. Below is my code. My discord tokens are hosted in a seprate .env file.
# bot 2
import os
import discord
import time
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN2')
client = discord.Client()
#client.event
async def on_ready():
print(
f'{client.user} has connected to Discord!')
#client.event
async def on_message(message):
if message.author == client.user:
return
remind_me = 'You will be reminded '
if 'remindme!' in message.content.lower():
if message.content[-1] == 'h' or \
message.content[-1] == 's' or\
message.content[-1] == 'm':
response = remind_me + '\'' + message.content[10:-3] + '\' in' + message.content[-3::]
await message.channel.send(response)
else:
await message.channel.send('I don\'t understand.')
if message.content[-1] == 'h':
time.sleep(3600 * int(message.content[-2]))
await message.author.send('Reminder! \n' + message.content[10:-3])
if message.content[-1] == 's':
time.sleep(int(message.content[-2]))
await message.author.send('Reminder! \n' + message.content[10:-3])
if message.content[-1] == 'm':
time.sleep(int(message.content[-2])*60)
await message.author.send('Reminder! \n' + message.content[10:-3])
client.run(TOKEN)
I know I can clean up the if statements, so dont worry about that.
Use asyncio.sleep instead of time.sleep.
import asyncio
[...]
if message.content[-1] == 'h':
asyncio.sleep(3600 * int(message.content[-2]))
await message.author.send('Reminder! \n' + message.content[10:-3])
[...]

Messages repeating infinitely -- Discord.py

So this is 100% a wip and I'm super confused on how to stop the discord bot from reading the same user message over and over again.
import discord
class MyClient(discord.Client):
async def on_ready(self):
print('Logged in as')
print(self.user.name)
print(self.user.id)
print('------')
async def on_message(self, message):
global choice
global r
# we do not want the bot to reply to itself
if message.author.id == self.user.id:
return
if message.content.startswith('!hello'):
await message.reply('Hello!', mention_author=True)
r=0
basic = True
#upgrade1 = False
choice = '!idle'
while(choice != '!end'):
if message.content.startswith('!idle'):
choice= await message.reply('!click / !bank / !upgrade / !minigame / !end')
if message.author.id == self.user.id:
return
if message.content.startswith('!click' or '!c'):
r = r + 1
await message.reply("You have " + str(r) + " Renown")
elif message.content.startswith ('!bank'):
await message.reply ("You have " + str(r) + " Renown")
#elif message.content.startswith ('!upgrade' and r >= 10):
#await message.reply('Upgrading..')
#r = r - 10
#basic = False
#elif message.content.startswith ('!upgrade' and r < 10):
#await message.reply ('Not Enough Renown')
elif message.content.startswith('!minigame' or '!mg'):
#random number choosing game
await message.reply ("Game")
elif message.content.startswith ('!end'):
basic = False
#while time.time() < future:
#r =r + .00001
client = MyClient()
client.run
Your issue comes from the while(choice != '!end') loop. Once the bot enters that loop, the messages he is sending within that loop of course will be repeated because you are not changing the value of choice unless the message is !idle.
Notice in your code how if the command is for example !click, you are sending the following message:
await message.reply("You have " + str(r) + " Renown")
But you are not changing the value of choice, so in the next iteration of the while loop it will repeat the same, once and again.
I'm not sure what you are trying to achieve, but having a while loop in the on_message event does not seem a good idea for me, as this is a blocking feature. In my opinion you should replace it with an if.

I'm having a problem with my modmail bot code that was written my discord.py

So, I'm making a modmail bot for my discord server. But when I run, the bot can connect but when I send a test messange in the bot DM, I receive this error: AttributeError: 'NoneType' object has no attribute 'send'.
I'm using python 3.9.1 on Arch Linux. Here is my code:
import discord
from discord.ext.commands import Bot
from discord.ext import commands
import asyncio
client = commands.Bot(command_prefix="!")
#client.event
async def on_ready():
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name='for moderation mail'))
print("Thunderbird is ready")
#client.event
async def on_message(message):
empty_array = []
modmail_channel = discord.utils.get(client.get_all_channels(), name="modmail-box")
if message.author == client.user:
return
if str(message.channel.type) == "private":
if message.attachments != empty_array:
files = message.attachments
await modmail_channel.send("<#" + message.author.id + ">: ")
for file in files:
await modmail_channel.send(file.url)
else:
await modmail_channel.send("<#" + str(message.author.id) + ">: " + message.content)
elif str(message.channel) == "modmail" and message.content.startswith("<"):
member_object = message.mentions[0]
if message.attachments != empty_array:
files = message.attachments
await member_object.send("**[MOD]** " + "**" + message.author.display_name + "**: ")
for file in files:
await member_object.send(file.url)
else:
index = message.content.index(" ")
string = message.content
mod_message = string[index:]
await member_object.send("**[MOD]** " + "**" + message.author.display_name + "**: " + mod_message)
client.run('No leaking here')
I am assuming that the error is on this line:
await member_object.send("**[MOD]** " + "**" + message.author.display_name + "**: ")
and member_object is coming from this block:
elif str(message.channel) == "modmail" and message.content.startswith("<"):
member_object = message.mentions[0]
if message.attachments != empty_array:
files = message.attachments
await member_object.send("**[MOD]** " + "**" + message.author.display_name + "**: ")
for file in files:
await member_object.send(file.url)
You are defining member_object as the first element in the message.mentions array, however your error of NoneType has no attribute suggests that there are no elements in that array and so member_object is actually equal to None not an instance of a class which has a send method.
You need to check your input to that function and ensure that when your code reaches that block it actually has the attributes that you are expecting

Bot keeps executing random parts of loop

Im trying to make a bot that sends a message whenever it detects page status changes, but after 3-5 seconds it randomly sends "server is online" even tho nothing changed in the page.
import os
import discord
from dotenv import load_dotenv
import time
import requests
def check(r):
if "online" in r.text:
return True
else:
return False
online = False
load_dotenv()
TOKEN = "#hidden"
GUILD = #hidden
client = discord.Client()
#client.event
async def on_ready():
for guild in client.guilds:
if guild.name == GUILD:
break
print(
f'{client.user} is connected to the following guild:\n'
f'{guild.name}(id: {guild.id})')
channel = client.get_channel(#hidden)
last_status = check(requests.get("#page"))
while True:
if check(requests.get("#page")) == last_status:
continue
else:
if check(requests.get(#page")):
await channel.send("server is online")
last_status = check(requests.get("#page"))
else:
await channel.send("Server is offline")
last_status = check(requests.get("#page"))
client.run(TOKEN)
It's probably because you have multiple runing instance of on_readyfunction.
Discord API often send (especially during this period of overload due to the pandemic) a instruction of reconnect.
When discord.py recieve this instruction, it reconnect and call on_ready again, without killing the other.
The solution is to use asyncio.ensure_future and client.wait_until_ready to be sure there is only one instance
code:
import os
import discord
from dotenv import load_dotenv
import time
import requests
import asyncio
def check(r):
if "online" in r.text:
return True
else:
return False
online = False
load_dotenv()
TOKEN = "#hidden"
GUILD = #hidden
client = discord.Client()
async def routine():
await client.wait_until_ready()
channel = client.get_channel(#hidden)
last_status = check(requests.get("#page"))
while True:
if check(requests.get("#page")) == last_status:
continue
else:
if check(requests.get(#page")):
await channel.send("server is online")
last_status = check(requests.get("#page"))
else:
await channel.send("Server is offline")
last_status = check(requests.get("#page"))
#client.event
async def on_ready():
for guild in client.guilds:
if guild.name == GUILD:
break
print(
f'{client.user} is connected to the following guild:\n'
f'{guild.name}(id: {guild.id})')
asyncio.ensure_future(routine())
client.run(TOKEN)

An invalid syntax within an discord.py event

import discord
from discord.ext.commands import Bot
from discord.ext import commands+
from discord.utils import get
import time
bot = commands.Bot(command_prefix='r.', description="prefix")
#bot.event
async def on_message_delete(message):
if(message.author != bot.user):
else:
deleted = message.content
channel = bot.get_channel(657899187957923852)
await channel.send(time + '"' + deleted + '"')
in the line 12 else: is invalid syntax ( unknown, line 12)pylint(syntax-error) I am trying to make it when a message that is not the bot will send to another channel that the channel id is equal to 657899187957923852.
with the imports i dont know what i have used and the ones that are necessary for this one command (edited)
You need to have some code in the block under the if statement.
#bot.event
async def on_message_delete(message):
if(message.author != bot.user):
return
else:
deleted = message.content
channel = bot.get_channel(657899187957923852)
await channel.send(time + '"' + deleted + '"')

Resources