Why is my tasks.loop not working on Heroku? - heroku

I have a cog named wednesday that prints a certain image from an images folder, as the name suggests, every Wednesday. For testing purposes, however, I've made the loop run every 30 seconds, and I've made it so that it runs today (Monday) instead of Wednesday. I've tried running this code locally on my computer, and it works with no issues. However, after deploying to Heroku, the loop doesn't work at all. Keep in mind that I have another cog that has a tasks.loop as well, which changes the status of my bot every few seconds. That cog, however, works without any issues on Heroku. As I said before, this wednesday cog works locally on my machine, which means that I'm properly adding and loading the cog for my bot, and doing other things that are required for the code to run. So, why is my code not working on Heroku?
import discord, datetime
from discord.ext import commands, tasks
import json
with open('config.json') as f:
config = json.load(f)
class wednesday(commands.Cog):
def __init__(self, bot):
self.bot = bot
#tasks.loop(seconds=30)
async def time_checker(self):
self.time = datetime.datetime.now
if self.time().hour == 19:
if datetime.datetime.today().weekday() == 0: # 0 because I'm testing today which is a Monday
self.channel = await self.bot.fetch_channel(config['id'])
await self.channel.send(file=discord.File('images/wednesday_pic.png'))
#commands.Cog.listener()
async def on_ready(self):
await self.bot.wait_until_ready()
self.time_checker.start()
def setup(bot):
bot.add_cog(wednesday(bot))
I'm not sure why this is happening: I think it might have something to do with the loop. I have other images in the images folder that work fine for other commands that I have, as the bot sends them when I call the command. Any help would be appreciated.

We need more debugging details. You may find out what the mistake is when doing the debugging.
Check requirements.txt and ensure the versions are locked. (Likely the case if you generated it through pip freeze. If not: Make sure the versions on your local development environment is the same as on Heroku.
Add more logs. Logs when your cog is loaded. Logs when your task is repeatedly executed along with the values of your variables. Logs that print out your current working directory (import os; print(os.getcwd()))
Check how far your bot reaches. Is it failing at the very beginning and it does not come online? Is the cog not being loaded? Is it the task routine? Is it the path resolution?
Provide the logs. Don't cut off the logs. Provide the entire thing. They may not seem relevant to your but other readers may spot something.
You provided there a relative path for your image. Provide information of your project folder structure. What's the command used to execute your project (Procfile, can also be seen in the logs.)

I've figured out what was causing the error. The Heroku servers run on the UTC time zone, and I am on the PST/PDT time zone. So, I used a UTC to PDT converter to get the appropriate time that I was looking for the loop to print out the image. Here is the final code:
import discord, datetime
from discord.ext import commands, tasks
import json
with open('config.json') as f:
config = json.load(f)
class wednesday(commands.Cog):
def __init__(self, bot):
self.bot = bot
#tasks.loop(hours=1)
async def time_checker(self):
self.channel = await self.bot.fetch_channel(config['channel'])
self.time = datetime.datetime.now
if self.time().hour == 12:
if datetime.datetime.today().weekday() == 3:
await self.channel.send(file=discord.File('images/wednesday_pic.png'))
#commands.Cog.listener()
async def on_ready(self):
await self.bot.wait_until_ready()
self.time_checker.start()
def setup(bot):
bot.add_cog(wednesday(bot))

Related

Discord.py Bot Autodelete

Im new to python (i learned the basics at a school course),
and for the moment im trying to create simple bots for my discord server.
And for the moment, the bot i really want, is an autodelete bot so that i can say, (for example) delete every message in the ...Channel after 24h. because i really dont wanna do that manually.
I know there are a few good bots that do that,
but for example MEE6 wants me to buy Premium to use the funktion.The other reason why i dont want to use any finished bot is that i really want to understand and learn the code,
i watched many tutorials and tried to put the parts of the scripts that i understood together, but it did not work. I also didnt find a tutorial which explained it to me so that i understood, so now im here and hope that im going to understand it.
I hope there are some ppl to help me. :)
Thanks
-Yami.Code
#bot.event()
async def on_ready(ctx):
while requirement == 1:
await ctx.channel.purge
time.sleep(20)
#the Error is:
line 11, in <module>
#bot.event()
TypeError: event() missing 1 required positional argument: 'coro'
Process finished with exit code 1
You shouldn't call bot.event (remove the parenthesis),
time.sleep is a blocking call, use asyncio.sleep instead (What does "blocking" mean)
on_ready doens't take ctx as an argument, if you want to send a message to a channel you should get the channel object first, and then use the send method
You're missing a parenthesis in your channel.purge method...
import asyncio # if you haven't already
#bot.event
async def on_ready():
channel = bot.get_channel(channel_id) # replace `channel_id` with an actual channel ID
while requirement == 1:
await channel.purge(limit=x) # change `x` accordingly...
await asyncio.sleep(20)

How to add a + 1 to the number every time someone uses a command

