general/utils/cog.py
import itertools
from random import random
from typing import Optional, Union
from discord import Embed, Member, User
from discord.ext import commands
from discord.ext.commands import CommandError, Context, guild_only, max_concurrency
from discord.utils import format_dt, snowflake_time
from PyDrocsid.async_thread import run_in_thread
from PyDrocsid.cog import Cog
from PyDrocsid.command import docs, reply
from PyDrocsid.converter import Color, UserMemberConverter
from PyDrocsid.translations import t
from PyDrocsid.util import measure_latency
from .colors import Colors
from .permissions import UtilsPermission
from ...contributor import Contributor
tg = t.g
t = t.utils
@run_in_thread
def generate_color(colors: list[tuple[float, float, float]], n: int, a: float) -> tuple[float, float, float]:
guess = [random() for _ in range(3)] # noqa: S311
last = None
for _ in range(n):
new_guess = guess.copy()
for i in range(3):
mx = 0
for c in colors:
b = 10 ** (-3 - sum(x in (0, 1) for x in c))
error = 1 / (b + sum((p - q) ** 2 for p, q in zip(guess, c)))
if error > mx:
mx = error
new_guess[i] = guess[i] + (
2 * (guess[i] - c[i]) * ((b + sum((p - q) ** 2 for p, q in zip(guess, c))) ** -2) * a
)
guess = [min(max(x, 0), 1) for x in new_guess]
if last == guess:
return guess[0], guess[1], guess[2]
last = guess.copy()
return guess[0], guess[1], guess[2]
class UtilsCog(Cog, name="Utils"):
CONTRIBUTORS = [Contributor.Defelo]
@commands.command()
@docs(t.commands.ping)
async def ping(self, ctx: Context):
latency: Optional[float] = measure_latency()
embed = Embed(title=t.pong, colour=Colors.Utils)
if latency is not None:
embed.description = t.pong_latency(latency * 1000)
await reply(ctx, embed=embed)
@commands.command(aliases=["sf", "time"])
@docs(t.commands.snowflake)
async def snowflake(self, ctx: Context, arg: int):
if arg < 0:
raise CommandError(t.invalid_snowflake)
try:
await reply(ctx, format_dt(snowflake_time(arg), style="F"))
except (OverflowError, ValueError, OSError):
raise CommandError(t.invalid_snowflake)
@commands.command(aliases=["enc"])
@docs(t.commands.encode)
async def encode(self, ctx: Context, *, user: UserMemberConverter):
user: Union[User, Member]
embed = Embed(color=Colors.Utils)
embed.set_author(name=str(user), icon_url=user.display_avatar.url)
embed.add_field(name=t.username, value=str(user.name.encode())[2:-1], inline=False)
if isinstance(user, Member) and user.nick:
embed.add_field(name=t.nickname, value=str(user.nick.encode())[2:-1], inline=False)
await reply(ctx, embed=embed)
@commands.command(aliases=["rc"])
@UtilsPermission.suggest_role_color.check
@max_concurrency(1)
@guild_only()
@docs(t.commands.suggest_role_color)
async def suggest_role_color(self, ctx: Context, *avoid: Color):
avoid: tuple[int]
colors = [hex(color)[2:].zfill(6) for role in ctx.guild.roles if (color := role.color.value)]
colors += [hex(c)[2:].zfill(6) for c in avoid]
colors = [[int(x, 16) / 255 for x in [c[:2], c[2:4], c[4:]]] for c in colors]
colors += itertools.product(range(2), repeat=3)
color = await generate_color(colors, 2000, 5e-5)
color = "%02X" * 3 % tuple([round(float(c) * 255) for c in color])
embed = Embed(title="#" + color, color=int(color, 16))
embed.set_image(url=f"https://singlecolorimage.com/get/{color}/400x100")
await reply(ctx, embed=embed)