I added this cog and ran =start @Some role
import asyncio
from colorsys import hls_to_rgb
import discord
from discord.ext import commands
class Rainbow(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.roles = set()
self.on_low_role = 'Sorry, your role isn\'t high enough'
self.step = 7
self.delay = 0.1
self.hue = 0
self.bot.loop.create_task(self.loop())
async def loop(self):
await self.bot.wait_until_ready()
while not self.bot.is_closed():
for i in self.roles:
#print(i.name, i.colour)
self.hue = (self.hue + self.step) % 360
rgb = [int(x * 255) for x in hls_to_rgb(self.hue / 360, 0.5, 1)]
clr = discord.Colour(((rgb[0] << 16) + (rgb[1] << 8) + rgb[2]))
await i.edit(colour=clr, reason='Automatic rainbow color change')
await asyncio.sleep(self.delay)
@commands.command()
@commands.has_permissions(manage_roles=True)
async def start(self, ctx: commands.Context, role: discord.Role):
if ctx.author.top_role > role:
m: discord.Message = await ctx.send('Starting...')
self.roles.add(role)
await m.edit(content='Started!')
else:
await ctx.send(self.on_low_role)
@commands.command()
@commands.has_permissions(manage_roles=True)
async def stop(self, ctx: commands.Context, role: discord.Role):
if ctx.author.top_role > role:
m: discord.Message = await ctx.send('Stopping...')
try:
self.roles.remove(role)
except KeyError:
await m.edit('This role doesn\'t rainbowin\'')
else:
await m.edit('Started!')
else:
await ctx.send(self.on_low_role)
def setup(bot: commands.Bot):
bot.add_cog(Rainbow(bot))
Lib should handle normally and wait until rate limit reset
Task exception was never retrieved
future: <Task finished coro=<Rainbow.loop() done, defined at C:\Users\iVan\Desktop\Movert\cogs\rainbow.py:17> exception=TypeError('string indices must be integers')>
Traceback (most recent call last):
File "C:\Users\iVan\Desktop\Movert\cogs\rainbow.py", line 25, in loop
await i.edit(colour=clr, reason='Automatic rainbow color change')
File "C:\Users\iVan\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\role.py", line 259, in edit
data = await self._state.http.edit_role(self.guild.id, self.id, reason=reason, **payload)
File "C:\Users\iVan\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\http.py", line 187, in request
retry_after = data['retry_after'] / 1000.0
TypeError: string indices must be integers
This issue is about rate limits bug, not about rainbow roles. Rainbow role is just an example, that's why there is 0.1 second as well. And i know they are forbidden.
Don't do rainbow roles.
Also, let me guess. The response probably isn't a JSON object anymore, instead:
Access denied | discordapp.com used Cloudflare to restrict access
Changing colours every 0.1 seconds :thinking:
So. There's a lot going on here.
What's actually happening is you're flagrantly violating discord's ratelimits, discord disconnects you because they don't want you hammering their API constantly, and while that is happening, you continue to try and send requests. This results in discord sending some "holy shit stop killing our API" string, which the lib, expecting you not to do something as dumb as this, doesn't bother to check for.
Think about this. Every time you update a role color, discord has to send that updated color to every single person. And you're trying to do this ten times a second. That's thousands and thousands of requests.
Discord.py handles ratelimits fine, but what it doesn't handle is the messages discord is sending you to (presumably) back the fuck off because you're flagrantly abusing their endpoints.
Finally, "rainbow role" bots are one of the few features discord has come out and said very clearly "this is api abuse, no, you cannot do this" and as far as I know, is one of the very very few things they have an automatic ban system implemented for bots that do this. Do not implement rainbow roles. No, not with X time between them, do not.
Also, using a set() for self.roles is unwise. Sets have slower iteration than a list, so you're just wasting cycles. Further, instead of checking for role hierarchy in the command, you should use command checks. discord.Color has the method from_rgb.
This should be resolved with https://github.com/Rapptz/discord.py/commit/dea3ba5eb7c99f54c72b11f3e3f7b8f41649e779, making the exception raised in these cases (a non-json response with a rate-limited status code), including when being blocked by Cloudflare, clearer and more explicit.