jarvis-bot/jarvis/cogs/util.py

330 lines
11 KiB
Python

"""J.A.R.V.I.S. Utility Cog."""
import re
import secrets
import string
from io import BytesIO
import discord
import discord_slash
from discord import File, Guild, Role, User
from discord.ext import commands
from discord_slash import SlashContext, cog_ext
from discord_slash.utils.manage_commands import create_choice, create_option
from PIL import Image, ImageDraw
import jarvis
from jarvis import jarvis_self, logo
from jarvis.config import get_config
from jarvis.data import pigpen
from jarvis.data.robotcamo import emotes, hk, names
from jarvis.utils import build_embed, convert_bytesize, get_repo_hash
from jarvis.utils.field import Field
class UtilCog(commands.Cog):
"""
Utility functions for J.A.R.V.I.S.
Mostly system utility functions, but may change over time
"""
def __init__(self, bot: commands.Cog):
self.bot = bot
self.config = get_config()
@cog_ext.cog_slash(
name="status",
description="Retrieve J.A.R.V.I.S. status",
)
@commands.cooldown(1, 30, commands.BucketType.channel)
async def _status(self, ctx: SlashContext) -> None:
title = "J.A.R.V.I.S. Status"
desc = "All systems online"
color = "#98CCDA"
fields = []
with jarvis_self.oneshot():
fields.append(Field("CPU Usage", jarvis_self.cpu_percent()))
fields.append(
Field(
"RAM Usage",
convert_bytesize(jarvis_self.memory_info().rss),
)
)
fields.append(Field("PID", jarvis_self.pid))
fields.append(Field("discord_slash", discord_slash.__version__))
fields.append(Field("discord.py", discord.__version__))
fields.append(Field("Version", jarvis.__version__, False))
fields.append(Field("Git Hash", get_repo_hash()[:7], False))
embed = build_embed(title=title, description=desc, fields=fields, color=color)
await ctx.send(embed=embed)
@cog_ext.cog_slash(
name="logo",
description="Get the current logo",
)
@commands.cooldown(1, 30, commands.BucketType.channel)
async def _logo(self, ctx: SlashContext) -> None:
lo = logo.get_logo(self.config.logo)
await ctx.send(f"```\n{lo}\n```")
@cog_ext.cog_slash(name="rchk", description="Robot Camo HK416")
async def _rchk(self, ctx: SlashContext) -> None:
await ctx.send(content=hk)
@cog_ext.cog_slash(
name="rcauto",
description="Automates robot camo letters",
options=[
create_option(
name="text",
description="Text to camo-ify",
option_type=3,
required=True,
)
],
)
async def _rcauto(self, ctx: SlashContext, text: str) -> None:
to_send = ""
if len(text) == 1 and not re.match(r"^[A-Z0-9-()$@!?^'#. ]$", text.upper()):
await ctx.send("Please use ASCII characters.", hidden=True)
return
for letter in text.upper():
if letter == " ":
to_send += " "
elif re.match(r"^[A-Z0-9-()$@!?^'#.]$", letter):
id = emotes[letter]
if ctx.author.is_on_mobile():
to_send += f":{names[id]}:"
else:
to_send += f"<:{names[id]}:{id}>"
if len(to_send) > 2000:
await ctx.send("Too long.", hidden=True)
else:
await ctx.send(to_send)
@cog_ext.cog_slash(
name="avatar",
description="Get a user avatar",
options=[
create_option(
name="user",
description="User to view avatar of",
option_type=6,
required=False,
)
],
)
@commands.cooldown(1, 5, commands.BucketType.user)
async def _avatar(self, ctx: SlashContext, user: User = None) -> None:
if not user:
user = ctx.author
avatar = user.avatar_url
embed = build_embed(title="Avatar", description="", fields=[], color="#00FFEE")
embed.set_image(url=avatar)
embed.set_author(name=f"{user.name}#{user.discriminator}", icon_url=avatar)
await ctx.send(embed=embed)
@cog_ext.cog_slash(
name="roleinfo",
description="Get role info",
options=[
create_option(
name="role",
description="Role to get info of",
option_type=8,
required=True,
)
],
)
async def _roleinfo(self, ctx: SlashContext, role: Role) -> None:
fields = [
Field(name="ID", value=role.id),
Field(name="Name", value=role.name),
Field(name="Color", value=str(role.color)),
Field(name="Mention", value=f"`{role.mention}`"),
Field(name="Hoisted", value="Yes" if role.hoist else "No"),
Field(name="Position", value=str(role.position)),
Field(name="Mentionable", value="Yes" if role.mentionable else "No"),
]
embed = build_embed(
title="",
description="",
fields=fields,
color=str(role.color),
timestamp=role.created_at,
)
embed.set_footer(text="Role Created")
embed.set_thumbnail(url="attachment://color_circle.png")
im = Image.new("RGBA", size=(200, 200), color=(0, 0, 0, 0))
canvas = ImageDraw.Draw(im)
color = role.color.to_rgb()
color = list(color)
color.append(255)
color = tuple(color)
canvas.rectangle([(0, 0), (200, 200)], fill=color)
del canvas
with BytesIO() as image_bytes:
im.save(image_bytes, "PNG")
image_bytes.seek(0)
color_circle = File(image_bytes, filename="color_circle.png")
await ctx.send(embed=embed, file=color_circle)
@cog_ext.cog_slash(
name="userinfo",
description="Get user info",
options=[
create_option(
name="user",
description="User to get info of",
option_type=6,
required=False,
)
],
)
async def _userinfo(self, ctx: SlashContext, user: User = None) -> None:
if not user:
user = ctx.author
user_roles = user.roles
if user_roles:
user_roles = sorted(user.roles, key=lambda x: -x.position)
_ = user_roles.pop(-1)
fields = [
Field(
name="Joined",
value=user.joined_at.strftime("%a, %b %-d, %Y %-I:%M %p"),
),
Field(
name="Registered",
value=user.created_at.strftime("%a, %b %-d, %Y %-I:%M %p"),
),
Field(
name=f"Roles [{len(user_roles)}]",
value=" ".join([x.mention for x in user_roles]) if user_roles else "None",
inline=False,
),
]
embed = build_embed(
title="",
description=user.mention,
fields=fields,
color=str(user_roles[0].color) if user_roles else "#FF0000",
)
embed.set_author(name=f"{user.name}#{user.discriminator}", icon_url=user.avatar_url)
embed.set_thumbnail(url=user.avatar_url)
embed.set_footer(text=f"ID: {user.id}")
await ctx.send(embed=embed)
@cog_ext.cog_slash(name="serverinfo", description="Get server info")
async def _server_info(self, ctx: SlashContext) -> None:
guild: Guild = ctx.guild
owner = f"{guild.owner.name}#{guild.owner.discriminator}" if guild.owner else "||`[redacted]`||"
region = guild.region
categories = len(guild.categories)
text_channels = len(guild.text_channels)
voice_channels = len(guild.voice_channels)
members = guild.member_count
roles = len(guild.roles)
role_list = ", ".join(role.name for role in guild.roles)
fields = [
Field(name="Owner", value=owner),
Field(name="Region", value=region),
Field(name="Channel Categories", value=categories),
Field(name="Text Channels", value=text_channels),
Field(name="Voice Channels", value=voice_channels),
Field(name="Members", value=members),
Field(name="Roles", value=roles),
]
if len(role_list) < 1024:
fields.append(Field(name="Role List", value=role_list, inline=False))
embed = build_embed(title="", description="", fields=fields, timestamp=guild.created_at)
embed.set_author(name=guild.name, icon_url=guild.icon_url)
embed.set_thumbnail(url=guild.icon_url)
embed.set_footer(text=f"ID: {guild.id} | Server Created")
await ctx.send(embed=embed)
@cog_ext.cog_subcommand(
base="pw",
name="gen",
base_desc="Password utilites",
description="Generate a secure password",
guild_ids=[862402786116763668],
options=[
create_option(
name="length",
description="Password length (default 32)",
option_type=4,
required=False,
),
create_option(
name="chars",
description="Characters to include (default last option)",
option_type=4,
required=False,
choices=[
create_choice(name="A-Za-z", value=0),
create_choice(name="A-Fa-f0-9", value=1),
create_choice(name="A-Za-z0-9", value=2),
create_choice(name="A-Za-z0-9!@#$%^&*", value=3),
],
),
],
)
@commands.cooldown(1, 15, type=commands.BucketType.user)
async def _pw_gen(self, ctx: SlashContext, length: int = 32, chars: int = 3) -> None:
if length > 256:
await ctx.send("Please limit password to 256 characters", hidden=True)
return
choices = [
string.ascii_letters,
string.hexdigits,
string.ascii_letters + string.digits,
string.ascii_letters + string.digits + "!@#$%^&*",
]
pw = "".join(secrets.choice(choices[chars]) for i in range(length))
await ctx.send(
f"Generated password:\n`{pw}`\n\n"
'**WARNING: Once you press "Dismiss Message", '
"*the password is lost forever***",
hidden=True,
)
@cog_ext.cog_slash(
name="pigpen",
description="Encode a string into pigpen",
options=[create_option(name="text", description="Text to encode", option_type=3, required=True)],
)
async def _pigpen(self, ctx: SlashContext, text: str) -> None:
outp = "`"
for c in text:
c = c.lower()
if c.lower() in pigpen.lookup:
c = pigpen.lookup[c.lower()]
elif c == " ":
c = " "
elif c == "`":
continue
outp += c + " "
outp += "`"
await ctx.send(outp[:2000])
def setup(bot: commands.Bot) -> None:
"""Add UtilCog to J.A.R.V.I.S."""
bot.add_cog(UtilCog(bot))