feat: add (incomplete) nix flake
dave.py isn't packaged yet. Will try to do this myself but dealing with vcpkg is a bit annoying.
This commit is contained in:
28
errornocord/utils/__init__.py
Normal file
28
errornocord/utils/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from .common import LimitedSizeDict, filter_secrets, format_duration, surround
|
||||
from .discord import (
|
||||
ChannelResponseWrapper,
|
||||
MessageInteractionWrapper,
|
||||
add_check_reaction,
|
||||
channel_send,
|
||||
cooldown,
|
||||
invalid_user_handler,
|
||||
load_opus,
|
||||
reply,
|
||||
snowflake_timestamp,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"add_check_reaction",
|
||||
"channel_send",
|
||||
"ChannelResponseWrapper",
|
||||
"cooldown",
|
||||
"filter_secrets",
|
||||
"format_duration",
|
||||
"invalid_user_handler",
|
||||
"LimitedSizeDict",
|
||||
"load_opus",
|
||||
"MessageInteractionWrapper",
|
||||
"reply",
|
||||
"snowflake_timestamp",
|
||||
"surround",
|
||||
]
|
||||
64
errornocord/utils/common.py
Normal file
64
errornocord/utils/common.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
from ..constants import SECRETS
|
||||
|
||||
|
||||
def surround(inner: str, outer="```") -> str:
|
||||
return outer + str(inner) + outer
|
||||
|
||||
|
||||
def format_duration(duration: int, natural: bool = False, short: bool = False) -> str:
|
||||
def format_plural(noun, count):
|
||||
if short:
|
||||
return noun[0]
|
||||
return " " + (noun if count == 1 else noun + "s")
|
||||
|
||||
segments = []
|
||||
|
||||
weeks, duration = divmod(duration, 604800)
|
||||
if weeks > 0:
|
||||
segments.append(f"{weeks}{format_plural('week', weeks)}")
|
||||
|
||||
days, duration = divmod(duration, 86400)
|
||||
if days > 0:
|
||||
segments.append(f"{days}{format_plural('day', days)}")
|
||||
|
||||
hours, duration = divmod(duration, 3600)
|
||||
if hours > 0:
|
||||
segments.append(f"{hours}{format_plural('hour', hours)}")
|
||||
|
||||
minutes, duration = divmod(duration, 60)
|
||||
if minutes > 0:
|
||||
segments.append(f"{minutes}{format_plural('minute', minutes)}")
|
||||
|
||||
if duration > 0:
|
||||
segments.append(f"{duration}{format_plural('second', duration)}")
|
||||
|
||||
separator = " " if short else ", "
|
||||
if not natural or len(segments) <= 1:
|
||||
return separator.join(segments)
|
||||
return separator.join(segments[:-1]) + f" and {segments[-1]}"
|
||||
|
||||
|
||||
def filter_secrets(text: str, secrets=SECRETS) -> str:
|
||||
for secret_name, secret in secrets.items():
|
||||
if not secret:
|
||||
continue
|
||||
text = text.replace(secret, f"<{secret_name}>")
|
||||
return text
|
||||
|
||||
|
||||
class LimitedSizeDict(OrderedDict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.size_limit = kwargs.pop("size_limit", 100)
|
||||
super().__init__(*args, **kwargs)
|
||||
self._check_size_limit()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
super().__setitem__(key, value)
|
||||
self._check_size_limit()
|
||||
|
||||
def _check_size_limit(self):
|
||||
if self.size_limit is not None:
|
||||
while len(self) > self.size_limit:
|
||||
self.popitem(last=False)
|
||||
115
errornocord/utils/discord.py
Normal file
115
errornocord/utils/discord.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import ctypes
|
||||
import time
|
||||
from logging import debug, error
|
||||
|
||||
import disnake
|
||||
|
||||
from .. import commands
|
||||
from ..constants import OWNERS
|
||||
from ..state import command_cooldowns, message_responses
|
||||
|
||||
|
||||
def cooldown(message, cooldown_time: int):
|
||||
if message.author.id in OWNERS:
|
||||
return
|
||||
|
||||
possible_commands = commands.match(message.content)
|
||||
if not possible_commands or len(possible_commands) > 1:
|
||||
return
|
||||
command = possible_commands[0]
|
||||
|
||||
end_time = time.time() + cooldown_time
|
||||
if message.author.id in command_cooldowns:
|
||||
command_cooldowns[message.author.id][command] = end_time
|
||||
else:
|
||||
command_cooldowns[message.author.id] = {command: end_time}
|
||||
|
||||
|
||||
async def reply(message, *args, **kwargs):
|
||||
if message.id in message_responses:
|
||||
if len(args) == 0:
|
||||
kwargs["content"] = None
|
||||
elif len(kwargs) == 0:
|
||||
kwargs["embeds"] = []
|
||||
|
||||
try:
|
||||
await message_responses[message.id].edit(
|
||||
*args,
|
||||
**kwargs,
|
||||
allowed_mentions=disnake.AllowedMentions.none(),
|
||||
)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
response = await message.reply(
|
||||
*args,
|
||||
**kwargs,
|
||||
allowed_mentions=disnake.AllowedMentions.none(),
|
||||
)
|
||||
except Exception:
|
||||
response = await channel_send(message, *args, **kwargs)
|
||||
message_responses[message.id] = response
|
||||
return message_responses[message.id]
|
||||
|
||||
|
||||
async def channel_send(message, *args, **kwargs):
|
||||
await message.channel.send(
|
||||
*args,
|
||||
**kwargs,
|
||||
allowed_mentions=disnake.AllowedMentions.none(),
|
||||
)
|
||||
|
||||
|
||||
def load_opus():
|
||||
path = ctypes.util._findLib_ld("opus")
|
||||
try:
|
||||
disnake.opus.load_opus(path)
|
||||
debug(f"successfully loaded opus from {path}")
|
||||
return
|
||||
except Exception as e:
|
||||
error(f"failed to load opus from {path}: {e}")
|
||||
raise Exception("could not locate working opus library")
|
||||
|
||||
|
||||
def snowflake_timestamp(snowflake) -> int:
|
||||
return round(((snowflake >> 22) + 1420070400000) / 1000)
|
||||
|
||||
|
||||
async def add_check_reaction(message):
|
||||
await message.add_reaction("✅")
|
||||
|
||||
|
||||
async def invalid_user_handler(interaction):
|
||||
await interaction.response.send_message(
|
||||
"you are not the intended receiver of this message!",
|
||||
ephemeral=True,
|
||||
)
|
||||
|
||||
|
||||
class ChannelResponseWrapper:
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
self.sent_message = None
|
||||
|
||||
async def send_message(self, **kwargs):
|
||||
kwargs.pop("ephemeral", None)
|
||||
self.sent_message = await reply(self.message, **kwargs)
|
||||
|
||||
async def edit_message(self, content=None, embed=None, view=None):
|
||||
if self.sent_message:
|
||||
content = content or self.sent_message.content
|
||||
if not embed and len(self.sent_message.embeds) > 0:
|
||||
embed = self.sent_message.embeds[0]
|
||||
await self.sent_message.edit(content=content, embed=embed, view=view)
|
||||
|
||||
|
||||
class MessageInteractionWrapper:
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
self.author = message.author
|
||||
self.response = ChannelResponseWrapper(message)
|
||||
|
||||
async def edit_original_message(self, content=None, embed=None, view=None):
|
||||
await self.response.edit_message(content=content, embed=embed, view=view)
|
||||
Reference in New Issue
Block a user