I'm making a bakery bot in discord.py where people are able to bake items, I'm making a command called "Cake", where users can run it every 2/1 minutes and their cake count will go up.
For example, if someone runs the command b!bake cake it would return "You've baked 1 more cake, you now have 2 cakes!"
How would I do this?
You need to store a the information somewhere.
If you want the data to be persistent across restart then write it to a file or database.
If you don't you can always save it to a variable accessible from anywhere. An option would be to use a global variable but it could be unaccessible from a cog, you can set an attribute in your bot's instance.
bot = commands.Bot(...)
bot.cakes = {}
#bot.command()
async def bake (ctx, dish):
if dish == 'cake':
if ctx.author.id not in bot.cakes.keys():
bot.cakes[ctx.author.id] = 1
else bot.cakes[ctx.author.id] += 1
await ctx.send(f"you have baked {bot.cakes[ctx.author.id]} cakes")
bot.run(TOKEN)

How can i find out if a person is currently talking in discord.py?

I would like to know whether somebody is talking or is AFK.
I have found some examples of things that might help, but I don't quite if it is even supposed to work. Like this one:
async def on_member(member):
if member.voice:
print("HI")
And i would like to know if i have to import something else because currently i only have:
import discord
from discord.ext import commands
The example you provided returns a VoiceState, which you could potentially use. If you have an AFK voice channel and push-to-talk voice channel set up in your guild, you could do something like this:
(Read up about on_voice_state_update() here)
#bot.event
async def on_voice_state_update(member, prev, cur):
user = f"{member.name}#{member.discriminator}"
if cur.afk and not prev.afk:
print(f"{user} went AFK!")
elif prev.afk and not cur.afk:
print(f"{user} is no longer AFK!")
elif cur.self_mute and not prev.self_mute: # Would work in a push to talk channel
print(f"{user} stopped talking!")
elif prev.self_mute and not cur.self_mute: # As would this one
print(f"{user} started talking!")
Clarifying:
Discord doesn't receive audio, unless you're planning on working with the bytes from the socket, but that's quite a sizeable project.

How can I make a bot change it's presence (listening, watching, playing, etc...) displayed on discord under their name?

I get nothing showing up that there is an error, but it does not show up in discord. Could someone show me what is wrong?
async def change_status(self):
await client.change_presence(game=Game(name = " ", type = 3))
I would like for the bot to have "listening" or "watching" show up on discord under it's name.
Okay, Update:
(pretty sure this is rewrite)
I figure some people will look this up over time so here it is.
On the discord.py discord server I looked around through #help and found a place where it said the correct answers they just needed to be edited slightly. Here is the answer for it:
await client.change_presence(activity=discord.Game(name="a game"))
This will set the bot to "Playing."
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="a movie"))
This will set it to "Watching" status.
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="a song"))
This will set it to a "Listening" status.
You need the bot to specify when to change the status.
try #client.event this changes the status as the bot comes online.
to change listening to streaming or to watching try changing 2 ( 1,2,3,4 )
As far as i know to use the streaming status feature you need to link your twitch account with your code.
#client.event
async def on_ready():
await client.change_presence(game=Game(name='What ever in here',type = 2))
print('Ready')

Gathering coin volumes - Is my code running asynchronously?

I'm fairly new to programming in python, I've been programming for about half a year. I've decided to try to build a functional trading bot. While trying to code this bot, I stumbled upon the asyncio module. I would really like to understand the module better but it's hard finding any simple tutorials or documentation about asyncio.
For my script I'm gathering per coin the volume. This works perfectly, but it takes a really long time to gather all the volumes. I would like to ask if my script is running synchronously, and if so how do I fix this? I'm using an API wrapper to communicate with the Binance Exchange.
import binance
import asyncio
import time
s = time.time()
names = [name for name in binance.ticker_prices()] #Gathering all the coin names
loop = asyncio.get_event_loop()
async def get_volume(name):
async def get_data():
return binance.ticker_24hr(name) #Returns per coin a dict of the data of the last 24hr
data = await get_data()
return (name, data['volume'])
tasks = [asyncio.ensure_future(get_volume(name)) for name in names]
results = loop.run_until_complete(asyncio.gather(*tasks))
print('Total time:', time.time() - s)
Since binance.ticker_24hr does not look like it's a coroutine, it is almost certainly blocking the event loop and therefore preventing asyncio.gather to do its job. As a quick fix, you can use run_in_executor to run the blocking function in a separate thread:
async def get_volume(name):
loop = asyncio.get_event_loop()
data = await loop.run_in_executor(None, binance.ticker_24hr, name)
return name, data['volume']
This will work just fine for a reasonable number of parallel tasks. The downside is that it uses threads, so it might not scale to a huge number of parallel requests (or it would require unnecessary waiting). The correct solution in the long run is to use a library that natively supports asyncio.
Maarten firstly you are calling get_ticker for every symbol which means you're making many unnecessary requests. If you call it without a symbol value, you get all tickers in one request. This removes any loops or async as well if you aren't performing other tasks. It looks like the binance library you're using doesn't support this. You can use python-binance to do it
return client.get_ticker()
That said I've been testing an asyncio version of python-binance. It's currently in a feature branch now if you want to try it.
pip install git+https://github.com/sammchardy/python-binance#feature/asyncio
Include the asyncio version of the client and initialise the client
from binance.client_async import AsyncClient as Client
client = Client("<api_key>", "<api_secret>")
Then you can await the calls to get the ticker for a particular symbol
return await client.get_ticker(symbol=name)
Or for all symbol tickers don't pass the symbol parameter
return await client.get_ticker()
Hope that helps

Resources