reformat (black)
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import importlib
|
||||
from . import discord_helpers
|
||||
|
||||
importlib.reload(discord_helpers)
|
||||
importlib.reload(discord_helpers)
|
||||
|
@ -12,35 +12,36 @@ Catbox Uploader (Async)
|
||||
|
||||
catbox_api_url: str = "https://catbox.moe/user/api.php"
|
||||
http_headers: dict[str, str] = {
|
||||
'accept': '*/*',
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53',
|
||||
'Accept-Language': 'en-US,en;q=0.9,it;q=0.8,es;q=0.7',
|
||||
'referer': 'https://www.google.com/',
|
||||
'cookie': 'DSID=AAO-7r4OSkS76zbHUkiOpnI0kk-X19BLDFF53G8gbnd21VZV2iehu-w_2v14cxvRvrkd_NjIdBWX7wUiQ66f-D8kOkTKD1BhLVlqrFAaqDP3LodRK2I0NfrObmhV9HsedGE7-mQeJpwJifSxdchqf524IMh9piBflGqP0Lg0_xjGmLKEQ0F4Na6THgC06VhtUG5infEdqMQ9otlJENe3PmOQTC_UeTH5DnENYwWC8KXs-M4fWmDADmG414V0_X0TfjrYu01nDH2Dcf3TIOFbRDb993g8nOCswLMi92LwjoqhYnFdf1jzgK0'
|
||||
}
|
||||
"accept": "*/*",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53",
|
||||
"Accept-Language": "en-US,en;q=0.9,it;q=0.8,es;q=0.7",
|
||||
"referer": "https://www.google.com/",
|
||||
"cookie": "DSID=AAO-7r4OSkS76zbHUkiOpnI0kk-X19BLDFF53G8gbnd21VZV2iehu-w_2v14cxvRvrkd_NjIdBWX7wUiQ66f-D8kOkTKD1BhLVlqrFAaqDP3LodRK2I0NfrObmhV9HsedGE7-mQeJpwJifSxdchqf524IMh9piBflGqP0Lg0_xjGmLKEQ0F4Na6THgC06VhtUG5infEdqMQ9otlJENe3PmOQTC_UeTH5DnENYwWC8KXs-M4fWmDADmG414V0_X0TfjrYu01nDH2Dcf3TIOFbRDb993g8nOCswLMi92LwjoqhYnFdf1jzgK0",
|
||||
}
|
||||
|
||||
|
||||
class CatboxAsync:
|
||||
def __init__(self) -> None:
|
||||
self.catbox_api_url = catbox_api_url
|
||||
self.headers = http_headers
|
||||
|
||||
|
||||
def generateRandomFileName(self, fileExt: Optional[str] = None) -> str:
|
||||
"""
|
||||
Generate random file name
|
||||
|
||||
|
||||
Args:
|
||||
fileExt (Optional[str]): File extension to use for naming
|
||||
Returns:
|
||||
str
|
||||
"""
|
||||
if not fileExt:
|
||||
fileExt = 'png'
|
||||
fileExt = "png"
|
||||
return f"{random.getrandbits(32)}.{fileExt}"
|
||||
|
||||
|
||||
async def upload(self, file: str) -> Optional[str]:
|
||||
"""
|
||||
Upload file to catbox
|
||||
|
||||
|
||||
Args:
|
||||
file (str): Path of file to be uploaded
|
||||
Returns:
|
||||
@ -49,33 +50,33 @@ class CatboxAsync:
|
||||
try:
|
||||
if not file:
|
||||
return None
|
||||
if not(os.path.exists(file)):
|
||||
logging.critical("Could not find %s",
|
||||
file)
|
||||
if not (os.path.exists(file)):
|
||||
logging.critical("Could not find %s", file)
|
||||
return None
|
||||
|
||||
|
||||
fileExt: Optional[str] = None
|
||||
if file.find(".") > 0:
|
||||
fileExt = "".join(file.split(".")[-1:])
|
||||
|
||||
with open(file, 'rb') as fileContents:
|
||||
|
||||
with open(file, "rb") as fileContents:
|
||||
post_data: FormData = FormData()
|
||||
post_data.add_field(name="reqtype",
|
||||
value="fileupload")
|
||||
post_data.add_field(name="userhash",
|
||||
value="")
|
||||
post_data.add_field(name="reqtype", value="fileupload")
|
||||
post_data.add_field(name="userhash", value="")
|
||||
with magic.Magic(flags=magic.MAGIC_MIME) as m:
|
||||
content_type = m.id_filename(file)
|
||||
post_data.add_field(name="fileToUpload",
|
||||
value=fileContents,
|
||||
filename=self.generateRandomFileName(fileExt),
|
||||
content_type=content_type
|
||||
)
|
||||
post_data.add_field(
|
||||
name="fileToUpload",
|
||||
value=fileContents,
|
||||
filename=self.generateRandomFileName(fileExt),
|
||||
content_type=content_type,
|
||||
)
|
||||
async with ClientSession() as session:
|
||||
async with await session.post(self.catbox_api_url,
|
||||
headers=self.headers,
|
||||
data=post_data,
|
||||
timeout=ClientTimeout(connect=10, sock_read=10)) as request:
|
||||
async with await session.post(
|
||||
self.catbox_api_url,
|
||||
headers=self.headers,
|
||||
data=post_data,
|
||||
timeout=ClientTimeout(connect=10, sock_read=10),
|
||||
) as request:
|
||||
request.raise_for_status()
|
||||
return await request.text()
|
||||
except:
|
||||
@ -85,4 +86,4 @@ class CatboxAsync:
|
||||
try:
|
||||
fileContents.close()
|
||||
except:
|
||||
return None
|
||||
return None
|
||||
|
@ -6,11 +6,13 @@ from typing import Optional, Any
|
||||
Discord Helper Methods
|
||||
"""
|
||||
|
||||
async def get_channel_by_name(bot: discord.Bot, channel: str,
|
||||
guild: int | None = None) -> Optional[Any]: # Optional[Any] used as pycord channel types can be ambigious
|
||||
|
||||
async def get_channel_by_name(
|
||||
bot: discord.Bot, channel: str, guild: int | None = None
|
||||
) -> Optional[Any]: # Optional[Any] used as pycord channel types can be ambigious
|
||||
"""
|
||||
Get Channel by Name
|
||||
|
||||
|
||||
Args:
|
||||
bot (discord.Bot)
|
||||
channel (str)
|
||||
@ -18,10 +20,9 @@ async def get_channel_by_name(bot: discord.Bot, channel: str,
|
||||
Returns:
|
||||
Optional[Any]
|
||||
"""
|
||||
channel = re.sub(r'^#', '', channel.strip())
|
||||
channel = re.sub(r"^#", "", channel.strip())
|
||||
if not guild:
|
||||
return discord.utils.get(bot.get_all_channels(),
|
||||
name=channel)
|
||||
return discord.utils.get(bot.get_all_channels(), name=channel)
|
||||
else:
|
||||
_guild: Optional[discord.Guild] = bot.get_guild(guild)
|
||||
if not _guild:
|
||||
@ -32,12 +33,14 @@ async def get_channel_by_name(bot: discord.Bot, channel: str,
|
||||
return _channel
|
||||
return None
|
||||
|
||||
async def send_message(bot: discord.Bot, channel: str,
|
||||
message: str, guild: int | None = None) -> None:
|
||||
|
||||
async def send_message(
|
||||
bot: discord.Bot, channel: str, message: str, guild: int | None = None
|
||||
) -> None:
|
||||
"""
|
||||
Send Message to the provided channel. If guild is provided, will limit to channels within that guild to ensure the correct
|
||||
channel is selected. Useful in the event a channel exists in more than one guild that the bot resides in.
|
||||
|
||||
|
||||
Args:
|
||||
bot (discord.Bot)
|
||||
channel (str)
|
||||
@ -50,9 +53,8 @@ async def send_message(bot: discord.Bot, channel: str,
|
||||
channel_int: int = int(channel)
|
||||
_channel = bot.get_channel(channel_int)
|
||||
else:
|
||||
channel = re.sub(r'^#', '', channel.strip())
|
||||
_channel = await get_channel_by_name(bot=bot,
|
||||
channel=channel, guild=guild)
|
||||
channel = re.sub(r"^#", "", channel.strip())
|
||||
_channel = await get_channel_by_name(bot=bot, channel=channel, guild=guild)
|
||||
if not isinstance(_channel, discord.TextChannel):
|
||||
return None
|
||||
await _channel.send(message)
|
||||
|
@ -1,9 +1,9 @@
|
||||
import aiohttp
|
||||
import aiohttp
|
||||
from typing import Optional
|
||||
import regex
|
||||
import regex
|
||||
from regex import Pattern
|
||||
import os
|
||||
import random
|
||||
import os
|
||||
import random
|
||||
import logging
|
||||
import traceback
|
||||
from util.catbox import CatboxAsync
|
||||
@ -13,61 +13,64 @@ Jesus Meme Generator
|
||||
(requires Catbox uploader)
|
||||
"""
|
||||
|
||||
class JesusMemeGenerator():
|
||||
|
||||
class JesusMemeGenerator:
|
||||
def __init__(self) -> None:
|
||||
self.MEMEAPIURL = "https://apimeme.com/meme?meme="
|
||||
self.MEMESTORAGEDIR = os.path.join(os.path.expanduser("~"),
|
||||
"memes")
|
||||
self.MEMESTORAGEDIR = os.path.join(os.path.expanduser("~"), "memes")
|
||||
self.top_line_regex: Pattern = regex.compile(
|
||||
r'[^\p{Letter}\p{Number}\p{Punctuation}\p{Horiz_Space}\p{Currency_Symbol}]'
|
||||
)
|
||||
r"[^\p{Letter}\p{Number}\p{Punctuation}\p{Horiz_Space}\p{Currency_Symbol}]"
|
||||
)
|
||||
self.bottom_line_regex: Pattern = regex.compile(
|
||||
r'[^\p{Letter}\p{Number}\p{Punctuation}\p{Horiz_Space}\p{Currency_Symbol}]'
|
||||
)
|
||||
self.url_regex_1: Pattern = regex.compile(
|
||||
r'\p{Horiz_Space}')
|
||||
self.url_regex_2: Pattern = regex.compile(
|
||||
r'#')
|
||||
|
||||
async def create_meme(self, top_line: str, bottom_line: str,
|
||||
meme="Jesus-Talking-To-Cool-Dude") -> Optional[str]:
|
||||
r"[^\p{Letter}\p{Number}\p{Punctuation}\p{Horiz_Space}\p{Currency_Symbol}]"
|
||||
)
|
||||
self.url_regex_1: Pattern = regex.compile(r"\p{Horiz_Space}")
|
||||
self.url_regex_2: Pattern = regex.compile(r"#")
|
||||
|
||||
async def create_meme(
|
||||
self, top_line: str, bottom_line: str, meme="Jesus-Talking-To-Cool-Dude"
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
Create Meme
|
||||
|
||||
|
||||
Args:
|
||||
top_line (str): Top line of meme
|
||||
bottom_line (str): Bottom line of meme
|
||||
meme (str): The meme to use, defaults to Jesus-Talking-To-Cool-Dude
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[str]
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
if not top_line or not bottom_line:
|
||||
return None
|
||||
top_line = self.top_line_regex.sub('',
|
||||
top_line.strip())
|
||||
bottom_line = self.bottom_line_regex.sub('',
|
||||
bottom_line.strip())
|
||||
top_line = self.top_line_regex.sub("", top_line.strip())
|
||||
bottom_line = self.bottom_line_regex.sub("", bottom_line.strip())
|
||||
out_fname: Optional[str] = None
|
||||
|
||||
|
||||
if len(top_line) < 1 or len(bottom_line) < 1:
|
||||
return None
|
||||
|
||||
formed_url: str = f"{self.MEMEAPIURL}{meme}&top={top_line.strip()}&bottom={bottom_line.strip()}"
|
||||
formed_url = self.url_regex_1.sub('+', self.url_regex_2.sub('%23', formed_url.strip()))
|
||||
|
||||
formed_url: str = (
|
||||
f"{self.MEMEAPIURL}{meme}&top={top_line.strip()}&bottom={bottom_line.strip()}"
|
||||
)
|
||||
formed_url = self.url_regex_1.sub(
|
||||
"+", self.url_regex_2.sub("%23", formed_url.strip())
|
||||
)
|
||||
timeout = aiohttp.ClientTimeout(total=15)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.get(formed_url) as response:
|
||||
UUID = f"{random.getrandbits(8)}-{random.getrandbits(8)}"
|
||||
out_fname = f"{UUID}.jpg"
|
||||
with open(f"{self.MEMESTORAGEDIR}/{out_fname}", 'wb') as f:
|
||||
with open(f"{self.MEMESTORAGEDIR}/{out_fname}", "wb") as f:
|
||||
f.write(await response.read())
|
||||
|
||||
|
||||
if not out_fname:
|
||||
uploader = CatboxAsync()
|
||||
meme_link: Optional[str] = await uploader.upload(f"{self.MEMESTORAGEDIR}/{out_fname}")
|
||||
meme_link: Optional[str] = await uploader.upload(
|
||||
f"{self.MEMESTORAGEDIR}/{out_fname}"
|
||||
)
|
||||
if not meme_link:
|
||||
logging.info("Meme upload failed!")
|
||||
return None
|
||||
|
@ -13,37 +13,38 @@ import os
|
||||
|
||||
litterbox_api_url: str = "https://litterbox.catbox.moe/resources/internals/api.php"
|
||||
http_headers: dict[str, str] = {
|
||||
'accept': '*/*',
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53',
|
||||
'Accept-Language': 'en-US,en;q=0.9,it;q=0.8,es;q=0.7',
|
||||
'referer': 'https://www.google.com/',
|
||||
'cookie': 'DSID=AAO-7r4OSkS76zbHUkiOpnI0kk-X19BLDFF53G8gbnd21VZV2iehu-w_2v14cxvRvrkd_NjIdBWX7wUiQ66f-D8kOkTKD1BhLVlqrFAaqDP3LodRK2I0NfrObmhV9HsedGE7-mQeJpwJifSxdchqf524IMh9piBflGqP0Lg0_xjGmLKEQ0F4Na6THgC06VhtUG5infEdqMQ9otlJENe3PmOQTC_UeTH5DnENYwWC8KXs-M4fWmDADmG414V0_X0TfjrYu01nDH2Dcf3TIOFbRDb993g8nOCswLMi92LwjoqhYnFdf1jzgK0'
|
||||
}
|
||||
"accept": "*/*",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53",
|
||||
"Accept-Language": "en-US,en;q=0.9,it;q=0.8,es;q=0.7",
|
||||
"referer": "https://www.google.com/",
|
||||
"cookie": "DSID=AAO-7r4OSkS76zbHUkiOpnI0kk-X19BLDFF53G8gbnd21VZV2iehu-w_2v14cxvRvrkd_NjIdBWX7wUiQ66f-D8kOkTKD1BhLVlqrFAaqDP3LodRK2I0NfrObmhV9HsedGE7-mQeJpwJifSxdchqf524IMh9piBflGqP0Lg0_xjGmLKEQ0F4Na6THgC06VhtUG5infEdqMQ9otlJENe3PmOQTC_UeTH5DnENYwWC8KXs-M4fWmDADmG414V0_X0TfjrYu01nDH2Dcf3TIOFbRDb993g8nOCswLMi92LwjoqhYnFdf1jzgK0",
|
||||
}
|
||||
|
||||
|
||||
class LitterboxAsync:
|
||||
def __init__(self) -> None:
|
||||
self.headers: dict[str, str] = http_headers
|
||||
self.api_path = litterbox_api_url
|
||||
|
||||
|
||||
def generateRandomFileName(self, fileExt: Optional[str] = None) -> str:
|
||||
"""
|
||||
Generate Random Filename
|
||||
|
||||
|
||||
Args:
|
||||
fileExt (Optional[str]): File extension to use for naming
|
||||
Returns:
|
||||
str
|
||||
"""
|
||||
if not fileExt:
|
||||
fileExt = 'png'
|
||||
fileExt = "png"
|
||||
return f"{random.getrandbits(32)}.{fileExt}"
|
||||
|
||||
async def upload(self,
|
||||
file: Union[str, bytes, BufferedReader],
|
||||
time='1h') -> Optional[str]:
|
||||
async def upload(
|
||||
self, file: Union[str, bytes, BufferedReader], time="1h"
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
Upload File to Litterbox
|
||||
|
||||
|
||||
Args:
|
||||
file (Union[str, bytes, BufferedReader]): File to upload (accepts either filepath or io.BufferedReader)
|
||||
time (str): Expiration time, default: 1h
|
||||
@ -59,38 +60,38 @@ class LitterboxAsync:
|
||||
return None
|
||||
if isinstance(file, BufferedReader):
|
||||
file = file.read()
|
||||
|
||||
fileExt: str = 'png'
|
||||
|
||||
fileExt: str = "png"
|
||||
if isinstance(file, str):
|
||||
if file.find(".") > 0:
|
||||
fileExt = "".join(file.split(".")[-1:])
|
||||
|
||||
file = open(file, 'rb').read()
|
||||
|
||||
file = open(file, "rb").read()
|
||||
|
||||
with magic.Magic(flags=magic.MAGIC_MIME) as m:
|
||||
if isinstance(file, BufferedReader):
|
||||
content_type = str(m.id_buffer(file))
|
||||
else:
|
||||
content_type = str(m.id_filename(file))
|
||||
|
||||
content_type = str(m.id_filename(file))
|
||||
|
||||
post_data: FormData = FormData()
|
||||
post_data.add_field(name="reqtype",
|
||||
value="fileupload")
|
||||
post_data.add_field(name="userhash",
|
||||
value="")
|
||||
post_data.add_field(name="time",
|
||||
value=time)
|
||||
|
||||
post_data.add_field(name="fileToUpload",
|
||||
value=file,
|
||||
filename=self.generateRandomFileName(fileExt),
|
||||
content_type=content_type
|
||||
post_data.add_field(name="reqtype", value="fileupload")
|
||||
post_data.add_field(name="userhash", value="")
|
||||
post_data.add_field(name="time", value=time)
|
||||
|
||||
post_data.add_field(
|
||||
name="fileToUpload",
|
||||
value=file,
|
||||
filename=self.generateRandomFileName(fileExt),
|
||||
content_type=content_type,
|
||||
)
|
||||
async with ClientSession() as session:
|
||||
async with await session.post(self.api_path,
|
||||
headers=self.headers,
|
||||
data=post_data,
|
||||
timeout=ClientTimeout(connect=5, sock_read=70)) as request:
|
||||
async with await session.post(
|
||||
self.api_path,
|
||||
headers=self.headers,
|
||||
data=post_data,
|
||||
timeout=ClientTimeout(connect=5, sock_read=70),
|
||||
) as request:
|
||||
request.raise_for_status()
|
||||
return await request.text()
|
||||
except:
|
||||
|
@ -4,24 +4,28 @@ from typing import LiteralString, Optional, Union
|
||||
import aiosqlite as sqlite3
|
||||
from constructors import LoveHateException
|
||||
|
||||
|
||||
class DB:
|
||||
"""LoveHate DB Utility Class"""
|
||||
|
||||
def __init__(self, bot) -> None:
|
||||
self.db_path: str|LiteralString = os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "lovehate.db")
|
||||
|
||||
async def get_wholovehates(self, thing: str, loves: bool = False,
|
||||
hates: bool = False) -> Union[list[tuple], bool]:
|
||||
self.db_path: str | LiteralString = os.path.join(
|
||||
"/usr/local/share", "sqlite_dbs", "lovehate.db"
|
||||
)
|
||||
|
||||
async def get_wholovehates(
|
||||
self, thing: str, loves: bool = False, hates: bool = False
|
||||
) -> Union[list[tuple], bool]:
|
||||
"""
|
||||
Get a list of users who have professed their love OR hatred for <thing>
|
||||
|
||||
|
||||
Args:
|
||||
thing (str): The <thing> to check
|
||||
loves (bool): Are we looking for loves?
|
||||
hates (bool): ...or are we looking for hates?
|
||||
Returns:
|
||||
Union[list[tuple], bool]
|
||||
"""
|
||||
"""
|
||||
|
||||
query: str = "SELECT display_name FROM lovehate WHERE thing LIKE ? AND flag = ?"
|
||||
params: tuple = tuple()
|
||||
@ -35,8 +39,11 @@ class DB:
|
||||
flag = 1
|
||||
elif not hates and not loves:
|
||||
raise LoveHateException("Neither loves nor hates were requested")
|
||||
|
||||
params = (thing, flag,)
|
||||
|
||||
params = (
|
||||
thing,
|
||||
flag,
|
||||
)
|
||||
async with sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
||||
async with await db_conn.execute(query, params) as db_cursor:
|
||||
result: list[tuple] = await db_cursor.fetchall()
|
||||
@ -44,28 +51,35 @@ class DB:
|
||||
if not result:
|
||||
return False
|
||||
return result
|
||||
|
||||
async def get_lovehates(self, loves: bool = False, hates: bool = False,
|
||||
user: Optional[str] = None, thing: Optional[str] = None) -> Union[list[tuple], bool]:
|
||||
|
||||
async def get_lovehates(
|
||||
self,
|
||||
loves: bool = False,
|
||||
hates: bool = False,
|
||||
user: Optional[str] = None,
|
||||
thing: Optional[str] = None,
|
||||
) -> Union[list[tuple], bool]:
|
||||
"""
|
||||
Get a list of either 1) what {user} loves/hates, or who loves/hates {thing}, depending on bools loves, hates
|
||||
|
||||
|
||||
Args:
|
||||
loves (bool): Are we looking for loves?
|
||||
hates (bool): ...OR are we looking for hates?
|
||||
user (Optional[str]): the user to query against
|
||||
thing (Optional[str]): ... OR the thing to query against
|
||||
|
||||
|
||||
Returns:
|
||||
Union[list[tuple], bool]
|
||||
"""
|
||||
|
||||
query: str = ""
|
||||
params: tuple = tuple()
|
||||
|
||||
|
||||
if not user and not thing:
|
||||
raise LoveHateException("Neither a <user> or <thing> was specified to query against")
|
||||
|
||||
raise LoveHateException(
|
||||
"Neither a <user> or <thing> was specified to query against"
|
||||
)
|
||||
|
||||
flag: Optional[int] = None
|
||||
|
||||
if hates and loves:
|
||||
@ -79,10 +93,16 @@ class DB:
|
||||
|
||||
if user:
|
||||
query = "SELECT thing FROM lovehate WHERE display_name LIKE ? AND flag == ?"
|
||||
params = (user, flag,)
|
||||
params = (
|
||||
user,
|
||||
flag,
|
||||
)
|
||||
elif thing:
|
||||
query = "SELECT display_name FROM lovehate WHERE thing LIKE ? AND flag == ?"
|
||||
params = (thing, flag,)
|
||||
params = (
|
||||
thing,
|
||||
flag,
|
||||
)
|
||||
|
||||
async with sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
||||
async with await db_conn.execute(query, params) as db_cursor:
|
||||
@ -91,22 +111,27 @@ class DB:
|
||||
return False
|
||||
return result
|
||||
|
||||
|
||||
async def check_existence(self, user: str, thing: str) -> Optional[int]:
|
||||
"""
|
||||
Determine whether a user is opinionated on a <thing>
|
||||
|
||||
|
||||
Args:
|
||||
user (str): The user to check
|
||||
thing (str): The thing to check if the user has an opinion on
|
||||
Returns:
|
||||
Optional[int]
|
||||
"""
|
||||
|
||||
params = (user, thing,)
|
||||
|
||||
params = (
|
||||
user,
|
||||
thing,
|
||||
)
|
||||
|
||||
async with sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
||||
async with await db_conn.execute("SELECT id, flag FROM lovehate WHERE display_name LIKE ? AND thing LIKE ?", params) as db_cursor:
|
||||
async with await db_conn.execute(
|
||||
"SELECT id, flag FROM lovehate WHERE display_name LIKE ? AND thing LIKE ?",
|
||||
params,
|
||||
) as db_cursor:
|
||||
result = await db_cursor.fetchone()
|
||||
if not result:
|
||||
return None
|
||||
@ -116,29 +141,38 @@ class DB:
|
||||
async def update(self, user: str, thing: str, flag: int) -> str:
|
||||
"""
|
||||
Updates the lovehate database, and returns an appropriate response
|
||||
|
||||
|
||||
Args:
|
||||
user (str): The user to update
|
||||
thing (str): The thing the user loves/hates/doesn't care about anymore
|
||||
flag (int): int representation of love (1), hate (-1), and dontcare (0)
|
||||
Returns:
|
||||
Returns:
|
||||
str
|
||||
"""
|
||||
if not flag in range(-1, 2):
|
||||
raise LoveHateException(f"Invalid flag {flag} specified, is this love (1), hate (-1), or dontcare? (0)")
|
||||
raise LoveHateException(
|
||||
f"Invalid flag {flag} specified, is this love (1), hate (-1), or dontcare? (0)"
|
||||
)
|
||||
|
||||
db_query: str = ""
|
||||
params: tuple = (user, thing,)
|
||||
params: tuple = (
|
||||
user,
|
||||
thing,
|
||||
)
|
||||
|
||||
already_opinionated: Optional[int] = await self.check_existence(user, thing)
|
||||
if already_opinionated:
|
||||
if already_opinionated:
|
||||
if flag == 0:
|
||||
db_query = "DELETE FROM lovehate WHERE display_name LIKE ? AND thing LIKE ?"
|
||||
db_query = (
|
||||
"DELETE FROM lovehate WHERE display_name LIKE ? AND thing LIKE ?"
|
||||
)
|
||||
else:
|
||||
loves_or_hates: str = "loves"
|
||||
if already_opinionated == -1:
|
||||
loves_or_hates = "hates"
|
||||
raise LoveHateException(f"But {user} already {loves_or_hates} {thing}...")
|
||||
raise LoveHateException(
|
||||
f"But {user} already {loves_or_hates} {thing}..."
|
||||
)
|
||||
else:
|
||||
match flag:
|
||||
case -1:
|
||||
@ -147,19 +181,22 @@ class DB:
|
||||
db_query = "INSERT INTO lovehate(display_name, flag, thing) VALUES(?, 1, ?)"
|
||||
case _:
|
||||
raise LoveHateException("Unknown error, default case matched")
|
||||
|
||||
|
||||
async with sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
||||
async with await db_conn.execute(db_query, params) as db_cursor:
|
||||
await db_conn.commit()
|
||||
if db_cursor.rowcount != 1:
|
||||
raise LoveHateException(f"DB Error - RowCount: {db_cursor.rowcount} for INSERT query")
|
||||
raise LoveHateException(
|
||||
f"DB Error - RowCount: {db_cursor.rowcount} for INSERT query"
|
||||
)
|
||||
match flag:
|
||||
case -1:
|
||||
return f"We're done here, {user} hates {thing}."
|
||||
case 0:
|
||||
case 0:
|
||||
return f"We're done here, {user} no longer cares one way or the other about {thing}."
|
||||
case 1:
|
||||
return f"We're done here, {user} loves {thing}."
|
||||
case _:
|
||||
raise LoveHateException("Unknown error, default case matched [2]")
|
||||
raise LoveHateException(
|
||||
"Unknown error, default case matched [2]"
|
||||
)
|
||||
|
@ -11,144 +11,205 @@ from aiohttp import ClientSession, ClientTimeout
|
||||
from bohancompliment import ComplimentGenerator
|
||||
from discord import Embed
|
||||
|
||||
|
||||
class Util:
|
||||
"""Misc Utility"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.URL_URBANDICTIONARY: str = "http://api.urbandictionary.com/v0/define"
|
||||
self.URL_INSULTAPI: str = "https://insult.mattbas.org/api/insult"
|
||||
self.COMPLIMENT_GENERATOR = ComplimentGenerator()
|
||||
self.dbs: dict[str, str|LiteralString] = {
|
||||
'whisky': os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "whiskey.db"),
|
||||
'drinks': os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "cocktails.db"),
|
||||
'strains': os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "strains.db"),
|
||||
'qajoke': os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "qajoke.db"),
|
||||
'rjokes': os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "rjokes.db"),
|
||||
'randmsg': os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "randmsg.db"),
|
||||
'stats': os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "havoc_stats.db"),
|
||||
'cookies': os.path.join("/usr/local/share",
|
||||
"sqlite_dbs", "cookies.db"),
|
||||
self.dbs: dict[str, str | LiteralString] = {
|
||||
"whisky": os.path.join("/usr/local/share", "sqlite_dbs", "whiskey.db"),
|
||||
"drinks": os.path.join("/usr/local/share", "sqlite_dbs", "cocktails.db"),
|
||||
"strains": os.path.join("/usr/local/share", "sqlite_dbs", "strains.db"),
|
||||
"qajoke": os.path.join("/usr/local/share", "sqlite_dbs", "qajoke.db"),
|
||||
"rjokes": os.path.join("/usr/local/share", "sqlite_dbs", "rjokes.db"),
|
||||
"randmsg": os.path.join("/usr/local/share", "sqlite_dbs", "randmsg.db"),
|
||||
"stats": os.path.join("/usr/local/share", "sqlite_dbs", "havoc_stats.db"),
|
||||
"cookies": os.path.join("/usr/local/share", "sqlite_dbs", "cookies.db"),
|
||||
}
|
||||
self.COFFEES: list = ['a cup of french-pressed coffee', 'a cup of cold brew', 'a cup of flash brew',
|
||||
'a cup of Turkish coffee', 'a cup of Moka', 'an espresso',
|
||||
'a cup of Nescafe coffee',
|
||||
'an iced coffee', 'a Frappé', 'a freddo cappuccino',
|
||||
'a cup of Chock full o\'Nuts', 'a cup of Folgers', 'a cup of Lavazza',
|
||||
'a cup of Maxwell House', 'a cup of Moccona', 'a cup of Mr. Brown Coffee',
|
||||
'a cup of affogato al caffè',
|
||||
'a cup of Caffè Medici', 'a cup of Café Touba',
|
||||
'a double-double', 'an indian filter coffee', 'a cup of pocillo',
|
||||
'a cup of caffè americano', 'a cup of caffè lungo', 'a latte', 'a manilo',
|
||||
'a flat white', 'a cup of café cubano', 'a cup of caffè crema',
|
||||
'a cup of cafe zorro', 'an espresso roberto', 'an espresso romano',
|
||||
'an espresso sara', 'a guillermo', 'a ristretto', 'a cup of melya',
|
||||
'a cup of caffè marocchino', 'a cup of café miel', 'a cup of café de olla',
|
||||
'a Mazagran', 'a Palazzo', 'an ice shot', 'a macchiato',
|
||||
'a cortado', 'a red eye', 'a cappuccino',
|
||||
'a mocha', 'a café au lait', 'a bicerin',
|
||||
'a caffè corretto', 'a ca phe trung', 'a café bombón', 'a Vienna coffee',
|
||||
'a flat black', 'a lungo', 'a doppio', 'a ristretto bianco', 'a piccolo latte',
|
||||
'a gibraltar', 'a breve', 'a café con leche', 'a su café', 'a café del tiempo',
|
||||
'a java chip frappuccino', 'a pumpkin spice latte', 'a caramel macchiato',
|
||||
'a white chocolate mocha', 'a hazelnut coffee', 'a toffee nut latte',
|
||||
'a peppermint mocha', 'a cinnamon dolce latte', 'a coconut milk latte',
|
||||
'an almond milk cappuccino', 'an oat milk latte', 'a caramel frappuccino',
|
||||
'a chocolate frappuccino', 'a butter pecan coffee', 'a maple pecan latte',
|
||||
'a sea salt caramel mocha', 'a nitro cold brew', 'a pumpkin cold brew',
|
||||
'a honey almond flat white', 'a sweet cream cold brew', 'a matcha latte',
|
||||
'a golden latte', 'a turmeric latte', 'a beetroot latte', 'a kopi luwak']
|
||||
self.COFFEES: list = [
|
||||
"a cup of french-pressed coffee",
|
||||
"a cup of cold brew",
|
||||
"a cup of flash brew",
|
||||
"a cup of Turkish coffee",
|
||||
"a cup of Moka",
|
||||
"an espresso",
|
||||
"a cup of Nescafe coffee",
|
||||
"an iced coffee",
|
||||
"a Frappé",
|
||||
"a freddo cappuccino",
|
||||
"a cup of Chock full o'Nuts",
|
||||
"a cup of Folgers",
|
||||
"a cup of Lavazza",
|
||||
"a cup of Maxwell House",
|
||||
"a cup of Moccona",
|
||||
"a cup of Mr. Brown Coffee",
|
||||
"a cup of affogato al caffè",
|
||||
"a cup of Caffè Medici",
|
||||
"a cup of Café Touba",
|
||||
"a double-double",
|
||||
"an indian filter coffee",
|
||||
"a cup of pocillo",
|
||||
"a cup of caffè americano",
|
||||
"a cup of caffè lungo",
|
||||
"a latte",
|
||||
"a manilo",
|
||||
"a flat white",
|
||||
"a cup of café cubano",
|
||||
"a cup of caffè crema",
|
||||
"a cup of cafe zorro",
|
||||
"an espresso roberto",
|
||||
"an espresso romano",
|
||||
"an espresso sara",
|
||||
"a guillermo",
|
||||
"a ristretto",
|
||||
"a cup of melya",
|
||||
"a cup of caffè marocchino",
|
||||
"a cup of café miel",
|
||||
"a cup of café de olla",
|
||||
"a Mazagran",
|
||||
"a Palazzo",
|
||||
"an ice shot",
|
||||
"a macchiato",
|
||||
"a cortado",
|
||||
"a red eye",
|
||||
"a cappuccino",
|
||||
"a mocha",
|
||||
"a café au lait",
|
||||
"a bicerin",
|
||||
"a caffè corretto",
|
||||
"a ca phe trung",
|
||||
"a café bombón",
|
||||
"a Vienna coffee",
|
||||
"a flat black",
|
||||
"a lungo",
|
||||
"a doppio",
|
||||
"a ristretto bianco",
|
||||
"a piccolo latte",
|
||||
"a gibraltar",
|
||||
"a breve",
|
||||
"a café con leche",
|
||||
"a su café",
|
||||
"a café del tiempo",
|
||||
"a java chip frappuccino",
|
||||
"a pumpkin spice latte",
|
||||
"a caramel macchiato",
|
||||
"a white chocolate mocha",
|
||||
"a hazelnut coffee",
|
||||
"a toffee nut latte",
|
||||
"a peppermint mocha",
|
||||
"a cinnamon dolce latte",
|
||||
"a coconut milk latte",
|
||||
"an almond milk cappuccino",
|
||||
"an oat milk latte",
|
||||
"a caramel frappuccino",
|
||||
"a chocolate frappuccino",
|
||||
"a butter pecan coffee",
|
||||
"a maple pecan latte",
|
||||
"a sea salt caramel mocha",
|
||||
"a nitro cold brew",
|
||||
"a pumpkin cold brew",
|
||||
"a honey almond flat white",
|
||||
"a sweet cream cold brew",
|
||||
"a matcha latte",
|
||||
"a golden latte",
|
||||
"a turmeric latte",
|
||||
"a beetroot latte",
|
||||
"a kopi luwak",
|
||||
]
|
||||
self.LAST_5_COFFEES: list = []
|
||||
|
||||
|
||||
def tdTuple(self, td:datetime.timedelta) -> tuple:
|
||||
def tdTuple(self, td: datetime.timedelta) -> tuple:
|
||||
"""
|
||||
Create TimeDelta Tuple
|
||||
|
||||
|
||||
Args:
|
||||
td (datetime.timedelta)
|
||||
Returns:
|
||||
tuple
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def _t(t, n):
|
||||
if t < n:
|
||||
if t < n:
|
||||
return (t, 0)
|
||||
v = t//n
|
||||
return (t - (v * n), v)
|
||||
v = t // n
|
||||
return (t - (v * n), v)
|
||||
|
||||
(s, h) = _t(td.seconds, 3600)
|
||||
(s, m) = _t(s, 60)
|
||||
(s, m) = _t(s, 60)
|
||||
(mics, mils) = _t(td.microseconds, 1000)
|
||||
return (td.days, h, m, s, mics, mils)
|
||||
|
||||
async def get_counter(self, counter: Optional[str] = None) -> Optional[dict]:
|
||||
"""
|
||||
Get Counter
|
||||
|
||||
|
||||
Args:
|
||||
counter (Optional[str])
|
||||
Returns:
|
||||
Optional[dict]
|
||||
|
||||
|
||||
"""
|
||||
stats_db: str|LiteralString = self.dbs.get('stats', '')
|
||||
stats_db: str | LiteralString = self.dbs.get("stats", "")
|
||||
if not stats_db:
|
||||
return None
|
||||
async with sqlite3.connect(stats_db,
|
||||
timeout=3) as db_conn:
|
||||
async with sqlite3.connect(stats_db, timeout=3) as db_conn:
|
||||
db_conn.row_factory = sqlite3.Row
|
||||
query: str = "SELECT ? FROM stats LIMIT 1"
|
||||
if not counter:
|
||||
query = "SELECT * FROM stats LIMIT 1"
|
||||
async with await db_conn.execute(query, (counter,) if counter else None) as db_cursor:
|
||||
async with await db_conn.execute(
|
||||
query, (counter,) if counter else None
|
||||
) as db_cursor:
|
||||
result = await db_cursor.fetchone()
|
||||
return result
|
||||
|
||||
|
||||
async def get_stats_embed(self) -> Optional[Embed]:
|
||||
"""
|
||||
Get Stats Embed
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[Embed]
|
||||
|
||||
|
||||
"""
|
||||
counters: Optional[dict] = await self.get_counter()
|
||||
if not counters:
|
||||
return None
|
||||
embed: Embed = Embed(title="Stats")
|
||||
counter_message: str = ""
|
||||
counters_sorted: dict = dict(sorted(counters.items(),
|
||||
key=lambda item: item[1], reverse=True))
|
||||
counters_sorted: dict = dict(
|
||||
sorted(counters.items(), key=lambda item: item[1], reverse=True)
|
||||
)
|
||||
for counter, value in counters_sorted.items():
|
||||
counter = regex.sub(r'_', ' ',
|
||||
counter.strip()).title()
|
||||
counter = regex.sub(r"_", " ", counter.strip()).title()
|
||||
counter_message += f"- {value} {counter}\n"
|
||||
embed.description = counter_message.strip()
|
||||
return embed
|
||||
|
||||
|
||||
async def increment_counter(self, counter: str) -> bool:
|
||||
"""
|
||||
Increment Counter
|
||||
|
||||
|
||||
Args:
|
||||
counter (str)
|
||||
Returns:
|
||||
bool
|
||||
|
||||
|
||||
"""
|
||||
stats_db: str|LiteralString = self.dbs.get('stats', '')
|
||||
stats_db: str | LiteralString = self.dbs.get("stats", "")
|
||||
if not stats_db:
|
||||
return False
|
||||
async with sqlite3.connect(stats_db,
|
||||
timeout=3) as db_conn:
|
||||
async with await db_conn.execute(f"UPDATE stats SET {counter} = {counter} + 1") as db_cursor:
|
||||
async with sqlite3.connect(stats_db, timeout=3) as db_conn:
|
||||
async with await db_conn.execute(
|
||||
f"UPDATE stats SET {counter} = {counter} + 1"
|
||||
) as db_cursor:
|
||||
if db_cursor.rowcount < 0:
|
||||
logging.critical("[karma::increment_counter] Fail! %s", db_cursor.rowcount)
|
||||
logging.critical(
|
||||
"[karma::increment_counter] Fail! %s", db_cursor.rowcount
|
||||
)
|
||||
return False
|
||||
await db_conn.commit()
|
||||
return True
|
||||
@ -156,32 +217,39 @@ class Util:
|
||||
async def get_ud_def(self, term: str) -> tuple[str, str]:
|
||||
"""
|
||||
Get Definition from UD
|
||||
|
||||
|
||||
Args:
|
||||
term (str)
|
||||
Returns:
|
||||
tuple[str, str]
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
async with ClientSession() as session:
|
||||
async with await session.get(self.URL_URBANDICTIONARY,
|
||||
params={
|
||||
"term": term,
|
||||
},
|
||||
headers = {
|
||||
'content-type': 'application/json; charset=utf-8',
|
||||
}, timeout=ClientTimeout(connect=5, sock_read=5)) as request:
|
||||
logging.debug("UD returned: %s",
|
||||
await request.text())
|
||||
async with await session.get(
|
||||
self.URL_URBANDICTIONARY,
|
||||
params={
|
||||
"term": term,
|
||||
},
|
||||
headers={
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
},
|
||||
timeout=ClientTimeout(connect=5, sock_read=5),
|
||||
) as request:
|
||||
logging.debug("UD returned: %s", await request.text())
|
||||
data: dict = await request.json()
|
||||
if "list" in data:
|
||||
definitions: list[dict] = data["list"]
|
||||
if definitions:
|
||||
definition: dict = definitions[0]
|
||||
definition_word: str = definition.get("word", "N/A")
|
||||
definition_text: str = regex.sub(r'(\r|\n|\r\n)', ' ', definition["definition"].strip())
|
||||
return (definition_word, definition_text) # Tuple: Returned word, returned definition
|
||||
definition_text: str = regex.sub(
|
||||
r"(\r|\n|\r\n)", " ", definition["definition"].strip()
|
||||
)
|
||||
return (
|
||||
definition_word,
|
||||
definition_text,
|
||||
) # Tuple: Returned word, returned definition
|
||||
else:
|
||||
return (term, "Not found!")
|
||||
else:
|
||||
@ -193,24 +261,24 @@ class Util:
|
||||
async def get_insult(self, recipient: str) -> str:
|
||||
"""
|
||||
Get Insult
|
||||
|
||||
|
||||
Args:
|
||||
recipient (str)
|
||||
Returns:
|
||||
str
|
||||
|
||||
|
||||
"""
|
||||
async with ClientSession() as session:
|
||||
async with await session.get(f"{self.URL_INSULTAPI}?who={recipient}") as request:
|
||||
async with await session.get(
|
||||
f"{self.URL_INSULTAPI}?who={recipient}"
|
||||
) as request:
|
||||
request.raise_for_status()
|
||||
return await request.text()
|
||||
|
||||
|
||||
async def get_compliment(self, subject: str,
|
||||
language: Optional[str] = 'en') -> str:
|
||||
async def get_compliment(self, subject: str, language: Optional[str] = "en") -> str:
|
||||
"""
|
||||
Get Compliment
|
||||
|
||||
|
||||
Args:
|
||||
subject (str)
|
||||
language (Optional[str]) (default: 'en')
|
||||
@ -223,95 +291,117 @@ class Util:
|
||||
async def get_whisky(self) -> Optional[tuple]:
|
||||
"""
|
||||
Get Whisky
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[tuple]
|
||||
|
||||
|
||||
"""
|
||||
whisky_db: str|LiteralString = self.dbs.get('whisky', '')
|
||||
whisky_db: str | LiteralString = self.dbs.get("whisky", "")
|
||||
if not whisky_db:
|
||||
return None
|
||||
async with sqlite3.connect(database=whisky_db,
|
||||
timeout=2) as db_conn:
|
||||
db_query: str = "SELECT name, category, description FROM whiskeys ORDER BY random() LIMIT 1"
|
||||
async with sqlite3.connect(database=whisky_db, timeout=2) as db_conn:
|
||||
db_query: str = (
|
||||
"SELECT name, category, description FROM whiskeys ORDER BY random() LIMIT 1"
|
||||
)
|
||||
async with await db_conn.execute(db_query) as db_cursor:
|
||||
db_result: Optional[Union[sqlite3.Row, tuple]] = await db_cursor.fetchone()
|
||||
db_result: Optional[Union[sqlite3.Row, tuple]] = (
|
||||
await db_cursor.fetchone()
|
||||
)
|
||||
if not db_result:
|
||||
return None
|
||||
(name, category, description) = db_result
|
||||
name = regex.sub(r'(^\p{White_Space}|\r|\n)', '',
|
||||
regex.sub(r'\p{White_Space}{2,}', ' ',
|
||||
name.strip()))
|
||||
category = regex.sub(r'(^\p{White_Space}|\r|\n)', '',
|
||||
regex.sub(r'\p{White_Space}{2,}', ' ',
|
||||
category.strip()))
|
||||
description = regex.sub(r'(^\p{White_Space}|\r|\n)', '',
|
||||
regex.sub(r'\p{White_Space}{2,}', ' ',
|
||||
description.strip()))
|
||||
name = regex.sub(
|
||||
r"(^\p{White_Space}|\r|\n)",
|
||||
"",
|
||||
regex.sub(r"\p{White_Space}{2,}", " ", name.strip()),
|
||||
)
|
||||
category = regex.sub(
|
||||
r"(^\p{White_Space}|\r|\n)",
|
||||
"",
|
||||
regex.sub(r"\p{White_Space}{2,}", " ", category.strip()),
|
||||
)
|
||||
description = regex.sub(
|
||||
r"(^\p{White_Space}|\r|\n)",
|
||||
"",
|
||||
regex.sub(r"\p{White_Space}{2,}", " ", description.strip()),
|
||||
)
|
||||
return (name, category, description)
|
||||
|
||||
|
||||
async def get_drink(self) -> Optional[tuple]:
|
||||
"""
|
||||
Get Drink
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[tuple]
|
||||
|
||||
|
||||
"""
|
||||
drinks_db: str|LiteralString = self.dbs.get('drinks', '')
|
||||
drinks_db: str | LiteralString = self.dbs.get("drinks", "")
|
||||
if not drinks_db:
|
||||
return None
|
||||
async with sqlite3.connect(database=drinks_db,
|
||||
timeout=2) as db_conn:
|
||||
db_query: str = "SELECT name, ingredients FROM cocktails ORDER BY random() LIMIT 1"
|
||||
async with sqlite3.connect(database=drinks_db, timeout=2) as db_conn:
|
||||
db_query: str = (
|
||||
"SELECT name, ingredients FROM cocktails ORDER BY random() LIMIT 1"
|
||||
)
|
||||
async with await db_conn.execute(db_query) as db_cursor:
|
||||
db_result: tuple = await db_cursor.fetchone()
|
||||
|
||||
|
||||
(name, ingredients) = db_result
|
||||
name = regex.sub(r'(^\p{White_Space}|\r|\n)', '', regex.sub(r'\p{White_Space}{2,}', ' ', name.strip()))
|
||||
ingredients = regex.sub(r'(^\p{White_Space}|\r|\n)', '', regex.sub(r'\p{White_Space}{2,}', ' ', ingredients.strip()))
|
||||
ingredients = regex.sub(r'\*', '\u2731', ingredients.strip())
|
||||
name = regex.sub(
|
||||
r"(^\p{White_Space}|\r|\n)",
|
||||
"",
|
||||
regex.sub(r"\p{White_Space}{2,}", " ", name.strip()),
|
||||
)
|
||||
ingredients = regex.sub(
|
||||
r"(^\p{White_Space}|\r|\n)",
|
||||
"",
|
||||
regex.sub(r"\p{White_Space}{2,}", " ", ingredients.strip()),
|
||||
)
|
||||
ingredients = regex.sub(r"\*", "\u2731", ingredients.strip())
|
||||
return (name, ingredients)
|
||||
|
||||
|
||||
async def get_strain(self, strain: Optional[str] = None) -> Optional[tuple]:
|
||||
"""
|
||||
Get Strain
|
||||
|
||||
|
||||
Args:
|
||||
strain (Optional[str])
|
||||
Returns:
|
||||
Optional[tuple]
|
||||
|
||||
|
||||
"""
|
||||
strains_db: str|LiteralString = self.dbs.get('strains', '')
|
||||
strains_db: str | LiteralString = self.dbs.get("strains", "")
|
||||
if not strains_db:
|
||||
return None
|
||||
async with sqlite3.connect(database=strains_db,
|
||||
timeout=2) as db_conn:
|
||||
async with sqlite3.connect(database=strains_db, timeout=2) as db_conn:
|
||||
db_params: Optional[tuple] = None
|
||||
if not strain:
|
||||
db_query: str = "SELECT name, description FROM strains_w_desc ORDER BY random() LIMIT 1"
|
||||
db_query: str = (
|
||||
"SELECT name, description FROM strains_w_desc ORDER BY random() LIMIT 1"
|
||||
)
|
||||
else:
|
||||
db_query = "SELECT name, description FROM strains_w_desc WHERE name LIKE ?"
|
||||
db_params = (f"%{strain.strip()}%",)
|
||||
db_query = (
|
||||
"SELECT name, description FROM strains_w_desc WHERE name LIKE ?"
|
||||
)
|
||||
db_params = (f"%{strain.strip()}%",)
|
||||
async with await db_conn.execute(db_query, db_params) as db_cursor:
|
||||
db_result: Optional[tuple] = await db_cursor.fetchone()
|
||||
return db_result
|
||||
|
||||
|
||||
async def get_qajoke(self) -> Optional[tuple]:
|
||||
"""
|
||||
Get QA Joke
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[tuple]
|
||||
|
||||
|
||||
"""
|
||||
qajoke_db: str|LiteralString = self.dbs.get('qajoke', '')
|
||||
qajoke_db: str | LiteralString = self.dbs.get("qajoke", "")
|
||||
if not qajoke_db:
|
||||
return None
|
||||
async with sqlite3.connect(database=qajoke_db,
|
||||
timeout=2) as db_conn:
|
||||
db_query: str = "SELECT question, answer FROM jokes ORDER BY RANDOM() LIMIT 1"
|
||||
async with sqlite3.connect(database=qajoke_db, timeout=2) as db_conn:
|
||||
db_query: str = (
|
||||
"SELECT question, answer FROM jokes ORDER BY RANDOM() LIMIT 1"
|
||||
)
|
||||
async with await db_conn.execute(db_query) as cursor:
|
||||
(question, answer) = await cursor.fetchone()
|
||||
return (question, answer)
|
||||
@ -320,107 +410,110 @@ class Util:
|
||||
async def get_rjoke(self) -> Optional[tuple]:
|
||||
"""
|
||||
Get r/joke Joke
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[tuple]
|
||||
|
||||
|
||||
"""
|
||||
rjokes_db: str|LiteralString = self.dbs.get('rjokes', '')
|
||||
rjokes_db: str | LiteralString = self.dbs.get("rjokes", "")
|
||||
if not rjokes_db:
|
||||
return None
|
||||
async with sqlite3.connect(database=rjokes_db, timeout=2) as db_conn:
|
||||
db_query: str = "SELECT title, body, score FROM jokes WHERE score >= 100 ORDER BY RANDOM() LIMIT 1'"
|
||||
db_query: str = (
|
||||
"SELECT title, body, score FROM jokes WHERE score >= 100 ORDER BY RANDOM() LIMIT 1'"
|
||||
)
|
||||
async with await db_conn.execute(db_query) as cursor:
|
||||
(title, body, score) = await cursor.fetchone()
|
||||
return (title, body, score)
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
async def get_random_fact(self) -> str:
|
||||
"""
|
||||
Get Random Fact
|
||||
|
||||
|
||||
Returns:
|
||||
str
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
facts_api_url: str = "https://uselessfacts.jsph.pl/api/v2/facts/random"
|
||||
facts_backup_url: str = "https://cnichols1734.pythonanywhere.com/facts/random"
|
||||
facts_backup_url: str = (
|
||||
"https://cnichols1734.pythonanywhere.com/facts/random"
|
||||
)
|
||||
async with ClientSession() as client:
|
||||
try:
|
||||
async with await client.get(facts_api_url,
|
||||
timeout=ClientTimeout(connect=5, sock_read=5)) as request:
|
||||
async with await client.get(
|
||||
facts_api_url, timeout=ClientTimeout(connect=5, sock_read=5)
|
||||
) as request:
|
||||
_json: dict = await request.json()
|
||||
fact: str = _json.get('text', None)
|
||||
fact: str = _json.get("text", None)
|
||||
if not fact:
|
||||
raise BaseException("RandFact Src 1 Failed")
|
||||
return fact
|
||||
except:
|
||||
async with await client.get(facts_backup_url,
|
||||
timeout=ClientTimeout(connect=5, sock_read=5)) as request:
|
||||
async with await client.get(
|
||||
facts_backup_url, timeout=ClientTimeout(connect=5, sock_read=5)
|
||||
) as request:
|
||||
_json = await request.json()
|
||||
fact = _json.get('fact', None)
|
||||
fact = _json.get("fact", None)
|
||||
return fact
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return f"Failed to get a random fact :( [{str(e)}]"
|
||||
|
||||
|
||||
async def get_cookie(self) -> Optional[dict]:
|
||||
"""
|
||||
Get Cookie
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[dict]
|
||||
|
||||
|
||||
"""
|
||||
cookies_db = self.dbs.get('cookies', '')
|
||||
cookies_db = self.dbs.get("cookies", "")
|
||||
if not cookies_db:
|
||||
return None
|
||||
async with sqlite3.connect(cookies_db,
|
||||
timeout=2) as db_conn:
|
||||
db_query: str = "SELECT name, origin, image_url FROM cookies ORDER BY RANDOM() LIMIT 1"
|
||||
async with sqlite3.connect(cookies_db, timeout=2) as db_conn:
|
||||
db_query: str = (
|
||||
"SELECT name, origin, image_url FROM cookies ORDER BY RANDOM() LIMIT 1"
|
||||
)
|
||||
async with await db_conn.execute(db_query) as db_cursor:
|
||||
(name, origin, image_url) = await db_cursor.fetchone()
|
||||
return {
|
||||
'name': name,
|
||||
'origin': origin,
|
||||
'image_url': image_url
|
||||
}
|
||||
|
||||
return {"name": name, "origin": origin, "image_url": image_url}
|
||||
|
||||
def get_coffee(self,
|
||||
recipient_allergic: Optional[bool] = False) -> Optional[str]:
|
||||
def get_coffee(self, recipient_allergic: Optional[bool] = False) -> Optional[str]:
|
||||
"""
|
||||
Get Coffee
|
||||
|
||||
|
||||
Args:
|
||||
recipient_allergic (bool): Is the recipient allergic? (so we know when to keep our nuts out of it)
|
||||
|
||||
|
||||
Returns:
|
||||
str
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
randomCoffee: str = random.choice(self.COFFEES)
|
||||
if self.LAST_5_COFFEES and randomCoffee in self.LAST_5_COFFEES\
|
||||
or (recipient_allergic and "nut" in randomCoffee.lower()):
|
||||
return self.get_coffee() # Recurse
|
||||
if (
|
||||
self.LAST_5_COFFEES
|
||||
and randomCoffee in self.LAST_5_COFFEES
|
||||
or (recipient_allergic and "nut" in randomCoffee.lower())
|
||||
):
|
||||
return self.get_coffee() # Recurse
|
||||
if len(self.LAST_5_COFFEES) >= 5:
|
||||
self.LAST_5_COFFEES.pop() # Store no more than 5 of the last served coffees
|
||||
self.LAST_5_COFFEES.pop() # Store no more than 5 of the last served coffees
|
||||
self.LAST_5_COFFEES.append(randomCoffee)
|
||||
return randomCoffee
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
def get_days_to_xmas(self) -> Optional[tuple]:
|
||||
"""
|
||||
Get # of Days until Xmas
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[tuple]
|
||||
|
||||
|
||||
"""
|
||||
today: datetime.datetime = datetime.datetime.now(tz=pytz.UTC)
|
||||
xmas: datetime.datetime = datetime.datetime(
|
||||
@ -429,25 +522,24 @@ class Util:
|
||||
day=25,
|
||||
tzinfo=pytz.UTC,
|
||||
)
|
||||
td: datetime.timedelta = (xmas - today)
|
||||
td: datetime.timedelta = xmas - today
|
||||
days, hours, minutes, seconds, us, ms = self.tdTuple(td)
|
||||
|
||||
|
||||
return (days, hours, minutes, seconds, ms, us)
|
||||
|
||||
|
||||
async def get_randmsg(self) -> Optional[str]:
|
||||
"""
|
||||
Get Random Message from randmsg.db
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[str]
|
||||
|
||||
|
||||
"""
|
||||
randmsg_db: str|LiteralString = self.dbs.get('randmsg', '')
|
||||
randmsg_db: str | LiteralString = self.dbs.get("randmsg", "")
|
||||
if not randmsg_db:
|
||||
return None
|
||||
async with sqlite3.connect(database=randmsg_db,
|
||||
timeout=2) as db_conn:
|
||||
async with sqlite3.connect(database=randmsg_db, timeout=2) as db_conn:
|
||||
db_query: str = "SELECT msg FROM msgs ORDER BY RANDOM() LIMIT 1"
|
||||
async with await db_conn.execute(db_query) as db_cursor:
|
||||
(result,) = await db_cursor.fetchone()
|
||||
return result
|
||||
return result
|
||||
|
@ -4,6 +4,7 @@ from aiohttp import ClientSession, ClientTimeout
|
||||
|
||||
"""Radio Utils"""
|
||||
|
||||
|
||||
async def get_now_playing() -> Optional[str]:
|
||||
"""
|
||||
Get radio now playing
|
||||
@ -15,17 +16,21 @@ async def get_now_playing() -> Optional[str]:
|
||||
np_url: str = "https://api.codey.lol/radio/np"
|
||||
try:
|
||||
async with ClientSession() as session:
|
||||
async with await session.post(np_url, headers={
|
||||
'content-type': 'application/json; charset=utf-8',
|
||||
}, timeout=ClientTimeout(connect=1.5, sock_read=1.5)) as request:
|
||||
async with await session.post(
|
||||
np_url,
|
||||
headers={
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
},
|
||||
timeout=ClientTimeout(connect=1.5, sock_read=1.5),
|
||||
) as request:
|
||||
request.raise_for_status()
|
||||
response_json = await request.json()
|
||||
artistsong = response_json.get('artistsong')
|
||||
artistsong = response_json.get("artistsong")
|
||||
return artistsong
|
||||
except Exception as e:
|
||||
logging.critical("Now playing retrieval failed: %s",
|
||||
str(e))
|
||||
return None
|
||||
logging.critical("Now playing retrieval failed: %s", str(e))
|
||||
return None
|
||||
|
||||
|
||||
async def skip() -> bool:
|
||||
"""
|
||||
@ -38,13 +43,13 @@ async def skip() -> bool:
|
||||
try:
|
||||
ls_uri: str = "http://127.0.0.1:29000"
|
||||
async with ClientSession() as session:
|
||||
async with session.get(f"{ls_uri}/next",
|
||||
timeout=ClientTimeout(connect=2, sock_read=2)) as request:
|
||||
request.raise_for_status()
|
||||
text: Optional[str] = await request.text()
|
||||
return text == "OK"
|
||||
async with session.get(
|
||||
f"{ls_uri}/next", timeout=ClientTimeout(connect=2, sock_read=2)
|
||||
) as request:
|
||||
request.raise_for_status()
|
||||
text: Optional[str] = await request.text()
|
||||
return text == "OK"
|
||||
except Exception as e:
|
||||
logging.debug("Skip failed: %s", str(e))
|
||||
|
||||
return False # failsafe
|
||||
|
||||
logging.debug("Skip failed: %s", str(e))
|
||||
|
||||
return False # failsafe
|
||||
|
@ -6,21 +6,24 @@ import traceback
|
||||
from discord import Activity
|
||||
from typing import Optional, Union
|
||||
|
||||
|
||||
class Utility:
|
||||
"""Sing Utility"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.api_url: str = "http://127.0.0.1:52111/lyric/search"
|
||||
self.api_src: str = "DISC-HAVOC"
|
||||
|
||||
def parse_song_input(self, song: Optional[str] = None,
|
||||
activity: Optional[Activity] = None) -> Union[bool, tuple]:
|
||||
|
||||
def parse_song_input(
|
||||
self, song: Optional[str] = None, activity: Optional[Activity] = None
|
||||
) -> Union[bool, tuple]:
|
||||
"""
|
||||
Parse Song (Sing Command) Input
|
||||
|
||||
|
||||
Args:
|
||||
song (Optional[str]): Song to search
|
||||
activity (Optional[discord.Activity]): Discord activity, used to attempt lookup if no song is provided
|
||||
|
||||
|
||||
Returns:
|
||||
Union[bool, tuple]
|
||||
"""
|
||||
@ -29,13 +32,18 @@ class Utility:
|
||||
return False
|
||||
if not song and activity:
|
||||
if not activity.name:
|
||||
return False # No valid activity found
|
||||
return False # No valid activity found
|
||||
match activity.name.lower():
|
||||
case "codey toons" | "cider" | "sonixd":
|
||||
search_artist: str = " ".join(str(activity.state)\
|
||||
.strip().split(" ")[1:])
|
||||
search_artist = regex.sub(r"(\s{0,})(\[(spotify|tidal|sonixd|browser|yt music)])$", "",
|
||||
search_artist.strip(), flags=regex.IGNORECASE)
|
||||
search_artist: str = " ".join(
|
||||
str(activity.state).strip().split(" ")[1:]
|
||||
)
|
||||
search_artist = regex.sub(
|
||||
r"(\s{0,})(\[(spotify|tidal|sonixd|browser|yt music)])$",
|
||||
"",
|
||||
search_artist.strip(),
|
||||
flags=regex.IGNORECASE,
|
||||
)
|
||||
search_song = str(activity.details)
|
||||
song = f"{search_artist} : {search_song}"
|
||||
case "tidal hi-fi":
|
||||
@ -43,110 +51,136 @@ class Utility:
|
||||
search_song = str(activity.details)
|
||||
song = f"{search_artist} : {search_song}"
|
||||
case "spotify":
|
||||
if not activity.title or not activity.artist: # type: ignore
|
||||
if not activity.title or not activity.artist: # type: ignore
|
||||
"""
|
||||
Attributes exist, but mypy does not recognize them. Ignored.
|
||||
"""
|
||||
return False
|
||||
search_artist = str(activity.title) # type: ignore
|
||||
search_song = str(activity.artist) # type: ignore
|
||||
search_artist = str(activity.title) # type: ignore
|
||||
search_song = str(activity.artist) # type: ignore
|
||||
song = f"{search_artist} : {search_song}"
|
||||
case "serious.fm" | "cocks.fm" | "something":
|
||||
if not activity.details:
|
||||
song = str(activity.state)
|
||||
else:
|
||||
search_artist = str(activity.state).rsplit("[", maxsplit=1)[0] # Strip genre
|
||||
search_artist = str(activity.state).rsplit("[", maxsplit=1)[
|
||||
0
|
||||
] # Strip genre
|
||||
search_song = str(activity.details)
|
||||
song = f"{search_artist} : {search_song}"
|
||||
case _:
|
||||
return False # Unsupported activity detected
|
||||
|
||||
search_split_by: str = ":" if not(song) or len(song.split(":")) > 1\
|
||||
else "-" # Support either : or - to separate artist/track
|
||||
return False # Unsupported activity detected
|
||||
|
||||
search_split_by: str = (
|
||||
":" if not (song) or len(song.split(":")) > 1 else "-"
|
||||
) # Support either : or - to separate artist/track
|
||||
if not song:
|
||||
return False
|
||||
search_artist = song.split(search_split_by)[0].strip()
|
||||
search_song = "".join(song.split(search_split_by)[1:]).strip()
|
||||
search_subsearch: Optional[str] = None
|
||||
if search_split_by == ":" and len(song.split(":")) > 2: # Support sub-search if : is used (per instructions)
|
||||
search_song = song.split(search_split_by)[1].strip() # Reduce search_song to only the 2nd split of : [the rest is meant to be lyric text]
|
||||
search_subsearch = "".join(song.split(search_split_by)[2:]) # Lyric text from split index 2 and beyond
|
||||
if (
|
||||
search_split_by == ":" and len(song.split(":")) > 2
|
||||
): # Support sub-search if : is used (per instructions)
|
||||
search_song = song.split(search_split_by)[
|
||||
1
|
||||
].strip() # Reduce search_song to only the 2nd split of : [the rest is meant to be lyric text]
|
||||
search_subsearch = "".join(
|
||||
song.split(search_split_by)[2:]
|
||||
) # Lyric text from split index 2 and beyond
|
||||
return (search_artist, search_song, search_subsearch)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
async def lyric_search(self, artist: str, song: str,
|
||||
sub: Optional[str] = None) -> Optional[list]:
|
||||
|
||||
async def lyric_search(
|
||||
self, artist: str, song: str, sub: Optional[str] = None
|
||||
) -> Optional[list]:
|
||||
"""
|
||||
Lyric Search
|
||||
|
||||
|
||||
Args:
|
||||
artist (str): Artist to search
|
||||
song (str): Song to search
|
||||
sub (Optional[str]): Lyrics for subsearch
|
||||
sub (Optional[str]): Lyrics for subsearch
|
||||
Returns:
|
||||
Optional[list]
|
||||
"""
|
||||
try:
|
||||
if not artist or not song:
|
||||
return [("FAIL! Artist/Song not provided",)]
|
||||
|
||||
|
||||
search_obj: dict = {
|
||||
'a': artist.strip(),
|
||||
's': song.strip(),
|
||||
'extra': True,
|
||||
'src': self.api_src,
|
||||
"a": artist.strip(),
|
||||
"s": song.strip(),
|
||||
"extra": True,
|
||||
"src": self.api_src,
|
||||
}
|
||||
|
||||
|
||||
if len(song.strip()) < 1:
|
||||
search_obj.pop('a')
|
||||
search_obj.pop('s')
|
||||
search_obj['t'] = artist.strip() # Parse failed, try title without sep
|
||||
|
||||
search_obj.pop("a")
|
||||
search_obj.pop("s")
|
||||
search_obj["t"] = artist.strip() # Parse failed, try title without sep
|
||||
|
||||
if sub and len(sub) >= 2:
|
||||
search_obj['sub'] = sub.strip()
|
||||
|
||||
search_obj["sub"] = sub.strip()
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with await session.post(self.api_url,
|
||||
json=search_obj,
|
||||
timeout=aiohttp.ClientTimeout(connect=5, sock_read=10)) as request:
|
||||
async with await session.post(
|
||||
self.api_url,
|
||||
json=search_obj,
|
||||
timeout=aiohttp.ClientTimeout(connect=5, sock_read=10),
|
||||
) as request:
|
||||
request.raise_for_status()
|
||||
response: dict = await request.json()
|
||||
if response.get('err'):
|
||||
if response.get("err"):
|
||||
return [(f"ERR: {response.get('errorText')}",)]
|
||||
|
||||
out_lyrics = regex.sub(r'<br>', '\u200B\n', response.get('lyrics', ''))
|
||||
|
||||
out_lyrics = regex.sub(
|
||||
r"<br>", "\u200b\n", response.get("lyrics", "")
|
||||
)
|
||||
response_obj: dict = {
|
||||
'artist': response.get('artist'),
|
||||
'song': response.get('song'),
|
||||
'lyrics': out_lyrics,
|
||||
'src': response.get('src'),
|
||||
'confidence': float(response.get('confidence', 0.0)),
|
||||
'time': float(response.get('time', -1.0)),
|
||||
"artist": response.get("artist"),
|
||||
"song": response.get("song"),
|
||||
"lyrics": out_lyrics,
|
||||
"src": response.get("src"),
|
||||
"confidence": float(response.get("confidence", 0.0)),
|
||||
"time": float(response.get("time", -1.0)),
|
||||
}
|
||||
|
||||
lyrics = response_obj.get('lyrics')
|
||||
|
||||
lyrics = response_obj.get("lyrics")
|
||||
if not lyrics:
|
||||
return None
|
||||
response_obj['lyrics'] = textwrap.wrap(text=lyrics.strip(),
|
||||
width=1500, drop_whitespace=False,
|
||||
replace_whitespace=False, break_long_words=True,
|
||||
break_on_hyphens=True, max_lines=8)
|
||||
response_obj['lyrics_short'] = textwrap.wrap(text=lyrics.strip(),
|
||||
width=750, drop_whitespace=False,
|
||||
replace_whitespace=False, break_long_words=True,
|
||||
break_on_hyphens=True, max_lines=1)
|
||||
|
||||
response_obj["lyrics"] = textwrap.wrap(
|
||||
text=lyrics.strip(),
|
||||
width=1500,
|
||||
drop_whitespace=False,
|
||||
replace_whitespace=False,
|
||||
break_long_words=True,
|
||||
break_on_hyphens=True,
|
||||
max_lines=8,
|
||||
)
|
||||
response_obj["lyrics_short"] = textwrap.wrap(
|
||||
text=lyrics.strip(),
|
||||
width=750,
|
||||
drop_whitespace=False,
|
||||
replace_whitespace=False,
|
||||
break_long_words=True,
|
||||
break_on_hyphens=True,
|
||||
max_lines=1,
|
||||
)
|
||||
|
||||
return [
|
||||
(
|
||||
response_obj.get('artist'), response_obj.get('song'), response_obj.get('src'),
|
||||
response_obj.get("artist"),
|
||||
response_obj.get("song"),
|
||||
response_obj.get("src"),
|
||||
f"{int(response_obj.get('confidence', -1.0))}%",
|
||||
f"{response_obj.get('time', -666.0):.4f}s",
|
||||
),
|
||||
response_obj.get('lyrics'),
|
||||
response_obj.get('lyrics_short'),
|
||||
]
|
||||
response_obj.get("lyrics"),
|
||||
response_obj.get("lyrics_short"),
|
||||
]
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return [f"Retrieval failed: {str(e)}"]
|
||||
return [f"Retrieval failed: {str(e)}"]
|
||||
|
Reference in New Issue
Block a user