reformat (black)
This commit is contained in:
parent
d12b066c8e
commit
1bb482315e
8
api.py
8
api.py
@ -6,6 +6,7 @@ from fastapi import FastAPI, HTTPException
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
import util
|
import util
|
||||||
|
|
||||||
|
|
||||||
class ValidSendMsgRequest(BaseModel):
|
class ValidSendMsgRequest(BaseModel):
|
||||||
"""
|
"""
|
||||||
- **guild**: optional, guild id in case multiple channels match (normally first result would be used)
|
- **guild**: optional, guild id in case multiple channels match (normally first result would be used)
|
||||||
@ -17,14 +18,15 @@ class ValidSendMsgRequest(BaseModel):
|
|||||||
channel: str
|
channel: str
|
||||||
message: str
|
message: str
|
||||||
|
|
||||||
|
|
||||||
class API:
|
class API:
|
||||||
"""API [FastAPI Instance] for Havoc"""
|
"""API [FastAPI Instance] for Havoc"""
|
||||||
|
|
||||||
def __init__(self, discord_bot):
|
def __init__(self, discord_bot):
|
||||||
api_app = FastAPI(title="Havoc API")
|
api_app = FastAPI(title="Havoc API")
|
||||||
self.bot = discord_bot
|
self.bot = discord_bot
|
||||||
self.api_app = api_app
|
self.api_app = api_app
|
||||||
|
|
||||||
|
|
||||||
@api_app.get("/{any:path}")
|
@api_app.get("/{any:path}")
|
||||||
def block_get():
|
def block_get():
|
||||||
raise HTTPException(status_code=403, detail="Invalid request")
|
raise HTTPException(status_code=403, detail="Invalid request")
|
||||||
@ -38,12 +40,14 @@ class API:
|
|||||||
message=data.message,
|
message=data.message,
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
'result': "presumed_success",
|
"result": "presumed_success",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def __init__():
|
def __init__():
|
||||||
import util
|
import util
|
||||||
|
|
||||||
importlib.reload(util)
|
importlib.reload(util)
|
||||||
|
|
||||||
|
|
||||||
__init__()
|
__init__()
|
119
cogs/karma.py
119
cogs/karma.py
@ -1,6 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
from os import path
|
from os import path
|
||||||
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
|
|
||||||
|
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
|
||||||
import constants
|
import constants
|
||||||
import traceback
|
import traceback
|
||||||
import time
|
import time
|
||||||
@ -14,8 +15,10 @@ from aiohttp import ClientSession, ClientTimeout
|
|||||||
from discord.ext import bridge, commands, tasks
|
from discord.ext import bridge, commands, tasks
|
||||||
from disc_havoc import Havoc
|
from disc_havoc import Havoc
|
||||||
|
|
||||||
|
|
||||||
class Util:
|
class Util:
|
||||||
"""Karma Utility"""
|
"""Karma Utility"""
|
||||||
|
|
||||||
def __init__(self, bot: Havoc):
|
def __init__(self, bot: Havoc):
|
||||||
self.bot: Havoc = bot
|
self.bot: Havoc = bot
|
||||||
self.api_key: str = constants.PRV_API_KEY
|
self.api_key: str = constants.PRV_API_KEY
|
||||||
@ -37,14 +40,17 @@ class Util:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with await session.post(self.karma_retrieval_url,
|
async with await session.post(
|
||||||
json={'keyword': keyword},
|
self.karma_retrieval_url,
|
||||||
|
json={"keyword": keyword},
|
||||||
headers={
|
headers={
|
||||||
'content-type': 'application/json; charset=utf-8',
|
"content-type": "application/json; charset=utf-8",
|
||||||
'X-Authd-With': f'Bearer {constants.KARMA_API_KEY}',
|
"X-Authd-With": f"Bearer {constants.KARMA_API_KEY}",
|
||||||
}, timeout=ClientTimeout(connect=3, sock_read=5)) as request:
|
},
|
||||||
|
timeout=ClientTimeout(connect=3, sock_read=5),
|
||||||
|
) as request:
|
||||||
resp = await request.json()
|
resp = await request.json()
|
||||||
return resp.get('count')
|
return resp.get("count")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
@ -60,21 +66,24 @@ class Util:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with await session.post(self.karma_top_10_url,
|
async with await session.post(
|
||||||
json = {
|
self.karma_top_10_url,
|
||||||
'n': n,
|
json={
|
||||||
|
"n": n,
|
||||||
},
|
},
|
||||||
headers={
|
headers={
|
||||||
'content-type': 'application/json; charset=utf-8',
|
"content-type": "application/json; charset=utf-8",
|
||||||
'X-Authd-With': f'Bearer {constants.KARMA_API_KEY}'
|
"X-Authd-With": f"Bearer {constants.KARMA_API_KEY}",
|
||||||
}, timeout=ClientTimeout(connect=3, sock_read=5)) as request:
|
},
|
||||||
|
timeout=ClientTimeout(connect=3, sock_read=5),
|
||||||
|
) as request:
|
||||||
resp: dict = await request.json()
|
resp: dict = await request.json()
|
||||||
return resp
|
return resp
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_top_embed(self, n:int = 10) -> Optional[discord.Embed]:
|
async def get_top_embed(self, n: int = 10) -> Optional[discord.Embed]:
|
||||||
"""
|
"""
|
||||||
Get Top Karma Embed
|
Get Top Karma Embed
|
||||||
|
|
||||||
@ -88,14 +97,18 @@ class Util:
|
|||||||
return None
|
return None
|
||||||
top_formatted: str = ""
|
top_formatted: str = ""
|
||||||
for x, item in enumerate(top):
|
for x, item in enumerate(top):
|
||||||
top_formatted += f"{x+1}. **{discord.utils.escape_markdown(item[0])}**: *{item[1]}*\n"
|
top_formatted += (
|
||||||
|
f"{x+1}. **{discord.utils.escape_markdown(item[0])}**: *{item[1]}*\n"
|
||||||
|
)
|
||||||
top_formatted = top_formatted.strip()
|
top_formatted = top_formatted.strip()
|
||||||
embed: discord.Embed = discord.Embed(title=f"Top {n} Karma",
|
embed: discord.Embed = discord.Embed(
|
||||||
description=top_formatted,
|
title=f"Top {n} Karma", description=top_formatted, colour=0xFF00FF
|
||||||
colour=0xff00ff)
|
)
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
async def update_karma(self, display: str, _id: int, keyword: str, flag: int) -> bool:
|
async def update_karma(
|
||||||
|
self, display: str, _id: int, keyword: str, flag: int
|
||||||
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Update Karma for Keyword
|
Update Karma for Keyword
|
||||||
Args:
|
Args:
|
||||||
@ -111,27 +124,28 @@ class Util:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
reqObj: dict = {
|
reqObj: dict = {
|
||||||
'granter': f"Discord: {display} ({_id})",
|
"granter": f"Discord: {display} ({_id})",
|
||||||
'keyword': keyword,
|
"keyword": keyword,
|
||||||
'flag': flag,
|
"flag": flag,
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with await session.post(self.karma_update_url,
|
async with await session.post(
|
||||||
|
self.karma_update_url,
|
||||||
json=reqObj,
|
json=reqObj,
|
||||||
headers={
|
headers={
|
||||||
'content-type': 'application/json; charset=utf-8',
|
"content-type": "application/json; charset=utf-8",
|
||||||
'X-Authd-With': f'Bearer {self.api_key}',
|
"X-Authd-With": f"Bearer {self.api_key}",
|
||||||
},
|
},
|
||||||
timeout=ClientTimeout(connect=3, sock_read=5)) as request:
|
timeout=ClientTimeout(connect=3, sock_read=5),
|
||||||
|
) as request:
|
||||||
result = await request.json()
|
result = await request.json()
|
||||||
return result.get('success', False)
|
return result.get("success", False)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def check_cooldown(self, user_id: int) -> bool:
|
async def check_cooldown(self, user_id: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if member has met cooldown period prior to adjusting karma
|
Check if member has met cooldown period prior to adjusting karma
|
||||||
@ -149,17 +163,19 @@ class Util:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Karma(commands.Cog):
|
class Karma(commands.Cog):
|
||||||
"""Karma Cog for Havoc"""
|
"""Karma Cog for Havoc"""
|
||||||
|
|
||||||
def __init__(self, bot: Havoc):
|
def __init__(self, bot: Havoc):
|
||||||
importlib.reload(constants)
|
importlib.reload(constants)
|
||||||
self.bot: Havoc = bot
|
self.bot: Havoc = bot
|
||||||
self.util = Util(self.bot)
|
self.util = Util(self.bot)
|
||||||
# self.karma_regex = regex.compile(r'(\w+)(\+\+|\-\-)')
|
# self.karma_regex = regex.compile(r'(\w+)(\+\+|\-\-)')
|
||||||
self.karma_regex: Pattern = regex.compile(r'(\b\w+(?:\s+\w+)*)(\+\+($|\s)|\-\-($|\s))')
|
self.karma_regex: Pattern = regex.compile(
|
||||||
self.mention_regex: Pattern = regex.compile(r'(<@([0-9]{17,20})>)(\+\+|\-\-)')
|
r"(\b\w+(?:\s+\w+)*)(\+\+($|\s)|\-\-($|\s))"
|
||||||
self.mention_regex_no_flag: Pattern = regex.compile(r'(<@([0-9]{17,20})>+)')
|
)
|
||||||
|
self.mention_regex: Pattern = regex.compile(r"(<@([0-9]{17,20})>)(\+\+|\-\-)")
|
||||||
|
self.mention_regex_no_flag: Pattern = regex.compile(r"(<@([0-9]{17,20})>+)")
|
||||||
self.karma_chanid: int = 1307065684785893406
|
self.karma_chanid: int = 1307065684785893406
|
||||||
self.karma_msgid: int = 1325442184572567686
|
self.karma_msgid: int = 1325442184572567686
|
||||||
|
|
||||||
@ -170,7 +186,6 @@ class Karma(commands.Cog):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@tasks.loop(seconds=30, reconnect=True)
|
@tasks.loop(seconds=30, reconnect=True)
|
||||||
async def update_karma_chan(self) -> None:
|
async def update_karma_chan(self) -> None:
|
||||||
"""Update the Karma Chan Leaderboard"""
|
"""Update the Karma Chan Leaderboard"""
|
||||||
@ -180,12 +195,13 @@ class Karma(commands.Cog):
|
|||||||
if not isinstance(channel, discord.TextChannel):
|
if not isinstance(channel, discord.TextChannel):
|
||||||
return
|
return
|
||||||
message_to_edit = await channel.fetch_message(self.karma_msgid)
|
message_to_edit = await channel.fetch_message(self.karma_msgid)
|
||||||
await message_to_edit.edit(embed=top_embed,
|
await message_to_edit.edit(
|
||||||
content="## This message will automatically update periodically.")
|
embed=top_embed,
|
||||||
|
content="## This message will automatically update periodically.",
|
||||||
|
)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_message(self, message: discord.Message) -> None:
|
async def on_message(self, message: discord.Message) -> None:
|
||||||
"""
|
"""
|
||||||
@ -196,21 +212,26 @@ class Karma(commands.Cog):
|
|||||||
return
|
return
|
||||||
if not isinstance(message.channel, discord.TextChannel):
|
if not isinstance(message.channel, discord.TextChannel):
|
||||||
return
|
return
|
||||||
if message.channel.id == self.karma_chanid and not message.author.id == self.bot.user.id:
|
if (
|
||||||
|
message.channel.id == self.karma_chanid
|
||||||
|
and not message.author.id == self.bot.user.id
|
||||||
|
):
|
||||||
"""Message to #karma not by Havoc, delete it"""
|
"""Message to #karma not by Havoc, delete it"""
|
||||||
await message.delete(reason="Messages to #karma are not allowed")
|
await message.delete(reason="Messages to #karma are not allowed")
|
||||||
removal_embed: discord.Embed = discord.Embed(
|
removal_embed: discord.Embed = discord.Embed(
|
||||||
title="Message Deleted",
|
title="Message Deleted",
|
||||||
description=f"Your message to **#{message.channel.name}** has been automatically deleted.\n**Reason**: Messages to this channel by users is not allowed."
|
description=f"Your message to **#{message.channel.name}** has been automatically deleted.\n**Reason**: Messages to this channel by users is not allowed.",
|
||||||
)
|
)
|
||||||
await message.author.send(embed=removal_embed)
|
await message.author.send(embed=removal_embed)
|
||||||
|
|
||||||
|
|
||||||
if message.author.id == self.bot.user.id: # Bots own message
|
if message.author.id == self.bot.user.id: # Bots own message
|
||||||
return
|
return
|
||||||
if not message.guild:
|
if not message.guild:
|
||||||
return
|
return
|
||||||
if not message.guild.id in [1145182936002482196, 1228740575235149855]: # Not a valid guild for cmd
|
if not message.guild.id in [
|
||||||
|
1145182936002482196,
|
||||||
|
1228740575235149855,
|
||||||
|
]: # Not a valid guild for cmd
|
||||||
return
|
return
|
||||||
|
|
||||||
message_content: str = message.content.strip()
|
message_content: str = message.content.strip()
|
||||||
@ -235,7 +256,9 @@ class Karma(commands.Cog):
|
|||||||
|
|
||||||
message_content = discord.utils.escape_markdown(message_content)
|
message_content = discord.utils.escape_markdown(message_content)
|
||||||
|
|
||||||
karma_regex: list[str] = regex.findall(self.karma_regex, message_content.strip())
|
karma_regex: list[str] = regex.findall(
|
||||||
|
self.karma_regex, message_content.strip()
|
||||||
|
)
|
||||||
if not karma_regex: # Not a request to adjust karma
|
if not karma_regex: # Not a request to adjust karma
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -274,8 +297,9 @@ class Karma(commands.Cog):
|
|||||||
|
|
||||||
self.util.timers[message.author.id] = now
|
self.util.timers[message.author.id] = now
|
||||||
|
|
||||||
updated: bool = await self.util.update_karma(message.author.display_name,
|
updated: bool = await self.util.update_karma(
|
||||||
message.author.id, keyword, flag)
|
message.author.display_name, message.author.id, keyword, flag
|
||||||
|
)
|
||||||
if updated:
|
if updated:
|
||||||
return await message.add_reaction(emoji="👍")
|
return await message.add_reaction(emoji="👍")
|
||||||
|
|
||||||
@ -299,7 +323,9 @@ class Karma(commands.Cog):
|
|||||||
guild: Optional[discord.Guild] = self.bot.get_guild(ctx.guild.id)
|
guild: Optional[discord.Guild] = self.bot.get_guild(ctx.guild.id)
|
||||||
if not guild:
|
if not guild:
|
||||||
return
|
return
|
||||||
guild_member: Optional[discord.Member] = guild.get_member(mentioned_uid)
|
guild_member: Optional[discord.Member] = guild.get_member(
|
||||||
|
mentioned_uid
|
||||||
|
)
|
||||||
if not guild_member:
|
if not guild_member:
|
||||||
return
|
return
|
||||||
display = guild_member.display_name
|
display = guild_member.display_name
|
||||||
@ -310,8 +336,9 @@ class Karma(commands.Cog):
|
|||||||
|
|
||||||
score: int = await self.util.get_karma(keyword)
|
score: int = await self.util.get_karma(keyword)
|
||||||
description: str = f"**{keyword}** has a karma of *{score}*"
|
description: str = f"**{keyword}** has a karma of *{score}*"
|
||||||
embed: discord.Embed = discord.Embed(title=f"Karma for {keyword}",
|
embed: discord.Embed = discord.Embed(
|
||||||
description=description)
|
title=f"Karma for {keyword}", description=description
|
||||||
|
)
|
||||||
return await ctx.respond(embed=embed)
|
return await ctx.respond(embed=embed)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.respond(f"Error: {str(e)}")
|
await ctx.respond(f"Error: {str(e)}")
|
||||||
|
@ -5,8 +5,10 @@ from discord.ext import bridge, commands
|
|||||||
from util.lovehate_db import DB
|
from util.lovehate_db import DB
|
||||||
from disc_havoc import Havoc
|
from disc_havoc import Havoc
|
||||||
|
|
||||||
|
|
||||||
class LoveHate(commands.Cog):
|
class LoveHate(commands.Cog):
|
||||||
"""LoveHate Cog for Havoc"""
|
"""LoveHate Cog for Havoc"""
|
||||||
|
|
||||||
def __init__(self, bot: Havoc) -> None:
|
def __init__(self, bot: Havoc) -> None:
|
||||||
self.bot: Havoc = bot
|
self.bot: Havoc = bot
|
||||||
self.db = DB(self.bot)
|
self.db = DB(self.bot)
|
||||||
@ -21,9 +23,8 @@ class LoveHate(commands.Cog):
|
|||||||
str
|
str
|
||||||
"""
|
"""
|
||||||
if len(items) > 1:
|
if len(items) > 1:
|
||||||
return ', '.join(items[:-1]) + ' and ' + items[-1]
|
return ", ".join(items[:-1]) + " and " + items[-1]
|
||||||
return items[0] if items else ''
|
return items[0] if items else ""
|
||||||
|
|
||||||
|
|
||||||
@bridge.bridge_command()
|
@bridge.bridge_command()
|
||||||
async def loves(self, ctx, user: Optional[str] = None) -> None:
|
async def loves(self, ctx, user: Optional[str] = None) -> None:
|
||||||
@ -33,8 +34,9 @@ class LoveHate(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
if not user:
|
if not user:
|
||||||
display_name = ctx.author.display_name
|
display_name = ctx.author.display_name
|
||||||
loves: Union[list[tuple], bool] = await self.db.get_lovehates(user=display_name,
|
loves: Union[list[tuple], bool] = await self.db.get_lovehates(
|
||||||
loves=True)
|
user=display_name, loves=True
|
||||||
|
)
|
||||||
if not loves:
|
if not loves:
|
||||||
return await ctx.respond("You don't seem to love anything...")
|
return await ctx.respond("You don't seem to love anything...")
|
||||||
|
|
||||||
@ -83,8 +85,9 @@ class LoveHate(commands.Cog):
|
|||||||
if not _thing:
|
if not _thing:
|
||||||
return
|
return
|
||||||
|
|
||||||
who_loves: Union[list, bool] = await self.db.get_wholovehates(thing=_thing,
|
who_loves: Union[list, bool] = await self.db.get_wholovehates(
|
||||||
loves=True)
|
thing=_thing, loves=True
|
||||||
|
)
|
||||||
if not isinstance(who_loves, list):
|
if not isinstance(who_loves, list):
|
||||||
return await ctx.respond(f"I couldn't find anyone who loves {thing}...")
|
return await ctx.respond(f"I couldn't find anyone who loves {thing}...")
|
||||||
|
|
||||||
@ -123,8 +126,9 @@ class LoveHate(commands.Cog):
|
|||||||
return
|
return
|
||||||
_thing = thing_member.display_name
|
_thing = thing_member.display_name
|
||||||
|
|
||||||
who_hates: Union[list[tuple], bool] = await self.db.get_wholovehates(thing=_thing,
|
who_hates: Union[list[tuple], bool] = await self.db.get_wholovehates(
|
||||||
hates=True)
|
thing=_thing, hates=True
|
||||||
|
)
|
||||||
if not who_hates:
|
if not who_hates:
|
||||||
return await ctx.respond(f"I couldn't find anyone who hates {thing}...")
|
return await ctx.respond(f"I couldn't find anyone who hates {thing}...")
|
||||||
|
|
||||||
@ -144,15 +148,13 @@ class LoveHate(commands.Cog):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return await ctx.respond(f"Error: {str(e)}")
|
return await ctx.respond(f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@bridge.bridge_command()
|
@bridge.bridge_command()
|
||||||
async def dontcare(self, ctx, thing: str) -> None:
|
async def dontcare(self, ctx, thing: str) -> None:
|
||||||
"""
|
"""
|
||||||
Make me forget your opinion on <thing>
|
Make me forget your opinion on <thing>
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
stop_caring: str = await self.db.update(ctx.author.display_name,
|
stop_caring: str = await self.db.update(ctx.author.display_name, thing, 0)
|
||||||
thing, 0)
|
|
||||||
return await ctx.respond(stop_caring)
|
return await ctx.respond(stop_caring)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.respond(f"Error: {str(e)}")
|
await ctx.respond(f"Error: {str(e)}")
|
||||||
@ -166,8 +168,9 @@ class LoveHate(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
if not user:
|
if not user:
|
||||||
display_name = ctx.author.display_name
|
display_name = ctx.author.display_name
|
||||||
hates: Union[list[tuple], bool] = await self.db.get_lovehates(user=display_name,
|
hates: Union[list[tuple], bool] = await self.db.get_lovehates(
|
||||||
hates=True)
|
user=display_name, hates=True
|
||||||
|
)
|
||||||
if not hates:
|
if not hates:
|
||||||
return await ctx.respond("You don't seem to hate anything...")
|
return await ctx.respond("You don't seem to hate anything...")
|
||||||
else:
|
else:
|
||||||
@ -188,7 +191,7 @@ class LoveHate(commands.Cog):
|
|||||||
await ctx.respond(f"Error: {str(e)}")
|
await ctx.respond(f"Error: {str(e)}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
@bridge.bridge_command(aliases=['sarcastichate'])
|
@bridge.bridge_command(aliases=["sarcastichate"])
|
||||||
async def love(self, ctx, *, thing: str) -> None:
|
async def love(self, ctx, *, thing: str) -> None:
|
||||||
"""
|
"""
|
||||||
Love <thing>
|
Love <thing>
|
||||||
@ -205,14 +208,13 @@ class LoveHate(commands.Cog):
|
|||||||
return
|
return
|
||||||
thing = thing_member.display_name
|
thing = thing_member.display_name
|
||||||
|
|
||||||
love: str = await self.db.update(ctx.author.display_name,
|
love: str = await self.db.update(ctx.author.display_name, thing, 1)
|
||||||
thing, 1)
|
|
||||||
return await ctx.respond(love)
|
return await ctx.respond(love)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.respond(f"Error: {str(e)}")
|
await ctx.respond(f"Error: {str(e)}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
@bridge.bridge_command(aliases=['sarcasticlove'])
|
@bridge.bridge_command(aliases=["sarcasticlove"])
|
||||||
async def hate(self, ctx, *, thing: str) -> None:
|
async def hate(self, ctx, *, thing: str) -> None:
|
||||||
"""
|
"""
|
||||||
Hate <thing>
|
Hate <thing>
|
||||||
@ -228,8 +230,7 @@ class LoveHate(commands.Cog):
|
|||||||
if not thing_member:
|
if not thing_member:
|
||||||
return
|
return
|
||||||
thing = thing_member.display_name
|
thing = thing_member.display_name
|
||||||
hate: str = await self.db.update(ctx.author.display_name,
|
hate: str = await self.db.update(ctx.author.display_name, thing, -1)
|
||||||
thing, -1)
|
|
||||||
return await ctx.respond(hate)
|
return await ctx.respond(hate)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.respond(f"Error: {str(e)}")
|
await ctx.respond(f"Error: {str(e)}")
|
||||||
|
342
cogs/meme.py
342
cogs/meme.py
@ -4,10 +4,7 @@ import json
|
|||||||
import io
|
import io
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
from typing import (LiteralString,
|
from typing import LiteralString, Optional, Any, Union
|
||||||
Optional,
|
|
||||||
Any,
|
|
||||||
Union)
|
|
||||||
import aiosqlite as sqlite3
|
import aiosqlite as sqlite3
|
||||||
import logging
|
import logging
|
||||||
import textwrap
|
import textwrap
|
||||||
@ -35,50 +32,66 @@ BOT_CHANIDS = []
|
|||||||
TODO: Cleanup new meme leaderboard stuff
|
TODO: Cleanup new meme leaderboard stuff
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Helper:
|
class Helper:
|
||||||
"""Meme Helper"""
|
"""Meme Helper"""
|
||||||
|
|
||||||
def load_meme_choices(self) -> None:
|
def load_meme_choices(self) -> None:
|
||||||
"""Load Available Meme Templates from JSON File"""
|
"""Load Available Meme Templates from JSON File"""
|
||||||
global meme_choices
|
global meme_choices
|
||||||
|
|
||||||
memes_file: str|LiteralString = os.path.join(os.path.dirname(__file__), "memes.json")
|
memes_file: str | LiteralString = os.path.join(
|
||||||
with open(memes_file, 'r', encoding='utf-8') as f:
|
os.path.dirname(__file__), "memes.json"
|
||||||
|
)
|
||||||
|
with open(memes_file, "r", encoding="utf-8") as f:
|
||||||
meme_choices = json.loads(f.read())
|
meme_choices = json.loads(f.read())
|
||||||
|
|
||||||
|
|
||||||
class MemeView(discord.ui.View):
|
class MemeView(discord.ui.View):
|
||||||
"""Meme Selection discord.ui.View"""
|
"""Meme Selection discord.ui.View"""
|
||||||
|
|
||||||
helper = Helper()
|
helper = Helper()
|
||||||
helper.load_meme_choices()
|
helper.load_meme_choices()
|
||||||
|
|
||||||
@discord.ui.select(
|
@discord.ui.select(
|
||||||
placeholder = "Choose a Meme!",
|
placeholder="Choose a Meme!",
|
||||||
min_values = 1,
|
min_values=1,
|
||||||
max_values = 1,
|
max_values=1,
|
||||||
options = [
|
options=[
|
||||||
discord.SelectOption(
|
discord.SelectOption(label=meme_label) for meme_label in meme_choices[0:24]
|
||||||
label=meme_label
|
],
|
||||||
) for meme_label in meme_choices[0:24]
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
async def select_callback(self, select: discord.ui.Select,
|
async def select_callback(
|
||||||
interaction: discord.Interaction) -> None:
|
self, select: discord.ui.Select, interaction: discord.Interaction
|
||||||
|
) -> None:
|
||||||
"""Meme Selection Callback"""
|
"""Meme Selection Callback"""
|
||||||
if not isinstance(select.values[0], str):
|
if not isinstance(select.values[0], str):
|
||||||
return
|
return
|
||||||
modal: discord.ui.Modal = MemeModal(meme=select.values[0], title="Meme Selected")
|
modal: discord.ui.Modal = MemeModal(
|
||||||
|
meme=select.values[0], title="Meme Selected"
|
||||||
|
)
|
||||||
await interaction.response.send_modal(modal)
|
await interaction.response.send_modal(modal)
|
||||||
|
|
||||||
|
|
||||||
class MemeModal(discord.ui.Modal):
|
class MemeModal(discord.ui.Modal):
|
||||||
"""Meme Creation discord.ui.Modal"""
|
"""Meme Creation discord.ui.Modal"""
|
||||||
|
|
||||||
def __init__(self, *args, meme: Optional[str] = None, **kwargs) -> None:
|
def __init__(self, *args, meme: Optional[str] = None, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.selected_meme: Optional[str] = meme
|
self.selected_meme: Optional[str] = meme
|
||||||
self.meme_generator = JesusMemeGenerator()
|
self.meme_generator = JesusMemeGenerator()
|
||||||
self.TEXT_LIMIT: int = 80
|
self.TEXT_LIMIT: int = 80
|
||||||
|
|
||||||
self.add_item(discord.ui.InputText(label="Top Text",
|
self.add_item(
|
||||||
style=discord.InputTextStyle.singleline))
|
discord.ui.InputText(
|
||||||
self.add_item(discord.ui.InputText(label="Bottom Text",
|
label="Top Text", style=discord.InputTextStyle.singleline
|
||||||
style=discord.InputTextStyle.singleline))
|
)
|
||||||
|
)
|
||||||
|
self.add_item(
|
||||||
|
discord.ui.InputText(
|
||||||
|
label="Bottom Text", style=discord.InputTextStyle.singleline
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction) -> None:
|
async def callback(self, interaction: discord.Interaction) -> None:
|
||||||
if not self.selected_meme: # No meme selected
|
if not self.selected_meme: # No meme selected
|
||||||
@ -86,17 +99,24 @@ class MemeModal(discord.ui.Modal):
|
|||||||
selected_meme: str = self.selected_meme
|
selected_meme: str = self.selected_meme
|
||||||
if not self.children or len(self.children) < 2: # Invalid request
|
if not self.children or len(self.children) < 2: # Invalid request
|
||||||
return
|
return
|
||||||
if not isinstance(self.children[0].value, str)\
|
if not isinstance(self.children[0].value, str) or not isinstance(
|
||||||
or not isinstance(self.children[1].value, str): # Invalid request
|
self.children[1].value, str
|
||||||
|
): # Invalid request
|
||||||
return
|
return
|
||||||
meme_top_line: str = self.children[0].value.strip()
|
meme_top_line: str = self.children[0].value.strip()
|
||||||
meme_bottom_line: str = self.children[1].value.strip()
|
meme_bottom_line: str = self.children[1].value.strip()
|
||||||
if len(meme_top_line) > self.TEXT_LIMIT or len(meme_bottom_line) > self.TEXT_LIMIT:
|
if (
|
||||||
await interaction.response.send_message("ERR: Text is limited to 80 characters for each the top and bottom lines.")
|
len(meme_top_line) > self.TEXT_LIMIT
|
||||||
|
or len(meme_bottom_line) > self.TEXT_LIMIT
|
||||||
|
):
|
||||||
|
await interaction.response.send_message(
|
||||||
|
"ERR: Text is limited to 80 characters for each the top and bottom lines."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
meme_link: Optional[str] = await self.meme_generator.create_meme(top_line=meme_top_line,
|
meme_link: Optional[str] = await self.meme_generator.create_meme(
|
||||||
bottom_line=meme_bottom_line, meme=selected_meme)
|
top_line=meme_top_line, bottom_line=meme_bottom_line, meme=selected_meme
|
||||||
|
)
|
||||||
if not meme_link:
|
if not meme_link:
|
||||||
await interaction.response.send_message("Failed!")
|
await interaction.response.send_message("Failed!")
|
||||||
return
|
return
|
||||||
@ -106,43 +126,45 @@ class MemeModal(discord.ui.Modal):
|
|||||||
await interaction.response.send_message(embeds=[embed])
|
await interaction.response.send_message(embeds=[embed])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
class Meme(commands.Cog):
|
class Meme(commands.Cog):
|
||||||
"""Meme Cog for Havoc"""
|
"""Meme Cog for Havoc"""
|
||||||
|
|
||||||
def __init__(self, bot: Havoc) -> None:
|
def __init__(self, bot: Havoc) -> None:
|
||||||
self.bot: Havoc = bot
|
self.bot: Havoc = bot
|
||||||
self.stats_db_path: LiteralString = os.path.join("/usr/local/share",
|
self.stats_db_path: LiteralString = os.path.join(
|
||||||
"sqlite_dbs", "stats.db")
|
"/usr/local/share", "sqlite_dbs", "stats.db"
|
||||||
|
)
|
||||||
self.meme_choices: list = []
|
self.meme_choices: list = []
|
||||||
self.meme_counter: int = 0
|
self.meme_counter: int = 0
|
||||||
self.THREADS: dict[str, dict[int, list]] = {
|
self.THREADS: dict[str, dict[int, list]] = {
|
||||||
# Format: Guild1: [ChanId : [Webhook, ThreadId], Guild2: [ChanId : [Webhook, ThreadId]
|
# Format: Guild1: [ChanId : [Webhook, ThreadId], Guild2: [ChanId : [Webhook, ThreadId]
|
||||||
'comic_explosm': {
|
"comic_explosm": {
|
||||||
1298729744216359055: [constants.EXPLOSM_WEBHOOK, 1299165855493390367],
|
1298729744216359055: [constants.EXPLOSM_WEBHOOK, 1299165855493390367],
|
||||||
1306414795049926676: [constants.EXPLOSM_WEBHOOK2, 1306416492304138364],
|
1306414795049926676: [constants.EXPLOSM_WEBHOOK2, 1306416492304138364],
|
||||||
},
|
},
|
||||||
'comic_xkcd': {
|
"comic_xkcd": {
|
||||||
1298729744216359055: [constants.XKCD_WEBHOOK, 1299165928755433483],
|
1298729744216359055: [constants.XKCD_WEBHOOK, 1299165928755433483],
|
||||||
1306414795049926676: [constants.XKCD_WEBHOOK2, 1306416681991798854],
|
1306414795049926676: [constants.XKCD_WEBHOOK2, 1306416681991798854],
|
||||||
},
|
},
|
||||||
'comic_smbc': {
|
"comic_smbc": {
|
||||||
1298729744216359055: [constants.SMBC_WEBHOOK, 1299166071038808104],
|
1298729744216359055: [constants.SMBC_WEBHOOK, 1299166071038808104],
|
||||||
1306414795049926676: [constants.SMBC_WEBHOOK2, 1306416842511745024],
|
1306414795049926676: [constants.SMBC_WEBHOOK2, 1306416842511745024],
|
||||||
},
|
},
|
||||||
'comic_qc': {
|
"comic_qc": {
|
||||||
1298729744216359055: [constants.QC_WEBHOOK, 1299392115364593674],
|
1298729744216359055: [constants.QC_WEBHOOK, 1299392115364593674],
|
||||||
1306414795049926676: [constants.QC_WEBHOOK2, 1306417084774744114],
|
1306414795049926676: [constants.QC_WEBHOOK2, 1306417084774744114],
|
||||||
},
|
},
|
||||||
'comic_dino': {
|
"comic_dino": {
|
||||||
1298729744216359055: [constants.DINO_WEBHOOK, 1299771918886506557],
|
1298729744216359055: [constants.DINO_WEBHOOK, 1299771918886506557],
|
||||||
1306414795049926676: [constants.DINO_WEBHOOK2, 1306417286713704548],
|
1306414795049926676: [constants.DINO_WEBHOOK2, 1306417286713704548],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.NO_THREAD_WEBHOOKS: dict[str, list] = {
|
self.NO_THREAD_WEBHOOKS: dict[str, list] = {
|
||||||
'theonion': [constants.ONION_WEBHOOK, constants.ONION_WEBHOOK2],
|
"theonion": [constants.ONION_WEBHOOK, constants.ONION_WEBHOOK2],
|
||||||
'thn': [constants.THN_WEBHOOK],
|
"thn": [constants.THN_WEBHOOK],
|
||||||
'memes': [constants.MEME_WEBHOOK1, constants.MEME_WEBHOOK2],
|
"memes": [constants.MEME_WEBHOOK1, constants.MEME_WEBHOOK2],
|
||||||
}
|
}
|
||||||
|
|
||||||
global BOT_CHANIDS
|
global BOT_CHANIDS
|
||||||
@ -155,6 +177,7 @@ class Meme(commands.Cog):
|
|||||||
|
|
||||||
def is_spamchan() -> bool: # type: ignore
|
def is_spamchan() -> bool: # type: ignore
|
||||||
"""Check if channel is spamchan"""
|
"""Check if channel is spamchan"""
|
||||||
|
|
||||||
def predicate(ctx):
|
def predicate(ctx):
|
||||||
try:
|
try:
|
||||||
if not ctx.channel.id in BOT_CHANIDS:
|
if not ctx.channel.id in BOT_CHANIDS:
|
||||||
@ -163,10 +186,10 @@ class Meme(commands.Cog):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return commands.check(predicate) # type: ignore
|
return commands.check(predicate) # type: ignore
|
||||||
|
|
||||||
async def leaderboard_increment(self,
|
async def leaderboard_increment(self, uid: int) -> None:
|
||||||
uid: int) -> None:
|
|
||||||
"""
|
"""
|
||||||
Increment leaderboard for uid
|
Increment leaderboard for uid
|
||||||
Args:
|
Args:
|
||||||
@ -175,7 +198,6 @@ class Meme(commands.Cog):
|
|||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
if not uid in self.meme_leaderboard:
|
if not uid in self.meme_leaderboard:
|
||||||
self.meme_leaderboard[uid] = 1
|
self.meme_leaderboard[uid] = 1
|
||||||
else:
|
else:
|
||||||
@ -199,23 +221,23 @@ class Meme(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
await self.update_meme_lb()
|
await self.update_meme_lb()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info("Failed to update meme leaderboard following increment: %s",
|
logging.info(
|
||||||
str(e))
|
"Failed to update meme leaderboard following increment: %s", str(e)
|
||||||
|
)
|
||||||
|
|
||||||
async def init_meme_leaderboard(self) -> None:
|
async def init_meme_leaderboard(self) -> None:
|
||||||
"""
|
"""
|
||||||
INIT MEME LEADERBOARD
|
INIT MEME LEADERBOARD
|
||||||
"""
|
"""
|
||||||
self.meme_leaderboard: dict [int, int] = {}
|
self.meme_leaderboard: dict[int, int] = {}
|
||||||
async with sqlite3.connect(self.stats_db_path, timeout=2) as db_conn:
|
async with sqlite3.connect(self.stats_db_path, timeout=2) as db_conn:
|
||||||
db_conn.row_factory = sqlite3.Row
|
db_conn.row_factory = sqlite3.Row
|
||||||
db_query: str = "SELECT discord_uid, count FROM memes WHERE count > 0"
|
db_query: str = "SELECT discord_uid, count FROM memes WHERE count > 0"
|
||||||
async with db_conn.execute(db_query) as db_cursor:
|
async with db_conn.execute(db_query) as db_cursor:
|
||||||
results = await db_cursor.fetchall()
|
results = await db_cursor.fetchall()
|
||||||
for result in results:
|
for result in results:
|
||||||
uid = result['discord_uid']
|
uid = result["discord_uid"]
|
||||||
count = result['count']
|
count = result["count"]
|
||||||
self.meme_leaderboard[uid] = count
|
self.meme_leaderboard[uid] = count
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
@ -284,28 +306,34 @@ class Meme(commands.Cog):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
agents: list[str] = constants.HTTP_UA_LIST
|
agents: list[str] = constants.HTTP_UA_LIST
|
||||||
headers: dict = {
|
headers: dict = {"User-Agent": random.choice(agents)}
|
||||||
'User-Agent': random.choice(agents)
|
|
||||||
}
|
|
||||||
if not only_comics:
|
if not only_comics:
|
||||||
try:
|
try:
|
||||||
for meme in memes:
|
for meme in memes:
|
||||||
if not meme:
|
if not meme:
|
||||||
continue
|
continue
|
||||||
(meme_id, meme_title, meme_url) = meme
|
(meme_id, meme_title, meme_url) = meme
|
||||||
request = requests.get(meme_url, stream=True, timeout=(5, 30), headers=headers)
|
request = requests.get(
|
||||||
|
meme_url, stream=True, timeout=(5, 30), headers=headers
|
||||||
|
)
|
||||||
if not request.status_code == 200:
|
if not request.status_code == 200:
|
||||||
continue
|
continue
|
||||||
meme_content: bytes = request.raw.read()
|
meme_content: bytes = request.raw.read()
|
||||||
for meme_hook in self.NO_THREAD_WEBHOOKS.get('memes', {}):
|
for meme_hook in self.NO_THREAD_WEBHOOKS.get("memes", {}):
|
||||||
meme_image: io.BytesIO = io.BytesIO(meme_content)
|
meme_image: io.BytesIO = io.BytesIO(meme_content)
|
||||||
ext: str = meme_url.split(".")[-1]\
|
ext: str = (
|
||||||
.split("?")[0].split("&")[0]
|
meme_url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
|
)
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
webhook: discord.Webhook = discord.Webhook.from_url(meme_hook,
|
webhook: discord.Webhook = discord.Webhook.from_url(
|
||||||
session=session)
|
meme_hook, session=session
|
||||||
await webhook.send(file=discord.File(meme_image,
|
)
|
||||||
filename=f'img.{ext}'), username="r/memes")
|
await webhook.send(
|
||||||
|
file=discord.File(
|
||||||
|
meme_image, filename=f"img.{ext}"
|
||||||
|
),
|
||||||
|
username="r/memes",
|
||||||
|
)
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -315,25 +343,33 @@ class Meme(commands.Cog):
|
|||||||
continue
|
continue
|
||||||
(comic_title, comic_url) = comic
|
(comic_title, comic_url) = comic
|
||||||
comic_title = discord.utils.escape_markdown(comic_title)
|
comic_title = discord.utils.escape_markdown(comic_title)
|
||||||
comic_request = requests.get(comic_url, stream=True, timeout=(5, 20), headers=headers)
|
comic_request = requests.get(
|
||||||
|
comic_url, stream=True, timeout=(5, 20), headers=headers
|
||||||
|
)
|
||||||
comic_request.raise_for_status()
|
comic_request.raise_for_status()
|
||||||
comic_content: bytes = comic_request.raw.read()
|
comic_content: bytes = comic_request.raw.read()
|
||||||
ext = comic_url.split(".")[-1]\
|
ext = comic_url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
.split("?")[0].split("&")[0]
|
|
||||||
|
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
for chanid, _hook in self.THREADS.get('comic_explosm', {}).items():
|
for chanid, _hook in self.THREADS.get(
|
||||||
|
"comic_explosm", {}
|
||||||
|
).items():
|
||||||
comic_image: io.BytesIO = io.BytesIO(comic_content)
|
comic_image: io.BytesIO = io.BytesIO(comic_content)
|
||||||
channel: int = chanid
|
channel: int = chanid
|
||||||
(hook_uri, thread_id) = _hook
|
(hook_uri, thread_id) = _hook
|
||||||
webhook = discord.Webhook.from_url(hook_uri,
|
webhook = discord.Webhook.from_url(
|
||||||
session=session)
|
hook_uri, session=session
|
||||||
|
)
|
||||||
_channel: Any = self.bot.get_channel(channel)
|
_channel: Any = self.bot.get_channel(channel)
|
||||||
if not _channel:
|
if not _channel:
|
||||||
return
|
return
|
||||||
thread = _channel.get_thread(thread_id)
|
thread = _channel.get_thread(thread_id)
|
||||||
await webhook.send(f"**{comic_title}**", file=discord.File(comic_image, filename=f'img.{ext}'),
|
await webhook.send(
|
||||||
username="Cyanide & Happiness", thread=thread)
|
f"**{comic_title}**",
|
||||||
|
file=discord.File(comic_image, filename=f"img.{ext}"),
|
||||||
|
username="Cyanide & Happiness",
|
||||||
|
thread=thread,
|
||||||
|
)
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -343,26 +379,32 @@ class Meme(commands.Cog):
|
|||||||
continue
|
continue
|
||||||
(comic_title, comic_url) = comic
|
(comic_title, comic_url) = comic
|
||||||
comic_title = discord.utils.escape_markdown(comic_title)
|
comic_title = discord.utils.escape_markdown(comic_title)
|
||||||
comic_request = requests.get(comic_url, stream=True, timeout=(5, 20), headers=headers)
|
comic_request = requests.get(
|
||||||
|
comic_url, stream=True, timeout=(5, 20), headers=headers
|
||||||
|
)
|
||||||
comic_request.raise_for_status()
|
comic_request.raise_for_status()
|
||||||
comic_content = comic_request.raw.read()
|
comic_content = comic_request.raw.read()
|
||||||
comic_image = io.BytesIO(comic_request.raw.read())
|
comic_image = io.BytesIO(comic_request.raw.read())
|
||||||
ext = comic_url.split(".")[-1]\
|
ext = comic_url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
.split("?")[0].split("&")[0]
|
|
||||||
|
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
for chanid, _hook in self.THREADS.get('comic_xkcd', {}).items():
|
for chanid, _hook in self.THREADS.get("comic_xkcd", {}).items():
|
||||||
comic_image = io.BytesIO(comic_content)
|
comic_image = io.BytesIO(comic_content)
|
||||||
channel = chanid
|
channel = chanid
|
||||||
(hook_uri, thread_id) = _hook
|
(hook_uri, thread_id) = _hook
|
||||||
webhook = discord.Webhook.from_url(hook_uri,
|
webhook = discord.Webhook.from_url(
|
||||||
session=session)
|
hook_uri, session=session
|
||||||
|
)
|
||||||
_channel = self.bot.get_channel(channel)
|
_channel = self.bot.get_channel(channel)
|
||||||
if not _channel:
|
if not _channel:
|
||||||
return
|
return
|
||||||
thread = _channel.get_thread(thread_id)
|
thread = _channel.get_thread(thread_id)
|
||||||
await webhook.send(f"**{comic_title}**", file=discord.File(comic_image, filename=f'img.{ext}'),
|
await webhook.send(
|
||||||
username="xkcd", thread=thread)
|
f"**{comic_title}**",
|
||||||
|
file=discord.File(comic_image, filename=f"img.{ext}"),
|
||||||
|
username="xkcd",
|
||||||
|
thread=thread,
|
||||||
|
)
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -372,25 +414,31 @@ class Meme(commands.Cog):
|
|||||||
continue
|
continue
|
||||||
(comic_title, comic_url) = comic
|
(comic_title, comic_url) = comic
|
||||||
comic_title = discord.utils.escape_markdown(comic_title)
|
comic_title = discord.utils.escape_markdown(comic_title)
|
||||||
comic_request = requests.get(comic_url, stream=True, timeout=(5, 20), headers=headers)
|
comic_request = requests.get(
|
||||||
|
comic_url, stream=True, timeout=(5, 20), headers=headers
|
||||||
|
)
|
||||||
comic_request.raise_for_status()
|
comic_request.raise_for_status()
|
||||||
comic_content = comic_request.raw.read()
|
comic_content = comic_request.raw.read()
|
||||||
ext = comic_url.split(".")[-1]\
|
ext = comic_url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
.split("?")[0].split("&")[0]
|
|
||||||
|
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
for chanid, _hook in self.THREADS.get('comic_smbc', {}).items():
|
for chanid, _hook in self.THREADS.get("comic_smbc", {}).items():
|
||||||
comic_image = io.BytesIO(comic_content)
|
comic_image = io.BytesIO(comic_content)
|
||||||
channel = chanid
|
channel = chanid
|
||||||
(hook_uri, thread_id) = _hook
|
(hook_uri, thread_id) = _hook
|
||||||
webhook = discord.Webhook.from_url(hook_uri,
|
webhook = discord.Webhook.from_url(
|
||||||
session=session)
|
hook_uri, session=session
|
||||||
|
)
|
||||||
_channel = self.bot.get_channel(channel)
|
_channel = self.bot.get_channel(channel)
|
||||||
if not _channel:
|
if not _channel:
|
||||||
return
|
return
|
||||||
thread = _channel.get_thread(thread_id)
|
thread = _channel.get_thread(thread_id)
|
||||||
await webhook.send(f"**{comic_title}**", file=discord.File(comic_image, filename=f'img.{ext}'),
|
await webhook.send(
|
||||||
username="SMBC", thread=thread)
|
f"**{comic_title}**",
|
||||||
|
file=discord.File(comic_image, filename=f"img.{ext}"),
|
||||||
|
username="SMBC",
|
||||||
|
thread=thread,
|
||||||
|
)
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -401,30 +449,33 @@ class Meme(commands.Cog):
|
|||||||
continue
|
continue
|
||||||
(comic_title, comic_url) = comic
|
(comic_title, comic_url) = comic
|
||||||
comic_title = discord.utils.escape_markdown(comic_title)
|
comic_title = discord.utils.escape_markdown(comic_title)
|
||||||
comic_url = regex.sub(r'^http://ww\.', 'http://www.',
|
comic_url = regex.sub(r"^http://ww\.", "http://www.", comic_url)
|
||||||
comic_url)
|
comic_url = regex.sub(r"\.pmg$", ".png", comic_url)
|
||||||
comic_url = regex.sub(r'\.pmg$', '.png',
|
comic_request = requests.get(
|
||||||
comic_url)
|
comic_url, stream=True, timeout=(5, 20), headers=headers
|
||||||
comic_request = requests.get(comic_url, stream=True,
|
)
|
||||||
timeout=(5, 20), headers=headers)
|
|
||||||
comic_request.raise_for_status()
|
comic_request.raise_for_status()
|
||||||
comic_content = comic_request.raw.read()
|
comic_content = comic_request.raw.read()
|
||||||
ext = comic_url.split(".")[-1]\
|
ext = comic_url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
.split("?")[0].split("&")[0]
|
|
||||||
|
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
for chanid, _hook in self.THREADS.get('comic_qc', {}).items():
|
for chanid, _hook in self.THREADS.get("comic_qc", {}).items():
|
||||||
comic_image = io.BytesIO(comic_content)
|
comic_image = io.BytesIO(comic_content)
|
||||||
channel = chanid
|
channel = chanid
|
||||||
(hook_uri, thread_id) = _hook
|
(hook_uri, thread_id) = _hook
|
||||||
webhook = discord.Webhook.from_url(hook_uri,
|
webhook = discord.Webhook.from_url(
|
||||||
session=session)
|
hook_uri, session=session
|
||||||
|
)
|
||||||
_channel = self.bot.get_channel(channel)
|
_channel = self.bot.get_channel(channel)
|
||||||
if not _channel:
|
if not _channel:
|
||||||
return
|
return
|
||||||
thread = _channel.get_thread(thread_id)
|
thread = _channel.get_thread(thread_id)
|
||||||
await webhook.send(f"**{comic_title}**", file=discord.File(comic_image, filename=f'img.{ext}'),
|
await webhook.send(
|
||||||
username="Questionable Content", thread=thread)
|
f"**{comic_title}**",
|
||||||
|
file=discord.File(comic_image, filename=f"img.{ext}"),
|
||||||
|
username="Questionable Content",
|
||||||
|
thread=thread,
|
||||||
|
)
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@ -435,25 +486,31 @@ class Meme(commands.Cog):
|
|||||||
continue
|
continue
|
||||||
(comic_title, comic_url) = comic
|
(comic_title, comic_url) = comic
|
||||||
comic_title = discord.utils.escape_markdown(comic_title)
|
comic_title = discord.utils.escape_markdown(comic_title)
|
||||||
comic_request = requests.get(comic_url, stream=True, timeout=(5, 20), headers=headers)
|
comic_request = requests.get(
|
||||||
|
comic_url, stream=True, timeout=(5, 20), headers=headers
|
||||||
|
)
|
||||||
comic_request.raise_for_status()
|
comic_request.raise_for_status()
|
||||||
comic_content = comic_request.raw.read()
|
comic_content = comic_request.raw.read()
|
||||||
ext = comic_url.split(".")[-1]\
|
ext = comic_url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
.split("?")[0].split("&")[0]
|
|
||||||
|
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
for chanid, _hook in self.THREADS.get('comic_dino', {}).items():
|
for chanid, _hook in self.THREADS.get("comic_dino", {}).items():
|
||||||
comic_image = io.BytesIO(comic_content)
|
comic_image = io.BytesIO(comic_content)
|
||||||
channel = chanid
|
channel = chanid
|
||||||
(hook_uri, thread_id) = _hook
|
(hook_uri, thread_id) = _hook
|
||||||
webhook = discord.Webhook.from_url(hook_uri,
|
webhook = discord.Webhook.from_url(
|
||||||
session=session)
|
hook_uri, session=session
|
||||||
|
)
|
||||||
_channel = self.bot.get_channel(channel)
|
_channel = self.bot.get_channel(channel)
|
||||||
if not _channel:
|
if not _channel:
|
||||||
return
|
return
|
||||||
thread = _channel.get_thread(thread_id)
|
thread = _channel.get_thread(thread_id)
|
||||||
await webhook.send(f"**{comic_title}**", file=discord.File(comic_image, filename=f'img.{ext}'),
|
await webhook.send(
|
||||||
username="Dinosaur Comics", thread=thread)
|
f"**{comic_title}**",
|
||||||
|
file=discord.File(comic_image, filename=f"img.{ext}"),
|
||||||
|
username="Dinosaur Comics",
|
||||||
|
thread=thread,
|
||||||
|
)
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -462,15 +519,20 @@ class Meme(commands.Cog):
|
|||||||
if not onion:
|
if not onion:
|
||||||
continue
|
continue
|
||||||
(onion_title, onion_description, onion_link, onion_video) = onion
|
(onion_title, onion_description, onion_link, onion_video) = onion
|
||||||
onion_description = textwrap.wrap(text=onion_description,
|
onion_description = textwrap.wrap(
|
||||||
width=860, max_lines=1)[0]
|
text=onion_description, width=860, max_lines=1
|
||||||
|
)[0]
|
||||||
embed: discord.Embed = discord.Embed(title=onion_title)
|
embed: discord.Embed = discord.Embed(title=onion_title)
|
||||||
embed.add_field(name="Content", value=f"{onion_description[0:960]}\n-# {onion_link}")
|
embed.add_field(
|
||||||
|
name="Content",
|
||||||
|
value=f"{onion_description[0:960]}\n-# {onion_link}",
|
||||||
|
)
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
for hook in self.NO_THREAD_WEBHOOKS.get('theonion', {}):
|
for hook in self.NO_THREAD_WEBHOOKS.get("theonion", {}):
|
||||||
hook_uri = hook
|
hook_uri = hook
|
||||||
webhook = discord.Webhook.from_url(hook_uri,
|
webhook = discord.Webhook.from_url(
|
||||||
session=session)
|
hook_uri, session=session
|
||||||
|
)
|
||||||
await webhook.send(embed=embed, username="The Onion")
|
await webhook.send(embed=embed, username="The Onion")
|
||||||
if onion_video:
|
if onion_video:
|
||||||
await webhook.send(f"^ video: {onion_video}")
|
await webhook.send(f"^ video: {onion_video}")
|
||||||
@ -483,16 +545,20 @@ class Meme(commands.Cog):
|
|||||||
if not thn:
|
if not thn:
|
||||||
continue
|
continue
|
||||||
(thn_title, thn_description, thn_link, thn_pubdate, thn_video) = thn
|
(thn_title, thn_description, thn_link, thn_pubdate, thn_video) = thn
|
||||||
thn_description = textwrap.wrap(text=thn_description,
|
thn_description = textwrap.wrap(
|
||||||
width=860, max_lines=1)[0]
|
text=thn_description, width=860, max_lines=1
|
||||||
|
)[0]
|
||||||
embed = discord.Embed(title=thn_title)
|
embed = discord.Embed(title=thn_title)
|
||||||
embed.add_field(name="Content", value=f"{thn_description[0:960]}\n-# {thn_link}")
|
embed.add_field(
|
||||||
|
name="Content", value=f"{thn_description[0:960]}\n-# {thn_link}"
|
||||||
|
)
|
||||||
embed.add_field(name="Published", value=thn_pubdate, inline=False)
|
embed.add_field(name="Published", value=thn_pubdate, inline=False)
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
for hook in self.NO_THREAD_WEBHOOKS.get('thn', {}):
|
for hook in self.NO_THREAD_WEBHOOKS.get("thn", {}):
|
||||||
hook_uri = hook
|
hook_uri = hook
|
||||||
webhook = discord.Webhook.from_url(hook_uri,
|
webhook = discord.Webhook.from_url(
|
||||||
session=session)
|
hook_uri, session=session
|
||||||
|
)
|
||||||
await webhook.send(embed=embed, username="The Hacker News")
|
await webhook.send(embed=embed, username="The Hacker News")
|
||||||
if thn_video:
|
if thn_video:
|
||||||
await webhook.send(f"^ video: {thn_video}")
|
await webhook.send(f"^ video: {thn_video}")
|
||||||
@ -564,13 +630,17 @@ class Meme(commands.Cog):
|
|||||||
return
|
return
|
||||||
if not isinstance(message.channel, discord.TextChannel):
|
if not isinstance(message.channel, discord.TextChannel):
|
||||||
return
|
return
|
||||||
if message.channel.id == lb_chanid\
|
if (
|
||||||
and not message.author.id == self.bot.user.id:
|
message.channel.id == lb_chanid
|
||||||
|
and not message.author.id == self.bot.user.id
|
||||||
|
):
|
||||||
"""Message to #memes-top-10 not by Havoc, delete it"""
|
"""Message to #memes-top-10 not by Havoc, delete it"""
|
||||||
await message.delete(reason=f"Messages to #{message.channel.name} are not allowed")
|
await message.delete(
|
||||||
|
reason=f"Messages to #{message.channel.name} are not allowed"
|
||||||
|
)
|
||||||
removal_embed: discord.Embed = discord.Embed(
|
removal_embed: discord.Embed = discord.Embed(
|
||||||
title="Message Deleted",
|
title="Message Deleted",
|
||||||
description=f"Your message to **#{message.channel.name}** has been automatically deleted.\n**Reason**: Messages to this channel by users is not allowed."
|
description=f"Your message to **#{message.channel.name}** has been automatically deleted.\n**Reason**: Messages to this channel by users is not allowed.",
|
||||||
)
|
)
|
||||||
await message.author.send(embed=removal_embed)
|
await message.author.send(embed=removal_embed)
|
||||||
|
|
||||||
@ -598,12 +668,14 @@ class Meme(commands.Cog):
|
|||||||
out_top: list[tuple[int, int]] = []
|
out_top: list[tuple[int, int]] = []
|
||||||
async with sqlite3.connect(self.stats_db_path, timeout=2) as db_conn:
|
async with sqlite3.connect(self.stats_db_path, timeout=2) as db_conn:
|
||||||
db_conn.row_factory = sqlite3.Row
|
db_conn.row_factory = sqlite3.Row
|
||||||
query: str = "SELECT discord_uid, count FROM memes WHERE count > 0 ORDER BY count DESC"
|
query: str = (
|
||||||
|
"SELECT discord_uid, count FROM memes WHERE count > 0 ORDER BY count DESC"
|
||||||
|
)
|
||||||
async with db_conn.execute(query) as db_cursor:
|
async with db_conn.execute(query) as db_cursor:
|
||||||
db_result = await db_cursor.fetchall()
|
db_result = await db_cursor.fetchall()
|
||||||
for res in db_result:
|
for res in db_result:
|
||||||
uid = res['discord_uid']
|
uid = res["discord_uid"]
|
||||||
count = res['count']
|
count = res["count"]
|
||||||
out_top.append((uid, count))
|
out_top.append((uid, count))
|
||||||
# Check for and remove missing members
|
# Check for and remove missing members
|
||||||
guild_id: int = 1145182936002482196
|
guild_id: int = 1145182936002482196
|
||||||
@ -615,12 +687,12 @@ class Meme(commands.Cog):
|
|||||||
member: Optional[discord.Member] = guild.get_member(uid)
|
member: Optional[discord.Member] = guild.get_member(uid)
|
||||||
if not member:
|
if not member:
|
||||||
out_top.pop(x)
|
out_top.pop(x)
|
||||||
return out_top[0:(n+1)]
|
return out_top[0 : (n + 1)]
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_top_embed(self, n:int = 10) -> Optional[discord.Embed]:
|
async def get_top_embed(self, n: int = 10) -> Optional[discord.Embed]:
|
||||||
"""
|
"""
|
||||||
Get Top Memes Embed
|
Get Top Memes Embed
|
||||||
|
|
||||||
@ -643,11 +715,13 @@ class Meme(commands.Cog):
|
|||||||
if not member:
|
if not member:
|
||||||
continue
|
continue
|
||||||
display_name: str = member.display_name
|
display_name: str = member.display_name
|
||||||
top_formatted += f"{x+1}. **{discord.utils.escape_markdown(display_name)}**: *{count}*\n"
|
top_formatted += (
|
||||||
|
f"{x+1}. **{discord.utils.escape_markdown(display_name)}**: *{count}*\n"
|
||||||
|
)
|
||||||
top_formatted = top_formatted.strip()
|
top_formatted = top_formatted.strip()
|
||||||
embed: discord.Embed = discord.Embed(title=f"Top {n} Memes",
|
embed: discord.Embed = discord.Embed(
|
||||||
description=top_formatted,
|
title=f"Top {n} Memes", description=top_formatted, colour=0x25BD6B
|
||||||
colour=0x25bd6b)
|
)
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
@tasks.loop(seconds=30, reconnect=True)
|
@tasks.loop(seconds=30, reconnect=True)
|
||||||
@ -661,8 +735,10 @@ class Meme(commands.Cog):
|
|||||||
if not isinstance(channel, discord.TextChannel):
|
if not isinstance(channel, discord.TextChannel):
|
||||||
return
|
return
|
||||||
message_to_edit = await channel.fetch_message(message_id)
|
message_to_edit = await channel.fetch_message(message_id)
|
||||||
await message_to_edit.edit(embed=top_embed,
|
await message_to_edit.edit(
|
||||||
content="## This message will automatically update periodically.")
|
embed=top_embed,
|
||||||
|
content="## This message will automatically update periodically.",
|
||||||
|
)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
@ -671,17 +747,21 @@ class Meme(commands.Cog):
|
|||||||
async def doembed(self, ctx) -> None:
|
async def doembed(self, ctx) -> None:
|
||||||
"""Do Meme Embed"""
|
"""Do Meme Embed"""
|
||||||
meme_lb_chan_id: int = 1352373745108652145
|
meme_lb_chan_id: int = 1352373745108652145
|
||||||
meme_lb_chan: Union[discord.TextChannel, Any] = self.bot.get_channel(meme_lb_chan_id)
|
meme_lb_chan: Union[discord.TextChannel, Any] = self.bot.get_channel(
|
||||||
|
meme_lb_chan_id
|
||||||
|
)
|
||||||
embed = await self.get_top_embed()
|
embed = await self.get_top_embed()
|
||||||
if embed:
|
if embed:
|
||||||
await meme_lb_chan.send(embed=embed)
|
await meme_lb_chan.send(embed=embed)
|
||||||
else:
|
else:
|
||||||
await ctx.respond("NO embed :(")
|
await ctx.respond("NO embed :(")
|
||||||
|
|
||||||
def cog_unload(self) -> None:
|
def cog_unload(self) -> None:
|
||||||
self.meme_stream_loop.cancel()
|
self.meme_stream_loop.cancel()
|
||||||
self.explosm_loop.cancel()
|
self.explosm_loop.cancel()
|
||||||
self.update_meme_lb.cancel()
|
self.update_meme_lb.cancel()
|
||||||
|
|
||||||
|
|
||||||
def setup(bot) -> None:
|
def setup(bot) -> None:
|
||||||
"""Run on Cog Load"""
|
"""Run on Cog Load"""
|
||||||
bot.add_cog(Meme(bot))
|
bot.add_cog(Meme(bot))
|
673
cogs/misc.py
673
cogs/misc.py
File diff suppressed because it is too large
Load Diff
148
cogs/owner.py
148
cogs/owner.py
@ -9,20 +9,24 @@ from discord.ext import bridge, commands
|
|||||||
from disc_havoc import Havoc
|
from disc_havoc import Havoc
|
||||||
import util
|
import util
|
||||||
|
|
||||||
|
|
||||||
class Owner(commands.Cog):
|
class Owner(commands.Cog):
|
||||||
"""Owner Cog for Havoc"""
|
"""Owner Cog for Havoc"""
|
||||||
|
|
||||||
def __init__(self, bot: Havoc) -> None:
|
def __init__(self, bot: Havoc) -> None:
|
||||||
self.bot: Havoc = bot
|
self.bot: Havoc = bot
|
||||||
self.former_roles_store: dict[int, list[discord.Role]] = {}
|
self.former_roles_store: dict[int, list[discord.Role]] = {}
|
||||||
self._temperature: int = random.randrange(20, 30)
|
self._temperature: int = random.randrange(20, 30)
|
||||||
|
|
||||||
@bridge.bridge_command(guild_ids=[1145182936002482196])
|
@bridge.bridge_command(guild_ids=[1145182936002482196])
|
||||||
async def temperature(self, ctx, temp: Optional[int|str] = None) -> None:
|
async def temperature(self, ctx, temp: Optional[int | str] = None) -> None:
|
||||||
"""
|
"""
|
||||||
Set Temperature
|
Set Temperature
|
||||||
"""
|
"""
|
||||||
if not temp:
|
if not temp:
|
||||||
return await ctx.respond(f"The current temperature is: {self._temperature} °C")
|
return await ctx.respond(
|
||||||
|
f"The current temperature is: {self._temperature} °C"
|
||||||
|
)
|
||||||
|
|
||||||
if not self.bot.is_owner(ctx.author):
|
if not self.bot.is_owner(ctx.author):
|
||||||
return await ctx.respond("I am afraid I can't let you do that.")
|
return await ctx.respond("I am afraid I can't let you do that.")
|
||||||
@ -35,15 +39,13 @@ class Owner(commands.Cog):
|
|||||||
elif _temperature > 35:
|
elif _temperature > 35:
|
||||||
return await ctx.respond("Too hot! (35°C maximum)")
|
return await ctx.respond("Too hot! (35°C maximum)")
|
||||||
self._temperature = _temperature
|
self._temperature = _temperature
|
||||||
return await ctx.respond(f"As per your request, I have adjusted the temperature to {_temperature} °C.")
|
return await ctx.respond(
|
||||||
|
f"As per your request, I have adjusted the temperature to {_temperature} °C."
|
||||||
|
)
|
||||||
|
|
||||||
@bridge.bridge_command()
|
@bridge.bridge_command()
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
async def editmsg(self, ctx,
|
async def editmsg(self, ctx, msgid: str, *, newcontent: str) -> None:
|
||||||
msgid: str,
|
|
||||||
*,
|
|
||||||
newcontent: str
|
|
||||||
) -> None:
|
|
||||||
"""
|
"""
|
||||||
Edit a message previously sent by the bot
|
Edit a message previously sent by the bot
|
||||||
"""
|
"""
|
||||||
@ -51,14 +53,14 @@ class Owner(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
message: Optional[discord.Message] = self.bot.get_message(int(msgid))
|
message: Optional[discord.Message] = self.bot.get_message(int(msgid))
|
||||||
if not message:
|
if not message:
|
||||||
await ctx.respond(f"**Failed:** Message {msgid} not found.",
|
await ctx.respond(
|
||||||
ephemeral=True)
|
f"**Failed:** Message {msgid} not found.", ephemeral=True
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
await message.edit(content=newcontent)
|
await message.edit(content=newcontent)
|
||||||
await ctx.respond("**Done!**", ephemeral=True)
|
await ctx.respond("**Done!**", ephemeral=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.respond(f"**Failed:** {str(e)}",
|
await ctx.respond(f"**Failed:** {str(e)}", ephemeral=True)
|
||||||
ephemeral=True)
|
|
||||||
|
|
||||||
@bridge.bridge_command()
|
@bridge.bridge_command()
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
@ -70,41 +72,43 @@ class Owner(commands.Cog):
|
|||||||
self.bot.load_exts(False)
|
self.bot.load_exts(False)
|
||||||
await ctx.respond("Reloaded!", ephemeral=True)
|
await ctx.respond("Reloaded!", ephemeral=True)
|
||||||
|
|
||||||
|
|
||||||
@bridge.bridge_command()
|
@bridge.bridge_command()
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
async def say(self, ctx, *,
|
async def say(self, ctx, *, parameters: str) -> None:
|
||||||
parameters: str) -> None:
|
|
||||||
"""
|
"""
|
||||||
Make me say something in a channel
|
Make me say something in a channel
|
||||||
"""
|
"""
|
||||||
_parameters: list[str] = parameters.split(" ")
|
_parameters: list[str] = parameters.split(" ")
|
||||||
|
|
||||||
if not len(_parameters) > 1:
|
if not len(_parameters) > 1:
|
||||||
return await ctx.respond("**Error**: Incorrect command usage; required: <chan> <msg>", ephemeral=True)
|
return await ctx.respond(
|
||||||
|
"**Error**: Incorrect command usage; required: <chan> <msg>",
|
||||||
|
ephemeral=True,
|
||||||
|
)
|
||||||
|
|
||||||
channel: str = _parameters[0]
|
channel: str = _parameters[0]
|
||||||
channel_mentions: list[int] = discord.utils.raw_channel_mentions(channel)
|
channel_mentions: list[int] = discord.utils.raw_channel_mentions(channel)
|
||||||
if channel_mentions:
|
if channel_mentions:
|
||||||
channel = str(channel_mentions[0])
|
channel = str(channel_mentions[0])
|
||||||
msg: str = " ".join(_parameters[1:])
|
msg: str = " ".join(_parameters[1:])
|
||||||
await util.discord_helpers.send_message(self.bot, channel=channel,
|
await util.discord_helpers.send_message(self.bot, channel=channel, message=msg)
|
||||||
message=msg)
|
|
||||||
return await ctx.respond("**Done.**", ephemeral=True)
|
return await ctx.respond("**Done.**", ephemeral=True)
|
||||||
|
|
||||||
@bridge.bridge_command()
|
@bridge.bridge_command()
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
async def chgstatus(self, ctx, *,
|
async def chgstatus(self, ctx, *, status: Optional[str] = None) -> None:
|
||||||
status: Optional[str] = None) -> None:
|
|
||||||
"""
|
"""
|
||||||
Change bots status
|
Change bots status
|
||||||
"""
|
"""
|
||||||
if not status:
|
if not status:
|
||||||
return await ctx.respond("ERR: No status provided to change to!",
|
return await ctx.respond(
|
||||||
ephemeral=True)
|
"ERR: No status provided to change to!", ephemeral=True
|
||||||
|
)
|
||||||
|
|
||||||
await self.bot.change_presence(status=discord.Status.online,
|
await self.bot.change_presence(
|
||||||
activity=discord.CustomActivity(name=status.strip()))
|
status=discord.Status.online,
|
||||||
|
activity=discord.CustomActivity(name=status.strip()),
|
||||||
|
)
|
||||||
await ctx.respond("Done!", ephemeral=True)
|
await ctx.respond("Done!", ephemeral=True)
|
||||||
|
|
||||||
@commands.message_command(name="Remove Messages Starting Here")
|
@commands.message_command(name="Remove Messages Starting Here")
|
||||||
@ -120,10 +124,12 @@ class Owner(commands.Cog):
|
|||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
await ctx.channel.purge(after=message,
|
await ctx.channel.purge(
|
||||||
|
after=message,
|
||||||
bulk=True,
|
bulk=True,
|
||||||
limit=900000,
|
limit=900000,
|
||||||
reason=f"Purge initiated by {ctx.author.display_name}")
|
reason=f"Purge initiated by {ctx.author.display_name}",
|
||||||
|
)
|
||||||
await message.delete(reason=f"Purge initiated by {ctx.author.display_name}")
|
await message.delete(reason=f"Purge initiated by {ctx.author.display_name}")
|
||||||
await ctx.respond("**Done!**")
|
await ctx.respond("**Done!**")
|
||||||
# Wait 3 seconds, then delete interaction
|
# Wait 3 seconds, then delete interaction
|
||||||
@ -149,7 +155,9 @@ class Owner(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
if not isinstance(message.channel, discord.TextChannel):
|
if not isinstance(message.channel, discord.TextChannel):
|
||||||
return
|
return
|
||||||
memes_channel: discord.TextChannel = ctx.guild.get_channel(1147229098544988261)
|
memes_channel: discord.TextChannel = ctx.guild.get_channel(
|
||||||
|
1147229098544988261
|
||||||
|
)
|
||||||
message_content: str = message.content
|
message_content: str = message.content
|
||||||
message_author: str = message.author.display_name
|
message_author: str = message.author.display_name
|
||||||
message_channel: str = message.channel.name
|
message_channel: str = message.channel.name
|
||||||
@ -157,14 +165,17 @@ class Owner(commands.Cog):
|
|||||||
if message.attachments:
|
if message.attachments:
|
||||||
for item in message.attachments:
|
for item in message.attachments:
|
||||||
if item.url and len(item.url) >= 20:
|
if item.url and len(item.url) >= 20:
|
||||||
image: io.BytesIO = io.BytesIO(requests.get(item.url, stream=True,
|
image: io.BytesIO = io.BytesIO(
|
||||||
timeout=20).raw.read())
|
requests.get(item.url, stream=True, timeout=20).raw.read()
|
||||||
ext: str = item.url.split(".")[-1]\
|
)
|
||||||
.split("?")[0].split("&")[0]
|
ext: str = item.url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
_file = discord.File(image, filename=f'img.{ext}')
|
_file = discord.File(image, filename=f"img.{ext}")
|
||||||
if not _file:
|
if not _file:
|
||||||
return # No file to move
|
return # No file to move
|
||||||
await memes_channel.send(f"*Performing bureaucratic duties (this didn't belong in #{message_channel})...*\n**{message_author}:** {message_content}", file=_file)
|
await memes_channel.send(
|
||||||
|
f"*Performing bureaucratic duties (this didn't belong in #{message_channel})...*\n**{message_author}:** {message_content}",
|
||||||
|
file=_file,
|
||||||
|
)
|
||||||
await message.delete()
|
await message.delete()
|
||||||
await ctx.respond("OK!", ephemeral=True)
|
await ctx.respond("OK!", ephemeral=True)
|
||||||
except:
|
except:
|
||||||
@ -186,7 +197,9 @@ class Owner(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
if not isinstance(message.channel, discord.TextChannel):
|
if not isinstance(message.channel, discord.TextChannel):
|
||||||
return
|
return
|
||||||
drugs_channel: discord.TextChannel = ctx.guild.get_channel(1172247451047034910)
|
drugs_channel: discord.TextChannel = ctx.guild.get_channel(
|
||||||
|
1172247451047034910
|
||||||
|
)
|
||||||
message_content: str = message.content
|
message_content: str = message.content
|
||||||
message_author: str = message.author.display_name
|
message_author: str = message.author.display_name
|
||||||
message_channel: str = message.channel.name
|
message_channel: str = message.channel.name
|
||||||
@ -194,15 +207,18 @@ class Owner(commands.Cog):
|
|||||||
if message.attachments:
|
if message.attachments:
|
||||||
for item in message.attachments:
|
for item in message.attachments:
|
||||||
if item.url and len(item.url) >= 20:
|
if item.url and len(item.url) >= 20:
|
||||||
image: io.BytesIO = io.BytesIO(requests.get(item.url, stream=True,
|
image: io.BytesIO = io.BytesIO(
|
||||||
timeout=20).raw.read())
|
requests.get(item.url, stream=True, timeout=20).raw.read()
|
||||||
ext: str = item.url.split(".")[-1]\
|
)
|
||||||
.split("?")[0].split("&")[0]
|
ext: str = item.url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
_file = discord.File(image, filename=f'img.{ext}')
|
_file = discord.File(image, filename=f"img.{ext}")
|
||||||
if not _file:
|
if not _file:
|
||||||
return # No file to move
|
return # No file to move
|
||||||
await drugs_channel.send(f"*Performing bureaucratic duties (this didn't belong in #{message_channel})...\
|
await drugs_channel.send(
|
||||||
*\n**{message_author}:** {message_content}", file=_file)
|
f"*Performing bureaucratic duties (this didn't belong in #{message_channel})...\
|
||||||
|
*\n**{message_author}:** {message_content}",
|
||||||
|
file=_file,
|
||||||
|
)
|
||||||
await message.delete()
|
await message.delete()
|
||||||
await ctx.respond("OK!", ephemeral=True)
|
await ctx.respond("OK!", ephemeral=True)
|
||||||
except:
|
except:
|
||||||
@ -224,7 +240,9 @@ class Owner(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
if not isinstance(message.channel, discord.TextChannel):
|
if not isinstance(message.channel, discord.TextChannel):
|
||||||
return
|
return
|
||||||
funhouse_channel: discord.TextChannel = ctx.guild.get_channel(1213160512364478607)
|
funhouse_channel: discord.TextChannel = ctx.guild.get_channel(
|
||||||
|
1213160512364478607
|
||||||
|
)
|
||||||
message_content: str = message.content
|
message_content: str = message.content
|
||||||
message_author: str = message.author.display_name
|
message_author: str = message.author.display_name
|
||||||
message_channel: str = message.channel.name
|
message_channel: str = message.channel.name
|
||||||
@ -232,13 +250,15 @@ class Owner(commands.Cog):
|
|||||||
if message.attachments:
|
if message.attachments:
|
||||||
for item in message.attachments:
|
for item in message.attachments:
|
||||||
if item.url and len(item.url) >= 20:
|
if item.url and len(item.url) >= 20:
|
||||||
image: io.BytesIO = io.BytesIO(requests.get(item.url, stream=True,
|
image: io.BytesIO = io.BytesIO(
|
||||||
timeout=20).raw.read())
|
requests.get(item.url, stream=True, timeout=20).raw.read()
|
||||||
ext: str = item.url.split(".")[-1]\
|
)
|
||||||
.split("?")[0].split("&")[0]
|
ext: str = item.url.split(".")[-1].split("?")[0].split("&")[0]
|
||||||
_file = discord.File(image, filename=f'img.{ext}')
|
_file = discord.File(image, filename=f"img.{ext}")
|
||||||
await funhouse_channel.send(f"*Performing bureaucratic duties (this didn't belong in #{message_channel})\
|
await funhouse_channel.send(
|
||||||
...*\n**{message_author}:** {message_content}")
|
f"*Performing bureaucratic duties (this didn't belong in #{message_channel})\
|
||||||
|
...*\n**{message_author}:** {message_content}"
|
||||||
|
)
|
||||||
await message.delete()
|
await message.delete()
|
||||||
await ctx.respond("OK!", ephemeral=True)
|
await ctx.respond("OK!", ephemeral=True)
|
||||||
except:
|
except:
|
||||||
@ -265,10 +285,17 @@ class Owner(commands.Cog):
|
|||||||
audit_reason: str = f"Einsperren von {ctx.user.display_name}"
|
audit_reason: str = f"Einsperren von {ctx.user.display_name}"
|
||||||
member = ctx.guild.get_member(member.id)
|
member = ctx.guild.get_member(member.id)
|
||||||
member_display: str = member.display_name
|
member_display: str = member.display_name
|
||||||
einsperren_role: discord.Role = ctx.guild.get_role(1235415059300093973) if ctx.guild.id != 1145182936002482196\
|
einsperren_role: discord.Role = (
|
||||||
|
ctx.guild.get_role(1235415059300093973)
|
||||||
|
if ctx.guild.id != 1145182936002482196
|
||||||
else ctx.guild.get_role(1235406301614309386)
|
else ctx.guild.get_role(1235406301614309386)
|
||||||
member_roles: list = [role for role in member.roles if not role.name == "@everyone"]
|
)
|
||||||
member_role_names: list[str] = [str(role.name).lower() for role in member_roles]
|
member_roles: list = [
|
||||||
|
role for role in member.roles if not role.name == "@everyone"
|
||||||
|
]
|
||||||
|
member_role_names: list[str] = [
|
||||||
|
str(role.name).lower() for role in member_roles
|
||||||
|
]
|
||||||
opers_chan: discord.TextChannel = ctx.guild.get_channel(1181416083287187546)
|
opers_chan: discord.TextChannel = ctx.guild.get_channel(1181416083287187546)
|
||||||
if not "einsperren" in member_role_names:
|
if not "einsperren" in member_role_names:
|
||||||
try:
|
try:
|
||||||
@ -279,8 +306,12 @@ class Owner(commands.Cog):
|
|||||||
pass # Safe to ignore
|
pass # Safe to ignore
|
||||||
try:
|
try:
|
||||||
await member.edit(roles=[einsperren_role], reason=audit_reason)
|
await member.edit(roles=[einsperren_role], reason=audit_reason)
|
||||||
await ctx.respond(f"Gesendet {member_display} an einsperren.", ephemeral=True)
|
await ctx.respond(
|
||||||
await opers_chan.send(f"@everyone: {ctx.user.display_name} gesendet {member_display} an einsperren.")
|
f"Gesendet {member_display} an einsperren.", ephemeral=True
|
||||||
|
)
|
||||||
|
await opers_chan.send(
|
||||||
|
f"@everyone: {ctx.user.display_name} gesendet {member_display} an einsperren."
|
||||||
|
)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return await ctx.respond("GOTTVERDAMMT!!", ephemeral=True)
|
return await ctx.respond("GOTTVERDAMMT!!", ephemeral=True)
|
||||||
@ -292,12 +323,17 @@ class Owner(commands.Cog):
|
|||||||
former_roles: list = self.former_roles_store.get(member.id, [0])
|
former_roles: list = self.former_roles_store.get(member.id, [0])
|
||||||
await member.edit(roles=former_roles, reason=f"De-{audit_reason}")
|
await member.edit(roles=former_roles, reason=f"De-{audit_reason}")
|
||||||
|
|
||||||
await ctx.respond(f"{member_display} wurde von der Einsperre befreit.", ephemeral=True)
|
await ctx.respond(
|
||||||
await opers_chan.send(f"{member_display} wurde von {ctx.user.display_name} aus der Einsperre befreit.")
|
f"{member_display} wurde von der Einsperre befreit.", ephemeral=True
|
||||||
|
)
|
||||||
|
await opers_chan.send(
|
||||||
|
f"{member_display} wurde von {ctx.user.display_name} aus der Einsperre befreit."
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return await ctx.respond(f"ERR: {str(e)}", ephemeral=True)
|
return await ctx.respond(f"ERR: {str(e)}", ephemeral=True)
|
||||||
|
|
||||||
|
|
||||||
def setup(bot) -> None:
|
def setup(bot) -> None:
|
||||||
"""Run on Cog Load"""
|
"""Run on Cog Load"""
|
||||||
bot.add_cog(Owner(bot))
|
bot.add_cog(Owner(bot))
|
@ -6,20 +6,24 @@ from util.radio_util import get_now_playing, skip
|
|||||||
import discord
|
import discord
|
||||||
from disc_havoc import Havoc
|
from disc_havoc import Havoc
|
||||||
|
|
||||||
|
|
||||||
class Radio(commands.Cog):
|
class Radio(commands.Cog):
|
||||||
"""Radio Cog for Havoc"""
|
"""Radio Cog for Havoc"""
|
||||||
|
|
||||||
def __init__(self, bot: Havoc) -> None:
|
def __init__(self, bot: Havoc) -> None:
|
||||||
self.bot: Havoc = bot
|
self.bot: Havoc = bot
|
||||||
self.channels: dict[str, tuple] = {
|
self.channels: dict[str, tuple] = {
|
||||||
'sfm': (1145182936002482196, 1221615558492029050), # Tuple: Guild Id, Chan Id
|
"sfm": (
|
||||||
|
1145182936002482196,
|
||||||
|
1221615558492029050,
|
||||||
|
), # Tuple: Guild Id, Chan Id
|
||||||
}
|
}
|
||||||
self.STREAM_URL: str = "https://stream.codey.lol/sfm.ogg"
|
self.STREAM_URL: str = "https://stream.codey.lol/sfm.ogg"
|
||||||
self.LAST_NP_TRACK: Optional[str] = None
|
self.LAST_NP_TRACK: Optional[str] = None
|
||||||
try:
|
try:
|
||||||
self.radio_state_loop.cancel()
|
self.radio_state_loop.cancel()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.debug("Failed to cancel radio_state_loop: %s",
|
logging.debug("Failed to cancel radio_state_loop: %s", str(e))
|
||||||
str(e))
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_ready(self) -> None:
|
async def on_ready(self) -> None:
|
||||||
@ -28,12 +32,14 @@ class Radio(commands.Cog):
|
|||||||
|
|
||||||
def is_radio_chan(): # type: ignore
|
def is_radio_chan(): # type: ignore
|
||||||
"""Check if channel is radio chan"""
|
"""Check if channel is radio chan"""
|
||||||
|
|
||||||
def predicate(ctx):
|
def predicate(ctx):
|
||||||
try:
|
try:
|
||||||
return ctx.channel.id == 1221615558492029050
|
return ctx.channel.id == 1221615558492029050
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return commands.check(predicate)
|
return commands.check(predicate)
|
||||||
|
|
||||||
@bridge.bridge_command()
|
@bridge.bridge_command()
|
||||||
@ -49,7 +55,7 @@ class Radio(commands.Cog):
|
|||||||
async def radio_init(self) -> None:
|
async def radio_init(self) -> None:
|
||||||
"""Init Radio"""
|
"""Init Radio"""
|
||||||
try:
|
try:
|
||||||
(radio_guild, radio_chan) = self.channels['sfm']
|
(radio_guild, radio_chan) = self.channels["sfm"]
|
||||||
guild: Optional[discord.Guild] = self.bot.get_guild(radio_guild)
|
guild: Optional[discord.Guild] = self.bot.get_guild(radio_guild)
|
||||||
if not guild:
|
if not guild:
|
||||||
return
|
return
|
||||||
@ -62,8 +68,7 @@ class Radio(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
self.radio_state_loop.cancel()
|
self.radio_state_loop.cancel()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.debug("Failed to cancel radio_state_loop: %s",
|
logging.debug("Failed to cancel radio_state_loop: %s", str(e))
|
||||||
str(e))
|
|
||||||
self.radio_state_loop.start()
|
self.radio_state_loop.start()
|
||||||
logging.info("radio_state_loop task started!")
|
logging.info("radio_state_loop task started!")
|
||||||
except:
|
except:
|
||||||
@ -77,7 +82,7 @@ class Radio(commands.Cog):
|
|||||||
async def radio_state_loop(self) -> None:
|
async def radio_state_loop(self) -> None:
|
||||||
"""Radio State Loop"""
|
"""Radio State Loop"""
|
||||||
try:
|
try:
|
||||||
(radio_guild, radio_chan) = self.channels['sfm']
|
(radio_guild, radio_chan) = self.channels["sfm"]
|
||||||
try:
|
try:
|
||||||
vc: discord.VoiceProtocol = self.bot.voice_clients[-1]
|
vc: discord.VoiceProtocol = self.bot.voice_clients[-1]
|
||||||
except:
|
except:
|
||||||
@ -97,16 +102,22 @@ class Radio(commands.Cog):
|
|||||||
but they exist.
|
but they exist.
|
||||||
"""
|
"""
|
||||||
logging.info("Detected VC not playing... playing!")
|
logging.info("Detected VC not playing... playing!")
|
||||||
source: discord.FFmpegAudio = discord.FFmpegOpusAudio(self.STREAM_URL,
|
source: discord.FFmpegAudio = discord.FFmpegOpusAudio(
|
||||||
before_options="-timeout 3000000")
|
self.STREAM_URL, before_options="-timeout 3000000"
|
||||||
vc.play(source, # type: ignore
|
)
|
||||||
after=lambda e: logging.info("Error: %s", e) if e\
|
vc.play(
|
||||||
else None)
|
source, # type: ignore
|
||||||
|
after=lambda e: logging.info("Error: %s", e) if e else None,
|
||||||
|
)
|
||||||
# Get Now Playing
|
# Get Now Playing
|
||||||
np_track: Optional[str] = await get_now_playing()
|
np_track: Optional[str] = await get_now_playing()
|
||||||
if np_track and not self.LAST_NP_TRACK == np_track:
|
if np_track and not self.LAST_NP_TRACK == np_track:
|
||||||
self.LAST_NP_TRACK = np_track
|
self.LAST_NP_TRACK = np_track
|
||||||
await self.bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=np_track))
|
await self.bot.change_presence(
|
||||||
|
activity=discord.Activity(
|
||||||
|
type=discord.ActivityType.listening, name=np_track
|
||||||
|
)
|
||||||
|
)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
@ -120,11 +131,11 @@ class Radio(commands.Cog):
|
|||||||
await skip()
|
await skip()
|
||||||
return await ctx.respond("OK", ephemeral=True)
|
return await ctx.respond("OK", ephemeral=True)
|
||||||
|
|
||||||
|
|
||||||
def cog_unload(self) -> None:
|
def cog_unload(self) -> None:
|
||||||
"""Run on Cog Unload"""
|
"""Run on Cog Unload"""
|
||||||
self.radio_state_loop.cancel()
|
self.radio_state_loop.cancel()
|
||||||
|
|
||||||
|
|
||||||
def setup(bot) -> None:
|
def setup(bot) -> None:
|
||||||
"""Run on Cog Load"""
|
"""Run on Cog Load"""
|
||||||
bot.add_cog(Radio(bot))
|
bot.add_cog(Radio(bot))
|
156
cogs/sing.py
156
cogs/sing.py
@ -11,18 +11,23 @@ from disc_havoc import Havoc
|
|||||||
|
|
||||||
BOT_CHANIDS = []
|
BOT_CHANIDS = []
|
||||||
|
|
||||||
|
|
||||||
class Sing(commands.Cog):
|
class Sing(commands.Cog):
|
||||||
"""Sing Cog for Havoc"""
|
"""Sing Cog for Havoc"""
|
||||||
|
|
||||||
def __init__(self, bot: Havoc) -> None:
|
def __init__(self, bot: Havoc) -> None:
|
||||||
self.bot: Havoc = bot
|
self.bot: Havoc = bot
|
||||||
self.utility = Utility()
|
self.utility = Utility()
|
||||||
global BOT_CHANIDS
|
global BOT_CHANIDS
|
||||||
BOT_CHANIDS = self.bot.BOT_CHANIDS # Inherit
|
BOT_CHANIDS = self.bot.BOT_CHANIDS # Inherit
|
||||||
self.control_strip_regex: Pattern = regex.compile(r"\x0f|\x1f|\035|\002|\u2064|\x02|(\x03([0-9]{1,2}))|(\x03|\003)(?:\d{1,2}(?:,\d{1,2})?)?",
|
self.control_strip_regex: Pattern = regex.compile(
|
||||||
regex.UNICODE)
|
r"\x0f|\x1f|\035|\002|\u2064|\x02|(\x03([0-9]{1,2}))|(\x03|\003)(?:\d{1,2}(?:,\d{1,2})?)?",
|
||||||
|
regex.UNICODE,
|
||||||
|
)
|
||||||
|
|
||||||
def is_spamchan(): # type: ignore
|
def is_spamchan(): # type: ignore
|
||||||
"""Check if channel is spam chan"""
|
"""Check if channel is spam chan"""
|
||||||
|
|
||||||
def predicate(ctx):
|
def predicate(ctx):
|
||||||
try:
|
try:
|
||||||
if not ctx.channel.id in BOT_CHANIDS:
|
if not ctx.channel.id in BOT_CHANIDS:
|
||||||
@ -31,18 +36,19 @@ class Sing(commands.Cog):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return commands.check(predicate)
|
return commands.check(predicate)
|
||||||
|
|
||||||
@bridge.bridge_command(aliases=['sing'])
|
@bridge.bridge_command(aliases=["sing"])
|
||||||
async def s(self, ctx, *,
|
async def s(self, ctx, *, song: Optional[str] = None) -> None:
|
||||||
song: Optional[str] = None) -> None:
|
|
||||||
"""
|
"""
|
||||||
Search for lyrics, format is artist : song. Also reads activity.
|
Search for lyrics, format is artist : song. Also reads activity.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with ctx.channel.typing():
|
with ctx.channel.typing():
|
||||||
interaction: bool = isinstance(ctx,
|
interaction: bool = isinstance(
|
||||||
discord.ext.bridge.BridgeApplicationContext)
|
ctx, discord.ext.bridge.BridgeApplicationContext
|
||||||
|
)
|
||||||
activity: Optional[discord.Activity] = None
|
activity: Optional[discord.Activity] = None
|
||||||
if not song:
|
if not song:
|
||||||
if not ctx.author.activities:
|
if not ctx.author.activities:
|
||||||
@ -53,10 +59,14 @@ class Sing(commands.Cog):
|
|||||||
activity = _activity
|
activity = _activity
|
||||||
|
|
||||||
if not activity:
|
if not activity:
|
||||||
return await ctx.respond("**Error**: No song specified, no activity found to read.")
|
return await ctx.respond(
|
||||||
|
"**Error**: No song specified, no activity found to read."
|
||||||
|
)
|
||||||
|
|
||||||
if interaction:
|
if interaction:
|
||||||
await ctx.respond("*Searching...*") # Must respond to interactions within 3 seconds, per Discord
|
await ctx.respond(
|
||||||
|
"*Searching...*"
|
||||||
|
) # Must respond to interactions within 3 seconds, per Discord
|
||||||
|
|
||||||
parsed = self.utility.parse_song_input(song, activity)
|
parsed = self.utility.parse_song_input(song, activity)
|
||||||
|
|
||||||
@ -64,8 +74,9 @@ class Sing(commands.Cog):
|
|||||||
(search_artist, search_song, search_subsearch) = parsed
|
(search_artist, search_song, search_subsearch) = parsed
|
||||||
|
|
||||||
# await ctx.respond(f"So, {search_song} by {search_artist}? Subsearch: {search_subsearch} I will try...") # Commented, useful for debugging
|
# await ctx.respond(f"So, {search_song} by {search_artist}? Subsearch: {search_subsearch} I will try...") # Commented, useful for debugging
|
||||||
search_result: Optional[list] = await self.utility.lyric_search(search_artist, search_song,
|
search_result: Optional[list] = await self.utility.lyric_search(
|
||||||
search_subsearch)
|
search_artist, search_song, search_subsearch
|
||||||
|
)
|
||||||
|
|
||||||
if not search_result:
|
if not search_result:
|
||||||
await ctx.respond("ERR: No search result.")
|
await ctx.respond("ERR: No search result.")
|
||||||
@ -78,27 +89,44 @@ class Sing(commands.Cog):
|
|||||||
if not isinstance(search_result[0], tuple):
|
if not isinstance(search_result[0], tuple):
|
||||||
return # Invalid data type
|
return # Invalid data type
|
||||||
(
|
(
|
||||||
search_result_artist, search_result_song, search_result_src,
|
search_result_artist,
|
||||||
search_result_confidence, search_result_time_taken
|
search_result_song,
|
||||||
) = search_result[0] # First index is a tuple
|
search_result_src,
|
||||||
search_result_wrapped: list[str] = search_result[1] # Second index is the wrapped lyrics
|
search_result_confidence,
|
||||||
search_result_wrapped_short: list[str] = search_result[2] # Third is short wrapped lyrics
|
search_result_time_taken,
|
||||||
|
) = search_result[
|
||||||
|
0
|
||||||
|
] # First index is a tuple
|
||||||
|
search_result_wrapped: list[str] = search_result[
|
||||||
|
1
|
||||||
|
] # Second index is the wrapped lyrics
|
||||||
|
search_result_wrapped_short: list[str] = search_result[
|
||||||
|
2
|
||||||
|
] # Third is short wrapped lyrics
|
||||||
if not ctx.channel.id in BOT_CHANIDS:
|
if not ctx.channel.id in BOT_CHANIDS:
|
||||||
short_lyrics = " ".join(search_result_wrapped_short) # Replace with shortened lyrics for non spamchans
|
short_lyrics = " ".join(
|
||||||
short_lyrics = regex.sub(r'\p{Vert_Space}', ' / ', short_lyrics.strip())
|
search_result_wrapped_short
|
||||||
return await ctx.respond(f"**{search_result_song}** by **{search_result_artist}**\n-# {short_lyrics}")
|
) # Replace with shortened lyrics for non spamchans
|
||||||
|
short_lyrics = regex.sub(
|
||||||
|
r"\p{Vert_Space}", " / ", short_lyrics.strip()
|
||||||
|
)
|
||||||
|
return await ctx.respond(
|
||||||
|
f"**{search_result_song}** by **{search_result_artist}**\n-# {short_lyrics}"
|
||||||
|
)
|
||||||
|
|
||||||
c: int = 0
|
c: int = 0
|
||||||
out_messages: list = []
|
out_messages: list = []
|
||||||
footer: str = "" # Placeholder
|
footer: str = "" # Placeholder
|
||||||
for section in search_result_wrapped:
|
for section in search_result_wrapped:
|
||||||
c+=1
|
c += 1
|
||||||
if c == len(search_result_wrapped):
|
if c == len(search_result_wrapped):
|
||||||
footer = f"`Found on: {search_result_src}`"
|
footer = f"`Found on: {search_result_src}`"
|
||||||
# if ctx.guild.id == 1145182936002482196:
|
# if ctx.guild.id == 1145182936002482196:
|
||||||
# section = section.upper()
|
# section = section.upper()
|
||||||
section = regex.sub(r'\p{Vert_Space}', ' / ', section.strip())
|
section = regex.sub(r"\p{Vert_Space}", " / ", section.strip())
|
||||||
msg: str = f"**{search_result_song}** by **{search_result_artist}**\n-# {section}\n{footer}"
|
msg: str = (
|
||||||
|
f"**{search_result_song}** by **{search_result_artist}**\n-# {section}\n{footer}"
|
||||||
|
)
|
||||||
if c > 1:
|
if c > 1:
|
||||||
msg = "\n".join(msg.split("\n")[1:])
|
msg = "\n".join(msg.split("\n")[1:])
|
||||||
out_messages.append(msg.strip())
|
out_messages.append(msg.strip())
|
||||||
@ -122,58 +150,89 @@ class Sing(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
PODY_ID: int = 1172340700663255091
|
PODY_ID: int = 1172340700663255091
|
||||||
IS_SPAMCHAN: bool = ctx.channel.id in BOT_CHANIDS
|
IS_SPAMCHAN: bool = ctx.channel.id in BOT_CHANIDS
|
||||||
member_display = ctx.interaction.guild.get_member(member.id)\
|
member_display = ctx.interaction.guild.get_member(member.id).display_name
|
||||||
.display_name
|
if (
|
||||||
if not(ctx.interaction.guild.get_member(member.id).activities)\
|
not (ctx.interaction.guild.get_member(member.id).activities)
|
||||||
and not member.id == PODY_ID:
|
and not member.id == PODY_ID
|
||||||
return await ctx.respond(f"No activity detected to read for {member_display}.", ephemeral=True)
|
):
|
||||||
member_id: int = member.id #if not(member.id == PODY_ID) else 1234134345497837679 # Use Thomas for Pody!
|
return await ctx.respond(
|
||||||
|
f"No activity detected to read for {member_display}.",
|
||||||
|
ephemeral=True,
|
||||||
|
)
|
||||||
|
member_id: int = (
|
||||||
|
member.id
|
||||||
|
) # if not(member.id == PODY_ID) else 1234134345497837679 # Use Thomas for Pody!
|
||||||
activity: Optional[discord.Activity] = None
|
activity: Optional[discord.Activity] = None
|
||||||
if IS_SPAMCHAN:
|
if IS_SPAMCHAN:
|
||||||
await ctx.respond(f"***Reading activity of {member_display}...***")
|
await ctx.respond(f"***Reading activity of {member_display}...***")
|
||||||
for _activity in ctx.interaction.guild.get_member(member_id).activities:
|
for _activity in ctx.interaction.guild.get_member(member_id).activities:
|
||||||
if _activity.type == discord.ActivityType.listening:
|
if _activity.type == discord.ActivityType.listening:
|
||||||
activity = _activity
|
activity = _activity
|
||||||
parsed: Union[tuple, bool] = self.utility.parse_song_input(song=None,
|
parsed: Union[tuple, bool] = self.utility.parse_song_input(
|
||||||
activity=activity)
|
song=None, activity=activity
|
||||||
|
)
|
||||||
if not parsed:
|
if not parsed:
|
||||||
return await ctx.respond(f"Could not parse activity of {member_display}.", ephemeral=True)
|
return await ctx.respond(
|
||||||
|
f"Could not parse activity of {member_display}.", ephemeral=True
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(parsed, tuple):
|
if isinstance(parsed, tuple):
|
||||||
(search_artist, search_song, search_subsearch) = parsed
|
(search_artist, search_song, search_subsearch) = parsed
|
||||||
await ctx.respond("*Searching...*") # Must respond to interactions within 3 seconds, per Discord
|
await ctx.respond(
|
||||||
search_result: Optional[list] = await self.utility.lyric_search(search_artist, search_song,
|
"*Searching...*"
|
||||||
search_subsearch)
|
) # Must respond to interactions within 3 seconds, per Discord
|
||||||
|
search_result: Optional[list] = await self.utility.lyric_search(
|
||||||
|
search_artist, search_song, search_subsearch
|
||||||
|
)
|
||||||
if not search_result:
|
if not search_result:
|
||||||
await ctx.respond("ERR: No search result")
|
await ctx.respond("ERR: No search result")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if len(search_result) == 1 and isinstance(search_result[0][0], str):
|
||||||
|
return await ctx.send(
|
||||||
|
"ERR: No search result"
|
||||||
|
) # Error message from API
|
||||||
|
|
||||||
if len(search_result) == 1 and\
|
(
|
||||||
isinstance(search_result[0][0], str):
|
search_result_artist,
|
||||||
return await ctx.send("ERR: No search result") # Error message from API
|
search_result_song,
|
||||||
|
search_result_src,
|
||||||
(search_result_artist, search_result_song, search_result_src,
|
search_result_confidence,
|
||||||
search_result_confidence, search_result_time_taken) = search_result[0] # First index is a tuple
|
search_result_time_taken,
|
||||||
search_result_wrapped: list = search_result[1] # Second index is the wrapped lyrics
|
) = search_result[
|
||||||
search_result_wrapped_short: list[str] = search_result[2] # Third index is shortened lyrics
|
0
|
||||||
|
] # First index is a tuple
|
||||||
|
search_result_wrapped: list = search_result[
|
||||||
|
1
|
||||||
|
] # Second index is the wrapped lyrics
|
||||||
|
search_result_wrapped_short: list[str] = search_result[
|
||||||
|
2
|
||||||
|
] # Third index is shortened lyrics
|
||||||
|
|
||||||
if not IS_SPAMCHAN:
|
if not IS_SPAMCHAN:
|
||||||
short_lyrics = " ".join(search_result_wrapped_short) # Replace with shortened lyrics for non spamchans
|
short_lyrics = " ".join(
|
||||||
short_lyrics = regex.sub(r'\p{Vert_Space}', ' / ', short_lyrics.strip())
|
search_result_wrapped_short
|
||||||
return await ctx.respond(f"**{search_result_song}** by **{search_result_artist}**\n-# {short_lyrics}")
|
) # Replace with shortened lyrics for non spamchans
|
||||||
|
short_lyrics = regex.sub(
|
||||||
|
r"\p{Vert_Space}", " / ", short_lyrics.strip()
|
||||||
|
)
|
||||||
|
return await ctx.respond(
|
||||||
|
f"**{search_result_song}** by **{search_result_artist}**\n-# {short_lyrics}"
|
||||||
|
)
|
||||||
|
|
||||||
out_messages: list = []
|
out_messages: list = []
|
||||||
footer: str = ""
|
footer: str = ""
|
||||||
c: int = 0
|
c: int = 0
|
||||||
for section in search_result_wrapped:
|
for section in search_result_wrapped:
|
||||||
c+=1
|
c += 1
|
||||||
if c == len(search_result_wrapped):
|
if c == len(search_result_wrapped):
|
||||||
footer = f"`Found on: {search_result_src}`"
|
footer = f"`Found on: {search_result_src}`"
|
||||||
# if ctx.guild.id == 1145182936002482196:
|
# if ctx.guild.id == 1145182936002482196:
|
||||||
# section = section.upper()
|
# section = section.upper()
|
||||||
section = regex.sub(r'\p{Vert_Space}', ' / ', section.strip())
|
section = regex.sub(r"\p{Vert_Space}", " / ", section.strip())
|
||||||
msg: str = f"**{search_result_song}** by **{search_result_artist}**\n-# {section}\n{footer}"
|
msg: str = (
|
||||||
|
f"**{search_result_song}** by **{search_result_artist}**\n-# {section}\n{footer}"
|
||||||
|
)
|
||||||
if c > 1:
|
if c > 1:
|
||||||
msg = "\n".join(msg.split("\n")[1:])
|
msg = "\n".join(msg.split("\n")[1:])
|
||||||
out_messages.append(msg.strip())
|
out_messages.append(msg.strip())
|
||||||
@ -183,6 +242,7 @@ class Sing(commands.Cog):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return await ctx.respond(f"ERR: {str(e)}")
|
return await ctx.respond(f"ERR: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
def setup(bot) -> None:
|
def setup(bot) -> None:
|
||||||
"""Run on Cog Load"""
|
"""Run on Cog Load"""
|
||||||
bot.add_cog(Sing(bot))
|
bot.add_cog(Sing(bot))
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
"""
|
"""
|
||||||
AI
|
AI
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class AIException(Exception):
|
class AIException(Exception):
|
||||||
"""AI Exception (generic)"""
|
"""AI Exception (generic)"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
LoveHate
|
LoveHate
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class LoveHateException(Exception):
|
class LoveHateException(Exception):
|
||||||
"""Love Hate Exception (generic)"""
|
"""Love Hate Exception (generic)"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -17,6 +24,8 @@ class LoveHateException(Exception):
|
|||||||
Misc
|
Misc
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MiscException(Exception):
|
class MiscException(Exception):
|
||||||
"""Misc Exception (generic)"""
|
"""Misc Exception (generic)"""
|
||||||
|
|
||||||
pass
|
pass
|
@ -13,20 +13,20 @@ from termcolor import colored
|
|||||||
from constants import OWNERS, BOT_CHANIDS
|
from constants import OWNERS, BOT_CHANIDS
|
||||||
import api
|
import api
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO,
|
logging.basicConfig(
|
||||||
format='%(asctime)s %(message)s',
|
level=logging.INFO, format="%(asctime)s %(message)s", encoding="utf-8"
|
||||||
encoding='utf-8')
|
)
|
||||||
setproctitle.setproctitle('disc-havoc')
|
setproctitle.setproctitle("disc-havoc")
|
||||||
|
|
||||||
"""Auto Load Cogs"""
|
"""Auto Load Cogs"""
|
||||||
cogs_list: list[str] = [
|
cogs_list: list[str] = [
|
||||||
'misc',
|
"misc",
|
||||||
'owner',
|
"owner",
|
||||||
'sing',
|
"sing",
|
||||||
'meme',
|
"meme",
|
||||||
'karma',
|
"karma",
|
||||||
'lovehate',
|
"lovehate",
|
||||||
'radio',
|
"radio",
|
||||||
]
|
]
|
||||||
|
|
||||||
bot_activity = discord.CustomActivity(name="I made cookies!")
|
bot_activity = discord.CustomActivity(name="I made cookies!")
|
||||||
@ -36,15 +36,19 @@ load_dotenv()
|
|||||||
intents = discord.Intents.all()
|
intents = discord.Intents.all()
|
||||||
intents.message_content = True
|
intents.message_content = True
|
||||||
|
|
||||||
|
|
||||||
class Havoc(bridge.Bot):
|
class Havoc(bridge.Bot):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(command_prefix=".", intents=intents,
|
super().__init__(
|
||||||
owner_ids=OWNERS, activity=bot_activity,
|
command_prefix=".",
|
||||||
help_command=commands.MinimalHelpCommand())
|
intents=intents,
|
||||||
|
owner_ids=OWNERS,
|
||||||
|
activity=bot_activity,
|
||||||
|
help_command=commands.MinimalHelpCommand(),
|
||||||
|
)
|
||||||
self.BOT_CHANIDS = BOT_CHANIDS
|
self.BOT_CHANIDS = BOT_CHANIDS
|
||||||
self.load_exts()
|
self.load_exts()
|
||||||
|
|
||||||
|
|
||||||
def load_exts(self, initialRun: Optional[bool] = True) -> None:
|
def load_exts(self, initialRun: Optional[bool] = True) -> None:
|
||||||
"""
|
"""
|
||||||
Load Cogs/Extensions
|
Load Cogs/Extensions
|
||||||
@ -54,29 +58,28 @@ class Havoc(bridge.Bot):
|
|||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
load_method = self.load_extension if initialRun\
|
load_method = self.load_extension if initialRun else self.reload_extension
|
||||||
else self.reload_extension
|
|
||||||
|
|
||||||
for cog in cogs_list:
|
for cog in cogs_list:
|
||||||
logging.info("Loading: %s", cog)
|
logging.info("Loading: %s", cog)
|
||||||
load_method(f'cogs.{cog}')
|
load_method(f"cogs.{cog}")
|
||||||
|
|
||||||
importlib.reload(api)
|
importlib.reload(api)
|
||||||
from api import API
|
from api import API
|
||||||
|
|
||||||
api_config = hypercorn.config.Config()
|
api_config = hypercorn.config.Config()
|
||||||
api_config.bind = ["127.0.0.1:5992"]
|
api_config.bind = ["127.0.0.1:5992"]
|
||||||
api_instance = api.API(self)
|
api_instance = api.API(self)
|
||||||
try:
|
try:
|
||||||
self.fapi_task.cancel()
|
self.fapi_task.cancel()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.debug("Failed to cancel fapi_task: %s",
|
logging.debug("Failed to cancel fapi_task: %s", str(e))
|
||||||
str(e))
|
|
||||||
|
|
||||||
logging.info("Starting FAPI Task")
|
logging.info("Starting FAPI Task")
|
||||||
|
|
||||||
self.fapi_task: Task = self.loop.create_task(hypercorn.asyncio.serve(api_instance.api_app,
|
self.fapi_task: Task = self.loop.create_task(
|
||||||
api_config))
|
hypercorn.asyncio.serve(api_instance.api_app, api_config)
|
||||||
|
)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_ready(self) -> None:
|
async def on_ready(self) -> None:
|
||||||
@ -84,12 +87,17 @@ class Havoc(bridge.Bot):
|
|||||||
logging.info("%s online!", self.user)
|
logging.info("%s online!", self.user)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__() -> None:
|
def __init__() -> None:
|
||||||
logging.info(colored(f"Log level: {logging.getLevelName(logging.root.level)}",
|
logging.info(
|
||||||
"red", attrs=['reverse']))
|
colored(
|
||||||
|
f"Log level: {logging.getLevelName(logging.root.level)}",
|
||||||
|
"red",
|
||||||
|
attrs=["reverse"],
|
||||||
|
)
|
||||||
|
)
|
||||||
bot = Havoc()
|
bot = Havoc()
|
||||||
bot.run(os.getenv('TOKEN'))
|
bot.run(os.getenv("TOKEN"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
__init__()
|
__init__()
|
@ -12,14 +12,20 @@ from util.catbox import CatboxAsync
|
|||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
async def test() -> None:
|
async def test() -> None:
|
||||||
f = os.path.join(os.path.expanduser("~"), "qu.png")
|
f = os.path.join(os.path.expanduser("~"), "qu.png")
|
||||||
box1: LitterboxAsync = LitterboxAsync()
|
box1: LitterboxAsync = LitterboxAsync()
|
||||||
box2: CatboxAsync = CatboxAsync()
|
box2: CatboxAsync = CatboxAsync()
|
||||||
url1: Optional[str] = await box1.upload(f)
|
url1: Optional[str] = await box1.upload(f)
|
||||||
url2: Optional[str] = await box2.upload(f)
|
url2: Optional[str] = await box2.upload(f)
|
||||||
logging.info("""Uploaded URLs:
|
logging.info(
|
||||||
|
"""Uploaded URLs:
|
||||||
Litter - %s\n
|
Litter - %s\n
|
||||||
Cat - %s""", url1, url2)
|
Cat - %s""",
|
||||||
|
url1,
|
||||||
|
url2,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(test())
|
asyncio.run(test())
|
@ -12,12 +12,13 @@ Catbox Uploader (Async)
|
|||||||
|
|
||||||
catbox_api_url: str = "https://catbox.moe/user/api.php"
|
catbox_api_url: str = "https://catbox.moe/user/api.php"
|
||||||
http_headers: dict[str, str] = {
|
http_headers: dict[str, str] = {
|
||||||
'accept': '*/*',
|
"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',
|
"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',
|
"Accept-Language": "en-US,en;q=0.9,it;q=0.8,es;q=0.7",
|
||||||
'referer': 'https://www.google.com/',
|
"referer": "https://www.google.com/",
|
||||||
'cookie': 'DSID=AAO-7r4OSkS76zbHUkiOpnI0kk-X19BLDFF53G8gbnd21VZV2iehu-w_2v14cxvRvrkd_NjIdBWX7wUiQ66f-D8kOkTKD1BhLVlqrFAaqDP3LodRK2I0NfrObmhV9HsedGE7-mQeJpwJifSxdchqf524IMh9piBflGqP0Lg0_xjGmLKEQ0F4Na6THgC06VhtUG5infEdqMQ9otlJENe3PmOQTC_UeTH5DnENYwWC8KXs-M4fWmDADmG414V0_X0TfjrYu01nDH2Dcf3TIOFbRDb993g8nOCswLMi92LwjoqhYnFdf1jzgK0'
|
"cookie": "DSID=AAO-7r4OSkS76zbHUkiOpnI0kk-X19BLDFF53G8gbnd21VZV2iehu-w_2v14cxvRvrkd_NjIdBWX7wUiQ66f-D8kOkTKD1BhLVlqrFAaqDP3LodRK2I0NfrObmhV9HsedGE7-mQeJpwJifSxdchqf524IMh9piBflGqP0Lg0_xjGmLKEQ0F4Na6THgC06VhtUG5infEdqMQ9otlJENe3PmOQTC_UeTH5DnENYwWC8KXs-M4fWmDADmG414V0_X0TfjrYu01nDH2Dcf3TIOFbRDb993g8nOCswLMi92LwjoqhYnFdf1jzgK0",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CatboxAsync:
|
class CatboxAsync:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@ -34,7 +35,7 @@ class CatboxAsync:
|
|||||||
str
|
str
|
||||||
"""
|
"""
|
||||||
if not fileExt:
|
if not fileExt:
|
||||||
fileExt = 'png'
|
fileExt = "png"
|
||||||
return f"{random.getrandbits(32)}.{fileExt}"
|
return f"{random.getrandbits(32)}.{fileExt}"
|
||||||
|
|
||||||
async def upload(self, file: str) -> Optional[str]:
|
async def upload(self, file: str) -> Optional[str]:
|
||||||
@ -49,33 +50,33 @@ class CatboxAsync:
|
|||||||
try:
|
try:
|
||||||
if not file:
|
if not file:
|
||||||
return None
|
return None
|
||||||
if not(os.path.exists(file)):
|
if not (os.path.exists(file)):
|
||||||
logging.critical("Could not find %s",
|
logging.critical("Could not find %s", file)
|
||||||
file)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
fileExt: Optional[str] = None
|
fileExt: Optional[str] = None
|
||||||
if file.find(".") > 0:
|
if file.find(".") > 0:
|
||||||
fileExt = "".join(file.split(".")[-1:])
|
fileExt = "".join(file.split(".")[-1:])
|
||||||
|
|
||||||
with open(file, 'rb') as fileContents:
|
with open(file, "rb") as fileContents:
|
||||||
post_data: FormData = FormData()
|
post_data: FormData = FormData()
|
||||||
post_data.add_field(name="reqtype",
|
post_data.add_field(name="reqtype", value="fileupload")
|
||||||
value="fileupload")
|
post_data.add_field(name="userhash", value="")
|
||||||
post_data.add_field(name="userhash",
|
|
||||||
value="")
|
|
||||||
with magic.Magic(flags=magic.MAGIC_MIME) as m:
|
with magic.Magic(flags=magic.MAGIC_MIME) as m:
|
||||||
content_type = m.id_filename(file)
|
content_type = m.id_filename(file)
|
||||||
post_data.add_field(name="fileToUpload",
|
post_data.add_field(
|
||||||
|
name="fileToUpload",
|
||||||
value=fileContents,
|
value=fileContents,
|
||||||
filename=self.generateRandomFileName(fileExt),
|
filename=self.generateRandomFileName(fileExt),
|
||||||
content_type=content_type
|
content_type=content_type,
|
||||||
)
|
)
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with await session.post(self.catbox_api_url,
|
async with await session.post(
|
||||||
|
self.catbox_api_url,
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
data=post_data,
|
data=post_data,
|
||||||
timeout=ClientTimeout(connect=10, sock_read=10)) as request:
|
timeout=ClientTimeout(connect=10, sock_read=10),
|
||||||
|
) as request:
|
||||||
request.raise_for_status()
|
request.raise_for_status()
|
||||||
return await request.text()
|
return await request.text()
|
||||||
except:
|
except:
|
||||||
|
@ -6,8 +6,10 @@ from typing import Optional, Any
|
|||||||
Discord Helper Methods
|
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
|
Get Channel by Name
|
||||||
|
|
||||||
@ -18,10 +20,9 @@ async def get_channel_by_name(bot: discord.Bot, channel: str,
|
|||||||
Returns:
|
Returns:
|
||||||
Optional[Any]
|
Optional[Any]
|
||||||
"""
|
"""
|
||||||
channel = re.sub(r'^#', '', channel.strip())
|
channel = re.sub(r"^#", "", channel.strip())
|
||||||
if not guild:
|
if not guild:
|
||||||
return discord.utils.get(bot.get_all_channels(),
|
return discord.utils.get(bot.get_all_channels(), name=channel)
|
||||||
name=channel)
|
|
||||||
else:
|
else:
|
||||||
_guild: Optional[discord.Guild] = bot.get_guild(guild)
|
_guild: Optional[discord.Guild] = bot.get_guild(guild)
|
||||||
if not _guild:
|
if not _guild:
|
||||||
@ -32,8 +33,10 @@ async def get_channel_by_name(bot: discord.Bot, channel: str,
|
|||||||
return _channel
|
return _channel
|
||||||
return None
|
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
|
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.
|
channel is selected. Useful in the event a channel exists in more than one guild that the bot resides in.
|
||||||
@ -50,9 +53,8 @@ async def send_message(bot: discord.Bot, channel: str,
|
|||||||
channel_int: int = int(channel)
|
channel_int: int = int(channel)
|
||||||
_channel = bot.get_channel(channel_int)
|
_channel = bot.get_channel(channel_int)
|
||||||
else:
|
else:
|
||||||
channel = re.sub(r'^#', '', channel.strip())
|
channel = re.sub(r"^#", "", channel.strip())
|
||||||
_channel = await get_channel_by_name(bot=bot,
|
_channel = await get_channel_by_name(bot=bot, channel=channel, guild=guild)
|
||||||
channel=channel, guild=guild)
|
|
||||||
if not isinstance(_channel, discord.TextChannel):
|
if not isinstance(_channel, discord.TextChannel):
|
||||||
return None
|
return None
|
||||||
await _channel.send(message)
|
await _channel.send(message)
|
||||||
|
@ -13,24 +13,23 @@ Jesus Meme Generator
|
|||||||
(requires Catbox uploader)
|
(requires Catbox uploader)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class JesusMemeGenerator():
|
|
||||||
|
class JesusMemeGenerator:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.MEMEAPIURL = "https://apimeme.com/meme?meme="
|
self.MEMEAPIURL = "https://apimeme.com/meme?meme="
|
||||||
self.MEMESTORAGEDIR = os.path.join(os.path.expanduser("~"),
|
self.MEMESTORAGEDIR = os.path.join(os.path.expanduser("~"), "memes")
|
||||||
"memes")
|
|
||||||
self.top_line_regex: Pattern = regex.compile(
|
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(
|
self.bottom_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.url_regex_1: Pattern = regex.compile(
|
self.url_regex_1: Pattern = regex.compile(r"\p{Horiz_Space}")
|
||||||
r'\p{Horiz_Space}')
|
self.url_regex_2: Pattern = regex.compile(r"#")
|
||||||
self.url_regex_2: Pattern = regex.compile(
|
|
||||||
r'#')
|
|
||||||
|
|
||||||
async def create_meme(self, top_line: str, bottom_line: str,
|
async def create_meme(
|
||||||
meme="Jesus-Talking-To-Cool-Dude") -> Optional[str]:
|
self, top_line: str, bottom_line: str, meme="Jesus-Talking-To-Cool-Dude"
|
||||||
|
) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Create Meme
|
Create Meme
|
||||||
|
|
||||||
@ -46,28 +45,32 @@ class JesusMemeGenerator():
|
|||||||
try:
|
try:
|
||||||
if not top_line or not bottom_line:
|
if not top_line or not bottom_line:
|
||||||
return None
|
return None
|
||||||
top_line = self.top_line_regex.sub('',
|
top_line = self.top_line_regex.sub("", top_line.strip())
|
||||||
top_line.strip())
|
bottom_line = self.bottom_line_regex.sub("", bottom_line.strip())
|
||||||
bottom_line = self.bottom_line_regex.sub('',
|
|
||||||
bottom_line.strip())
|
|
||||||
out_fname: Optional[str] = None
|
out_fname: Optional[str] = None
|
||||||
|
|
||||||
if len(top_line) < 1 or len(bottom_line) < 1:
|
if len(top_line) < 1 or len(bottom_line) < 1:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
formed_url: str = f"{self.MEMEAPIURL}{meme}&top={top_line.strip()}&bottom={bottom_line.strip()}"
|
formed_url: str = (
|
||||||
formed_url = self.url_regex_1.sub('+', self.url_regex_2.sub('%23', formed_url.strip()))
|
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)
|
timeout = aiohttp.ClientTimeout(total=15)
|
||||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||||
async with session.get(formed_url) as response:
|
async with session.get(formed_url) as response:
|
||||||
UUID = f"{random.getrandbits(8)}-{random.getrandbits(8)}"
|
UUID = f"{random.getrandbits(8)}-{random.getrandbits(8)}"
|
||||||
out_fname = f"{UUID}.jpg"
|
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())
|
f.write(await response.read())
|
||||||
|
|
||||||
if not out_fname:
|
if not out_fname:
|
||||||
uploader = CatboxAsync()
|
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:
|
if not meme_link:
|
||||||
logging.info("Meme upload failed!")
|
logging.info("Meme upload failed!")
|
||||||
return None
|
return None
|
||||||
|
@ -13,12 +13,13 @@ import os
|
|||||||
|
|
||||||
litterbox_api_url: str = "https://litterbox.catbox.moe/resources/internals/api.php"
|
litterbox_api_url: str = "https://litterbox.catbox.moe/resources/internals/api.php"
|
||||||
http_headers: dict[str, str] = {
|
http_headers: dict[str, str] = {
|
||||||
'accept': '*/*',
|
"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',
|
"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',
|
"Accept-Language": "en-US,en;q=0.9,it;q=0.8,es;q=0.7",
|
||||||
'referer': 'https://www.google.com/',
|
"referer": "https://www.google.com/",
|
||||||
'cookie': 'DSID=AAO-7r4OSkS76zbHUkiOpnI0kk-X19BLDFF53G8gbnd21VZV2iehu-w_2v14cxvRvrkd_NjIdBWX7wUiQ66f-D8kOkTKD1BhLVlqrFAaqDP3LodRK2I0NfrObmhV9HsedGE7-mQeJpwJifSxdchqf524IMh9piBflGqP0Lg0_xjGmLKEQ0F4Na6THgC06VhtUG5infEdqMQ9otlJENe3PmOQTC_UeTH5DnENYwWC8KXs-M4fWmDADmG414V0_X0TfjrYu01nDH2Dcf3TIOFbRDb993g8nOCswLMi92LwjoqhYnFdf1jzgK0'
|
"cookie": "DSID=AAO-7r4OSkS76zbHUkiOpnI0kk-X19BLDFF53G8gbnd21VZV2iehu-w_2v14cxvRvrkd_NjIdBWX7wUiQ66f-D8kOkTKD1BhLVlqrFAaqDP3LodRK2I0NfrObmhV9HsedGE7-mQeJpwJifSxdchqf524IMh9piBflGqP0Lg0_xjGmLKEQ0F4Na6THgC06VhtUG5infEdqMQ9otlJENe3PmOQTC_UeTH5DnENYwWC8KXs-M4fWmDADmG414V0_X0TfjrYu01nDH2Dcf3TIOFbRDb993g8nOCswLMi92LwjoqhYnFdf1jzgK0",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LitterboxAsync:
|
class LitterboxAsync:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@ -35,12 +36,12 @@ class LitterboxAsync:
|
|||||||
str
|
str
|
||||||
"""
|
"""
|
||||||
if not fileExt:
|
if not fileExt:
|
||||||
fileExt = 'png'
|
fileExt = "png"
|
||||||
return f"{random.getrandbits(32)}.{fileExt}"
|
return f"{random.getrandbits(32)}.{fileExt}"
|
||||||
|
|
||||||
async def upload(self,
|
async def upload(
|
||||||
file: Union[str, bytes, BufferedReader],
|
self, file: Union[str, bytes, BufferedReader], time="1h"
|
||||||
time='1h') -> Optional[str]:
|
) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Upload File to Litterbox
|
Upload File to Litterbox
|
||||||
|
|
||||||
@ -60,12 +61,12 @@ class LitterboxAsync:
|
|||||||
if isinstance(file, BufferedReader):
|
if isinstance(file, BufferedReader):
|
||||||
file = file.read()
|
file = file.read()
|
||||||
|
|
||||||
fileExt: str = 'png'
|
fileExt: str = "png"
|
||||||
if isinstance(file, str):
|
if isinstance(file, str):
|
||||||
if file.find(".") > 0:
|
if file.find(".") > 0:
|
||||||
fileExt = "".join(file.split(".")[-1:])
|
fileExt = "".join(file.split(".")[-1:])
|
||||||
|
|
||||||
file = open(file, 'rb').read()
|
file = open(file, "rb").read()
|
||||||
|
|
||||||
with magic.Magic(flags=magic.MAGIC_MIME) as m:
|
with magic.Magic(flags=magic.MAGIC_MIME) as m:
|
||||||
if isinstance(file, BufferedReader):
|
if isinstance(file, BufferedReader):
|
||||||
@ -74,23 +75,23 @@ class LitterboxAsync:
|
|||||||
content_type = str(m.id_filename(file))
|
content_type = str(m.id_filename(file))
|
||||||
|
|
||||||
post_data: FormData = FormData()
|
post_data: FormData = FormData()
|
||||||
post_data.add_field(name="reqtype",
|
post_data.add_field(name="reqtype", value="fileupload")
|
||||||
value="fileupload")
|
post_data.add_field(name="userhash", value="")
|
||||||
post_data.add_field(name="userhash",
|
post_data.add_field(name="time", value=time)
|
||||||
value="")
|
|
||||||
post_data.add_field(name="time",
|
|
||||||
value=time)
|
|
||||||
|
|
||||||
post_data.add_field(name="fileToUpload",
|
post_data.add_field(
|
||||||
|
name="fileToUpload",
|
||||||
value=file,
|
value=file,
|
||||||
filename=self.generateRandomFileName(fileExt),
|
filename=self.generateRandomFileName(fileExt),
|
||||||
content_type=content_type
|
content_type=content_type,
|
||||||
)
|
)
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with await session.post(self.api_path,
|
async with await session.post(
|
||||||
|
self.api_path,
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
data=post_data,
|
data=post_data,
|
||||||
timeout=ClientTimeout(connect=5, sock_read=70)) as request:
|
timeout=ClientTimeout(connect=5, sock_read=70),
|
||||||
|
) as request:
|
||||||
request.raise_for_status()
|
request.raise_for_status()
|
||||||
return await request.text()
|
return await request.text()
|
||||||
except:
|
except:
|
||||||
|
@ -4,14 +4,18 @@ from typing import LiteralString, Optional, Union
|
|||||||
import aiosqlite as sqlite3
|
import aiosqlite as sqlite3
|
||||||
from constructors import LoveHateException
|
from constructors import LoveHateException
|
||||||
|
|
||||||
|
|
||||||
class DB:
|
class DB:
|
||||||
"""LoveHate DB Utility Class"""
|
"""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,
|
def __init__(self, bot) -> None:
|
||||||
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>
|
Get a list of users who have professed their love OR hatred for <thing>
|
||||||
|
|
||||||
@ -36,7 +40,10 @@ class DB:
|
|||||||
elif not hates and not loves:
|
elif not hates and not loves:
|
||||||
raise LoveHateException("Neither loves nor hates were requested")
|
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 sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
||||||
async with await db_conn.execute(query, params) as db_cursor:
|
async with await db_conn.execute(query, params) as db_cursor:
|
||||||
result: list[tuple] = await db_cursor.fetchall()
|
result: list[tuple] = await db_cursor.fetchall()
|
||||||
@ -45,8 +52,13 @@ class DB:
|
|||||||
return False
|
return False
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def get_lovehates(self, loves: bool = False, hates: bool = False,
|
async def get_lovehates(
|
||||||
user: Optional[str] = None, thing: Optional[str] = None) -> Union[list[tuple], bool]:
|
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
|
Get a list of either 1) what {user} loves/hates, or who loves/hates {thing}, depending on bools loves, hates
|
||||||
|
|
||||||
@ -64,7 +76,9 @@ class DB:
|
|||||||
params: tuple = tuple()
|
params: tuple = tuple()
|
||||||
|
|
||||||
if not user and not thing:
|
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
|
flag: Optional[int] = None
|
||||||
|
|
||||||
@ -79,10 +93,16 @@ class DB:
|
|||||||
|
|
||||||
if user:
|
if user:
|
||||||
query = "SELECT thing FROM lovehate WHERE display_name LIKE ? AND flag == ?"
|
query = "SELECT thing FROM lovehate WHERE display_name LIKE ? AND flag == ?"
|
||||||
params = (user, flag,)
|
params = (
|
||||||
|
user,
|
||||||
|
flag,
|
||||||
|
)
|
||||||
elif thing:
|
elif thing:
|
||||||
query = "SELECT display_name FROM lovehate WHERE thing LIKE ? AND flag == ?"
|
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 sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
||||||
async with await db_conn.execute(query, params) as db_cursor:
|
async with await db_conn.execute(query, params) as db_cursor:
|
||||||
@ -91,7 +111,6 @@ class DB:
|
|||||||
return False
|
return False
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
async def check_existence(self, user: str, thing: str) -> Optional[int]:
|
async def check_existence(self, user: str, thing: str) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Determine whether a user is opinionated on a <thing>
|
Determine whether a user is opinionated on a <thing>
|
||||||
@ -103,10 +122,16 @@ class DB:
|
|||||||
Optional[int]
|
Optional[int]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
params = (user, thing,)
|
params = (
|
||||||
|
user,
|
||||||
|
thing,
|
||||||
|
)
|
||||||
|
|
||||||
async with sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
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()
|
result = await db_cursor.fetchone()
|
||||||
if not result:
|
if not result:
|
||||||
return None
|
return None
|
||||||
@ -125,20 +150,29 @@ class DB:
|
|||||||
str
|
str
|
||||||
"""
|
"""
|
||||||
if not flag in range(-1, 2):
|
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 = ""
|
db_query: str = ""
|
||||||
params: tuple = (user, thing,)
|
params: tuple = (
|
||||||
|
user,
|
||||||
|
thing,
|
||||||
|
)
|
||||||
|
|
||||||
already_opinionated: Optional[int] = await self.check_existence(user, thing)
|
already_opinionated: Optional[int] = await self.check_existence(user, thing)
|
||||||
if already_opinionated:
|
if already_opinionated:
|
||||||
if flag == 0:
|
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:
|
else:
|
||||||
loves_or_hates: str = "loves"
|
loves_or_hates: str = "loves"
|
||||||
if already_opinionated == -1:
|
if already_opinionated == -1:
|
||||||
loves_or_hates = "hates"
|
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:
|
else:
|
||||||
match flag:
|
match flag:
|
||||||
case -1:
|
case -1:
|
||||||
@ -148,12 +182,13 @@ class DB:
|
|||||||
case _:
|
case _:
|
||||||
raise LoveHateException("Unknown error, default case matched")
|
raise LoveHateException("Unknown error, default case matched")
|
||||||
|
|
||||||
|
|
||||||
async with sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
async with sqlite3.connect(self.db_path, timeout=2) as db_conn:
|
||||||
async with await db_conn.execute(db_query, params) as db_cursor:
|
async with await db_conn.execute(db_query, params) as db_cursor:
|
||||||
await db_conn.commit()
|
await db_conn.commit()
|
||||||
if db_cursor.rowcount != 1:
|
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:
|
match flag:
|
||||||
case -1:
|
case -1:
|
||||||
return f"We're done here, {user} hates {thing}."
|
return f"We're done here, {user} hates {thing}."
|
||||||
@ -162,4 +197,6 @@ class DB:
|
|||||||
case 1:
|
case 1:
|
||||||
return f"We're done here, {user} loves {thing}."
|
return f"We're done here, {user} loves {thing}."
|
||||||
case _:
|
case _:
|
||||||
raise LoveHateException("Unknown error, default case matched [2]")
|
raise LoveHateException(
|
||||||
|
"Unknown error, default case matched [2]"
|
||||||
|
)
|
||||||
|
@ -11,62 +11,117 @@ from aiohttp import ClientSession, ClientTimeout
|
|||||||
from bohancompliment import ComplimentGenerator
|
from bohancompliment import ComplimentGenerator
|
||||||
from discord import Embed
|
from discord import Embed
|
||||||
|
|
||||||
|
|
||||||
class Util:
|
class Util:
|
||||||
"""Misc Utility"""
|
"""Misc Utility"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.URL_URBANDICTIONARY: str = "http://api.urbandictionary.com/v0/define"
|
self.URL_URBANDICTIONARY: str = "http://api.urbandictionary.com/v0/define"
|
||||||
self.URL_INSULTAPI: str = "https://insult.mattbas.org/api/insult"
|
self.URL_INSULTAPI: str = "https://insult.mattbas.org/api/insult"
|
||||||
self.COMPLIMENT_GENERATOR = ComplimentGenerator()
|
self.COMPLIMENT_GENERATOR = ComplimentGenerator()
|
||||||
self.dbs: dict[str, str|LiteralString] = {
|
self.dbs: dict[str, str | LiteralString] = {
|
||||||
'whisky': os.path.join("/usr/local/share",
|
"whisky": os.path.join("/usr/local/share", "sqlite_dbs", "whiskey.db"),
|
||||||
"sqlite_dbs", "whiskey.db"),
|
"drinks": os.path.join("/usr/local/share", "sqlite_dbs", "cocktails.db"),
|
||||||
'drinks': os.path.join("/usr/local/share",
|
"strains": os.path.join("/usr/local/share", "sqlite_dbs", "strains.db"),
|
||||||
"sqlite_dbs", "cocktails.db"),
|
"qajoke": os.path.join("/usr/local/share", "sqlite_dbs", "qajoke.db"),
|
||||||
'strains': os.path.join("/usr/local/share",
|
"rjokes": os.path.join("/usr/local/share", "sqlite_dbs", "rjokes.db"),
|
||||||
"sqlite_dbs", "strains.db"),
|
"randmsg": os.path.join("/usr/local/share", "sqlite_dbs", "randmsg.db"),
|
||||||
'qajoke': os.path.join("/usr/local/share",
|
"stats": os.path.join("/usr/local/share", "sqlite_dbs", "havoc_stats.db"),
|
||||||
"sqlite_dbs", "qajoke.db"),
|
"cookies": os.path.join("/usr/local/share", "sqlite_dbs", "cookies.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',
|
self.COFFEES: list = [
|
||||||
'a cup of Turkish coffee', 'a cup of Moka', 'an espresso',
|
"a cup of french-pressed coffee",
|
||||||
'a cup of Nescafe coffee',
|
"a cup of cold brew",
|
||||||
'an iced coffee', 'a Frappé', 'a freddo cappuccino',
|
"a cup of flash brew",
|
||||||
'a cup of Chock full o\'Nuts', 'a cup of Folgers', 'a cup of Lavazza',
|
"a cup of Turkish coffee",
|
||||||
'a cup of Maxwell House', 'a cup of Moccona', 'a cup of Mr. Brown Coffee',
|
"a cup of Moka",
|
||||||
'a cup of affogato al caffè',
|
"an espresso",
|
||||||
'a cup of Caffè Medici', 'a cup of Café Touba',
|
"a cup of Nescafe coffee",
|
||||||
'a double-double', 'an indian filter coffee', 'a cup of pocillo',
|
"an iced coffee",
|
||||||
'a cup of caffè americano', 'a cup of caffè lungo', 'a latte', 'a manilo',
|
"a Frappé",
|
||||||
'a flat white', 'a cup of café cubano', 'a cup of caffè crema',
|
"a freddo cappuccino",
|
||||||
'a cup of cafe zorro', 'an espresso roberto', 'an espresso romano',
|
"a cup of Chock full o'Nuts",
|
||||||
'an espresso sara', 'a guillermo', 'a ristretto', 'a cup of melya',
|
"a cup of Folgers",
|
||||||
'a cup of caffè marocchino', 'a cup of café miel', 'a cup of café de olla',
|
"a cup of Lavazza",
|
||||||
'a Mazagran', 'a Palazzo', 'an ice shot', 'a macchiato',
|
"a cup of Maxwell House",
|
||||||
'a cortado', 'a red eye', 'a cappuccino',
|
"a cup of Moccona",
|
||||||
'a mocha', 'a café au lait', 'a bicerin',
|
"a cup of Mr. Brown Coffee",
|
||||||
'a caffè corretto', 'a ca phe trung', 'a café bombón', 'a Vienna coffee',
|
"a cup of affogato al caffè",
|
||||||
'a flat black', 'a lungo', 'a doppio', 'a ristretto bianco', 'a piccolo latte',
|
"a cup of Caffè Medici",
|
||||||
'a gibraltar', 'a breve', 'a café con leche', 'a su café', 'a café del tiempo',
|
"a cup of Café Touba",
|
||||||
'a java chip frappuccino', 'a pumpkin spice latte', 'a caramel macchiato',
|
"a double-double",
|
||||||
'a white chocolate mocha', 'a hazelnut coffee', 'a toffee nut latte',
|
"an indian filter coffee",
|
||||||
'a peppermint mocha', 'a cinnamon dolce latte', 'a coconut milk latte',
|
"a cup of pocillo",
|
||||||
'an almond milk cappuccino', 'an oat milk latte', 'a caramel frappuccino',
|
"a cup of caffè americano",
|
||||||
'a chocolate frappuccino', 'a butter pecan coffee', 'a maple pecan latte',
|
"a cup of caffè lungo",
|
||||||
'a sea salt caramel mocha', 'a nitro cold brew', 'a pumpkin cold brew',
|
"a latte",
|
||||||
'a honey almond flat white', 'a sweet cream cold brew', 'a matcha latte',
|
"a manilo",
|
||||||
'a golden latte', 'a turmeric latte', 'a beetroot latte', 'a kopi luwak']
|
"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 = []
|
self.LAST_5_COFFEES: list = []
|
||||||
|
|
||||||
|
def tdTuple(self, td: datetime.timedelta) -> tuple:
|
||||||
def tdTuple(self, td:datetime.timedelta) -> tuple:
|
|
||||||
"""
|
"""
|
||||||
Create TimeDelta Tuple
|
Create TimeDelta Tuple
|
||||||
|
|
||||||
@ -76,11 +131,13 @@ class Util:
|
|||||||
tuple
|
tuple
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _t(t, n):
|
def _t(t, n):
|
||||||
if t < n:
|
if t < n:
|
||||||
return (t, 0)
|
return (t, 0)
|
||||||
v = t//n
|
v = t // n
|
||||||
return (t - (v * n), v)
|
return (t - (v * n), v)
|
||||||
|
|
||||||
(s, h) = _t(td.seconds, 3600)
|
(s, h) = _t(td.seconds, 3600)
|
||||||
(s, m) = _t(s, 60)
|
(s, m) = _t(s, 60)
|
||||||
(mics, mils) = _t(td.microseconds, 1000)
|
(mics, mils) = _t(td.microseconds, 1000)
|
||||||
@ -96,16 +153,17 @@ class Util:
|
|||||||
Optional[dict]
|
Optional[dict]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
stats_db: str|LiteralString = self.dbs.get('stats', '')
|
stats_db: str | LiteralString = self.dbs.get("stats", "")
|
||||||
if not stats_db:
|
if not stats_db:
|
||||||
return None
|
return None
|
||||||
async with sqlite3.connect(stats_db,
|
async with sqlite3.connect(stats_db, timeout=3) as db_conn:
|
||||||
timeout=3) as db_conn:
|
|
||||||
db_conn.row_factory = sqlite3.Row
|
db_conn.row_factory = sqlite3.Row
|
||||||
query: str = "SELECT ? FROM stats LIMIT 1"
|
query: str = "SELECT ? FROM stats LIMIT 1"
|
||||||
if not counter:
|
if not counter:
|
||||||
query = "SELECT * FROM stats LIMIT 1"
|
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()
|
result = await db_cursor.fetchone()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -122,11 +180,11 @@ class Util:
|
|||||||
return None
|
return None
|
||||||
embed: Embed = Embed(title="Stats")
|
embed: Embed = Embed(title="Stats")
|
||||||
counter_message: str = ""
|
counter_message: str = ""
|
||||||
counters_sorted: dict = dict(sorted(counters.items(),
|
counters_sorted: dict = dict(
|
||||||
key=lambda item: item[1], reverse=True))
|
sorted(counters.items(), key=lambda item: item[1], reverse=True)
|
||||||
|
)
|
||||||
for counter, value in counters_sorted.items():
|
for counter, value in counters_sorted.items():
|
||||||
counter = regex.sub(r'_', ' ',
|
counter = regex.sub(r"_", " ", counter.strip()).title()
|
||||||
counter.strip()).title()
|
|
||||||
counter_message += f"- {value} {counter}\n"
|
counter_message += f"- {value} {counter}\n"
|
||||||
embed.description = counter_message.strip()
|
embed.description = counter_message.strip()
|
||||||
return embed
|
return embed
|
||||||
@ -141,14 +199,17 @@ class Util:
|
|||||||
bool
|
bool
|
||||||
|
|
||||||
"""
|
"""
|
||||||
stats_db: str|LiteralString = self.dbs.get('stats', '')
|
stats_db: str | LiteralString = self.dbs.get("stats", "")
|
||||||
if not stats_db:
|
if not stats_db:
|
||||||
return False
|
return False
|
||||||
async with sqlite3.connect(stats_db,
|
async with sqlite3.connect(stats_db, timeout=3) as db_conn:
|
||||||
timeout=3) as db_conn:
|
async with await db_conn.execute(
|
||||||
async with await db_conn.execute(f"UPDATE stats SET {counter} = {counter} + 1") as db_cursor:
|
f"UPDATE stats SET {counter} = {counter} + 1"
|
||||||
|
) as db_cursor:
|
||||||
if db_cursor.rowcount < 0:
|
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
|
return False
|
||||||
await db_conn.commit()
|
await db_conn.commit()
|
||||||
return True
|
return True
|
||||||
@ -165,23 +226,30 @@ class Util:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with await session.get(self.URL_URBANDICTIONARY,
|
async with await session.get(
|
||||||
|
self.URL_URBANDICTIONARY,
|
||||||
params={
|
params={
|
||||||
"term": term,
|
"term": term,
|
||||||
},
|
},
|
||||||
headers = {
|
headers={
|
||||||
'content-type': 'application/json; charset=utf-8',
|
"content-type": "application/json; charset=utf-8",
|
||||||
}, timeout=ClientTimeout(connect=5, sock_read=5)) as request:
|
},
|
||||||
logging.debug("UD returned: %s",
|
timeout=ClientTimeout(connect=5, sock_read=5),
|
||||||
await request.text())
|
) as request:
|
||||||
|
logging.debug("UD returned: %s", await request.text())
|
||||||
data: dict = await request.json()
|
data: dict = await request.json()
|
||||||
if "list" in data:
|
if "list" in data:
|
||||||
definitions: list[dict] = data["list"]
|
definitions: list[dict] = data["list"]
|
||||||
if definitions:
|
if definitions:
|
||||||
definition: dict = definitions[0]
|
definition: dict = definitions[0]
|
||||||
definition_word: str = definition.get("word", "N/A")
|
definition_word: str = definition.get("word", "N/A")
|
||||||
definition_text: str = regex.sub(r'(\r|\n|\r\n)', ' ', definition["definition"].strip())
|
definition_text: str = regex.sub(
|
||||||
return (definition_word, definition_text) # Tuple: Returned word, returned definition
|
r"(\r|\n|\r\n)", " ", definition["definition"].strip()
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
definition_word,
|
||||||
|
definition_text,
|
||||||
|
) # Tuple: Returned word, returned definition
|
||||||
else:
|
else:
|
||||||
return (term, "Not found!")
|
return (term, "Not found!")
|
||||||
else:
|
else:
|
||||||
@ -201,13 +269,13 @@ class Util:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
async with ClientSession() as session:
|
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()
|
request.raise_for_status()
|
||||||
return await request.text()
|
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
|
Get Compliment
|
||||||
|
|
||||||
@ -228,26 +296,35 @@ class Util:
|
|||||||
Optional[tuple]
|
Optional[tuple]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
whisky_db: str|LiteralString = self.dbs.get('whisky', '')
|
whisky_db: str | LiteralString = self.dbs.get("whisky", "")
|
||||||
if not whisky_db:
|
if not whisky_db:
|
||||||
return None
|
return None
|
||||||
async with sqlite3.connect(database=whisky_db,
|
async with sqlite3.connect(database=whisky_db, timeout=2) as db_conn:
|
||||||
timeout=2) as db_conn:
|
db_query: str = (
|
||||||
db_query: str = "SELECT name, category, description FROM whiskeys ORDER BY random() LIMIT 1"
|
"SELECT name, category, description FROM whiskeys ORDER BY random() LIMIT 1"
|
||||||
|
)
|
||||||
async with await db_conn.execute(db_query) as db_cursor:
|
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:
|
if not db_result:
|
||||||
return None
|
return None
|
||||||
(name, category, description) = db_result
|
(name, category, description) = db_result
|
||||||
name = regex.sub(r'(^\p{White_Space}|\r|\n)', '',
|
name = regex.sub(
|
||||||
regex.sub(r'\p{White_Space}{2,}', ' ',
|
r"(^\p{White_Space}|\r|\n)",
|
||||||
name.strip()))
|
"",
|
||||||
category = regex.sub(r'(^\p{White_Space}|\r|\n)', '',
|
regex.sub(r"\p{White_Space}{2,}", " ", name.strip()),
|
||||||
regex.sub(r'\p{White_Space}{2,}', ' ',
|
)
|
||||||
category.strip()))
|
category = regex.sub(
|
||||||
description = regex.sub(r'(^\p{White_Space}|\r|\n)', '',
|
r"(^\p{White_Space}|\r|\n)",
|
||||||
regex.sub(r'\p{White_Space}{2,}', ' ',
|
"",
|
||||||
description.strip()))
|
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)
|
return (name, category, description)
|
||||||
|
|
||||||
async def get_drink(self) -> Optional[tuple]:
|
async def get_drink(self) -> Optional[tuple]:
|
||||||
@ -258,19 +335,28 @@ class Util:
|
|||||||
Optional[tuple]
|
Optional[tuple]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
drinks_db: str|LiteralString = self.dbs.get('drinks', '')
|
drinks_db: str | LiteralString = self.dbs.get("drinks", "")
|
||||||
if not drinks_db:
|
if not drinks_db:
|
||||||
return None
|
return None
|
||||||
async with sqlite3.connect(database=drinks_db,
|
async with sqlite3.connect(database=drinks_db, timeout=2) as db_conn:
|
||||||
timeout=2) as db_conn:
|
db_query: str = (
|
||||||
db_query: str = "SELECT name, ingredients FROM cocktails ORDER BY random() LIMIT 1"
|
"SELECT name, ingredients FROM cocktails ORDER BY random() LIMIT 1"
|
||||||
|
)
|
||||||
async with await db_conn.execute(db_query) as db_cursor:
|
async with await db_conn.execute(db_query) as db_cursor:
|
||||||
db_result: tuple = await db_cursor.fetchone()
|
db_result: tuple = await db_cursor.fetchone()
|
||||||
|
|
||||||
(name, ingredients) = db_result
|
(name, ingredients) = db_result
|
||||||
name = regex.sub(r'(^\p{White_Space}|\r|\n)', '', regex.sub(r'\p{White_Space}{2,}', ' ', name.strip()))
|
name = regex.sub(
|
||||||
ingredients = regex.sub(r'(^\p{White_Space}|\r|\n)', '', regex.sub(r'\p{White_Space}{2,}', ' ', ingredients.strip()))
|
r"(^\p{White_Space}|\r|\n)",
|
||||||
ingredients = regex.sub(r'\*', '\u2731', ingredients.strip())
|
"",
|
||||||
|
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)
|
return (name, ingredients)
|
||||||
|
|
||||||
async def get_strain(self, strain: Optional[str] = None) -> Optional[tuple]:
|
async def get_strain(self, strain: Optional[str] = None) -> Optional[tuple]:
|
||||||
@ -283,16 +369,19 @@ class Util:
|
|||||||
Optional[tuple]
|
Optional[tuple]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
strains_db: str|LiteralString = self.dbs.get('strains', '')
|
strains_db: str | LiteralString = self.dbs.get("strains", "")
|
||||||
if not strains_db:
|
if not strains_db:
|
||||||
return None
|
return None
|
||||||
async with sqlite3.connect(database=strains_db,
|
async with sqlite3.connect(database=strains_db, timeout=2) as db_conn:
|
||||||
timeout=2) as db_conn:
|
|
||||||
db_params: Optional[tuple] = None
|
db_params: Optional[tuple] = None
|
||||||
if not strain:
|
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:
|
else:
|
||||||
db_query = "SELECT name, description FROM strains_w_desc WHERE name LIKE ?"
|
db_query = (
|
||||||
|
"SELECT name, description FROM strains_w_desc WHERE name LIKE ?"
|
||||||
|
)
|
||||||
db_params = (f"%{strain.strip()}%",)
|
db_params = (f"%{strain.strip()}%",)
|
||||||
async with await db_conn.execute(db_query, db_params) as db_cursor:
|
async with await db_conn.execute(db_query, db_params) as db_cursor:
|
||||||
db_result: Optional[tuple] = await db_cursor.fetchone()
|
db_result: Optional[tuple] = await db_cursor.fetchone()
|
||||||
@ -306,12 +395,13 @@ class Util:
|
|||||||
Optional[tuple]
|
Optional[tuple]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
qajoke_db: str|LiteralString = self.dbs.get('qajoke', '')
|
qajoke_db: str | LiteralString = self.dbs.get("qajoke", "")
|
||||||
if not qajoke_db:
|
if not qajoke_db:
|
||||||
return None
|
return None
|
||||||
async with sqlite3.connect(database=qajoke_db,
|
async with sqlite3.connect(database=qajoke_db, timeout=2) as db_conn:
|
||||||
timeout=2) as db_conn:
|
db_query: str = (
|
||||||
db_query: str = "SELECT question, answer FROM jokes ORDER BY RANDOM() LIMIT 1"
|
"SELECT question, answer FROM jokes ORDER BY RANDOM() LIMIT 1"
|
||||||
|
)
|
||||||
async with await db_conn.execute(db_query) as cursor:
|
async with await db_conn.execute(db_query) as cursor:
|
||||||
(question, answer) = await cursor.fetchone()
|
(question, answer) = await cursor.fetchone()
|
||||||
return (question, answer)
|
return (question, answer)
|
||||||
@ -325,17 +415,18 @@ class Util:
|
|||||||
Optional[tuple]
|
Optional[tuple]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
rjokes_db: str|LiteralString = self.dbs.get('rjokes', '')
|
rjokes_db: str | LiteralString = self.dbs.get("rjokes", "")
|
||||||
if not rjokes_db:
|
if not rjokes_db:
|
||||||
return None
|
return None
|
||||||
async with sqlite3.connect(database=rjokes_db, timeout=2) as db_conn:
|
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:
|
async with await db_conn.execute(db_query) as cursor:
|
||||||
(title, body, score) = await cursor.fetchone()
|
(title, body, score) = await cursor.fetchone()
|
||||||
return (title, body, score)
|
return (title, body, score)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def get_random_fact(self) -> str:
|
async def get_random_fact(self) -> str:
|
||||||
"""
|
"""
|
||||||
Get Random Fact
|
Get Random Fact
|
||||||
@ -346,21 +437,25 @@ class Util:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
facts_api_url: str = "https://uselessfacts.jsph.pl/api/v2/facts/random"
|
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:
|
async with ClientSession() as client:
|
||||||
try:
|
try:
|
||||||
async with await client.get(facts_api_url,
|
async with await client.get(
|
||||||
timeout=ClientTimeout(connect=5, sock_read=5)) as request:
|
facts_api_url, timeout=ClientTimeout(connect=5, sock_read=5)
|
||||||
|
) as request:
|
||||||
_json: dict = await request.json()
|
_json: dict = await request.json()
|
||||||
fact: str = _json.get('text', None)
|
fact: str = _json.get("text", None)
|
||||||
if not fact:
|
if not fact:
|
||||||
raise BaseException("RandFact Src 1 Failed")
|
raise BaseException("RandFact Src 1 Failed")
|
||||||
return fact
|
return fact
|
||||||
except:
|
except:
|
||||||
async with await client.get(facts_backup_url,
|
async with await client.get(
|
||||||
timeout=ClientTimeout(connect=5, sock_read=5)) as request:
|
facts_backup_url, timeout=ClientTimeout(connect=5, sock_read=5)
|
||||||
|
) as request:
|
||||||
_json = await request.json()
|
_json = await request.json()
|
||||||
fact = _json.get('fact', None)
|
fact = _json.get("fact", None)
|
||||||
return fact
|
return fact
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@ -374,23 +469,18 @@ class Util:
|
|||||||
Optional[dict]
|
Optional[dict]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cookies_db = self.dbs.get('cookies', '')
|
cookies_db = self.dbs.get("cookies", "")
|
||||||
if not cookies_db:
|
if not cookies_db:
|
||||||
return None
|
return None
|
||||||
async with sqlite3.connect(cookies_db,
|
async with sqlite3.connect(cookies_db, timeout=2) as db_conn:
|
||||||
timeout=2) as db_conn:
|
db_query: str = (
|
||||||
db_query: str = "SELECT name, origin, image_url FROM cookies ORDER BY RANDOM() LIMIT 1"
|
"SELECT name, origin, image_url FROM cookies ORDER BY RANDOM() LIMIT 1"
|
||||||
|
)
|
||||||
async with await db_conn.execute(db_query) as db_cursor:
|
async with await db_conn.execute(db_query) as db_cursor:
|
||||||
(name, origin, image_url) = await db_cursor.fetchone()
|
(name, origin, image_url) = await db_cursor.fetchone()
|
||||||
return {
|
return {"name": name, "origin": origin, "image_url": image_url}
|
||||||
'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
|
Get Coffee
|
||||||
|
|
||||||
@ -403,8 +493,11 @@ class Util:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
randomCoffee: str = random.choice(self.COFFEES)
|
randomCoffee: str = random.choice(self.COFFEES)
|
||||||
if self.LAST_5_COFFEES and randomCoffee in self.LAST_5_COFFEES\
|
if (
|
||||||
or (recipient_allergic and "nut" in randomCoffee.lower()):
|
self.LAST_5_COFFEES
|
||||||
|
and randomCoffee in self.LAST_5_COFFEES
|
||||||
|
or (recipient_allergic and "nut" in randomCoffee.lower())
|
||||||
|
):
|
||||||
return self.get_coffee() # Recurse
|
return self.get_coffee() # Recurse
|
||||||
if len(self.LAST_5_COFFEES) >= 5:
|
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
|
||||||
@ -429,7 +522,7 @@ class Util:
|
|||||||
day=25,
|
day=25,
|
||||||
tzinfo=pytz.UTC,
|
tzinfo=pytz.UTC,
|
||||||
)
|
)
|
||||||
td: datetime.timedelta = (xmas - today)
|
td: datetime.timedelta = xmas - today
|
||||||
days, hours, minutes, seconds, us, ms = self.tdTuple(td)
|
days, hours, minutes, seconds, us, ms = self.tdTuple(td)
|
||||||
|
|
||||||
return (days, hours, minutes, seconds, ms, us)
|
return (days, hours, minutes, seconds, ms, us)
|
||||||
@ -442,11 +535,10 @@ class Util:
|
|||||||
Optional[str]
|
Optional[str]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
randmsg_db: str|LiteralString = self.dbs.get('randmsg', '')
|
randmsg_db: str | LiteralString = self.dbs.get("randmsg", "")
|
||||||
if not randmsg_db:
|
if not randmsg_db:
|
||||||
return None
|
return None
|
||||||
async with sqlite3.connect(database=randmsg_db,
|
async with sqlite3.connect(database=randmsg_db, timeout=2) as db_conn:
|
||||||
timeout=2) as db_conn:
|
|
||||||
db_query: str = "SELECT msg FROM msgs ORDER BY RANDOM() LIMIT 1"
|
db_query: str = "SELECT msg FROM msgs ORDER BY RANDOM() LIMIT 1"
|
||||||
async with await db_conn.execute(db_query) as db_cursor:
|
async with await db_conn.execute(db_query) as db_cursor:
|
||||||
(result,) = await db_cursor.fetchone()
|
(result,) = await db_cursor.fetchone()
|
||||||
|
@ -4,6 +4,7 @@ from aiohttp import ClientSession, ClientTimeout
|
|||||||
|
|
||||||
"""Radio Utils"""
|
"""Radio Utils"""
|
||||||
|
|
||||||
|
|
||||||
async def get_now_playing() -> Optional[str]:
|
async def get_now_playing() -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Get radio now playing
|
Get radio now playing
|
||||||
@ -15,18 +16,22 @@ async def get_now_playing() -> Optional[str]:
|
|||||||
np_url: str = "https://api.codey.lol/radio/np"
|
np_url: str = "https://api.codey.lol/radio/np"
|
||||||
try:
|
try:
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with await session.post(np_url, headers={
|
async with await session.post(
|
||||||
'content-type': 'application/json; charset=utf-8',
|
np_url,
|
||||||
}, timeout=ClientTimeout(connect=1.5, sock_read=1.5)) as request:
|
headers={
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
},
|
||||||
|
timeout=ClientTimeout(connect=1.5, sock_read=1.5),
|
||||||
|
) as request:
|
||||||
request.raise_for_status()
|
request.raise_for_status()
|
||||||
response_json = await request.json()
|
response_json = await request.json()
|
||||||
artistsong = response_json.get('artistsong')
|
artistsong = response_json.get("artistsong")
|
||||||
return artistsong
|
return artistsong
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.critical("Now playing retrieval failed: %s",
|
logging.critical("Now playing retrieval failed: %s", str(e))
|
||||||
str(e))
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def skip() -> bool:
|
async def skip() -> bool:
|
||||||
"""
|
"""
|
||||||
Ask LiquidSoap server to skip to the next track
|
Ask LiquidSoap server to skip to the next track
|
||||||
@ -38,8 +43,9 @@ async def skip() -> bool:
|
|||||||
try:
|
try:
|
||||||
ls_uri: str = "http://127.0.0.1:29000"
|
ls_uri: str = "http://127.0.0.1:29000"
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with session.get(f"{ls_uri}/next",
|
async with session.get(
|
||||||
timeout=ClientTimeout(connect=2, sock_read=2)) as request:
|
f"{ls_uri}/next", timeout=ClientTimeout(connect=2, sock_read=2)
|
||||||
|
) as request:
|
||||||
request.raise_for_status()
|
request.raise_for_status()
|
||||||
text: Optional[str] = await request.text()
|
text: Optional[str] = await request.text()
|
||||||
return text == "OK"
|
return text == "OK"
|
||||||
@ -47,4 +53,3 @@ async def skip() -> bool:
|
|||||||
logging.debug("Skip failed: %s", str(e))
|
logging.debug("Skip failed: %s", str(e))
|
||||||
|
|
||||||
return False # failsafe
|
return False # failsafe
|
||||||
|
|
||||||
|
@ -6,14 +6,17 @@ import traceback
|
|||||||
from discord import Activity
|
from discord import Activity
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
|
||||||
class Utility:
|
class Utility:
|
||||||
"""Sing Utility"""
|
"""Sing Utility"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.api_url: str = "http://127.0.0.1:52111/lyric/search"
|
self.api_url: str = "http://127.0.0.1:52111/lyric/search"
|
||||||
self.api_src: str = "DISC-HAVOC"
|
self.api_src: str = "DISC-HAVOC"
|
||||||
|
|
||||||
def parse_song_input(self, song: Optional[str] = None,
|
def parse_song_input(
|
||||||
activity: Optional[Activity] = None) -> Union[bool, tuple]:
|
self, song: Optional[str] = None, activity: Optional[Activity] = None
|
||||||
|
) -> Union[bool, tuple]:
|
||||||
"""
|
"""
|
||||||
Parse Song (Sing Command) Input
|
Parse Song (Sing Command) Input
|
||||||
|
|
||||||
@ -32,10 +35,15 @@ class Utility:
|
|||||||
return False # No valid activity found
|
return False # No valid activity found
|
||||||
match activity.name.lower():
|
match activity.name.lower():
|
||||||
case "codey toons" | "cider" | "sonixd":
|
case "codey toons" | "cider" | "sonixd":
|
||||||
search_artist: str = " ".join(str(activity.state)\
|
search_artist: str = " ".join(
|
||||||
.strip().split(" ")[1:])
|
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 = regex.sub(
|
||||||
|
r"(\s{0,})(\[(spotify|tidal|sonixd|browser|yt music)])$",
|
||||||
|
"",
|
||||||
|
search_artist.strip(),
|
||||||
|
flags=regex.IGNORECASE,
|
||||||
|
)
|
||||||
search_song = str(activity.details)
|
search_song = str(activity.details)
|
||||||
song = f"{search_artist} : {search_song}"
|
song = f"{search_artist} : {search_song}"
|
||||||
case "tidal hi-fi":
|
case "tidal hi-fi":
|
||||||
@ -55,29 +63,39 @@ class Utility:
|
|||||||
if not activity.details:
|
if not activity.details:
|
||||||
song = str(activity.state)
|
song = str(activity.state)
|
||||||
else:
|
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)
|
search_song = str(activity.details)
|
||||||
song = f"{search_artist} : {search_song}"
|
song = f"{search_artist} : {search_song}"
|
||||||
case _:
|
case _:
|
||||||
return False # Unsupported activity detected
|
return False # Unsupported activity detected
|
||||||
|
|
||||||
search_split_by: str = ":" if not(song) or len(song.split(":")) > 1\
|
search_split_by: str = (
|
||||||
else "-" # Support either : or - to separate artist/track
|
":" if not (song) or len(song.split(":")) > 1 else "-"
|
||||||
|
) # Support either : or - to separate artist/track
|
||||||
if not song:
|
if not song:
|
||||||
return False
|
return False
|
||||||
search_artist = song.split(search_split_by)[0].strip()
|
search_artist = song.split(search_split_by)[0].strip()
|
||||||
search_song = "".join(song.split(search_split_by)[1:]).strip()
|
search_song = "".join(song.split(search_split_by)[1:]).strip()
|
||||||
search_subsearch: Optional[str] = None
|
search_subsearch: Optional[str] = None
|
||||||
if search_split_by == ":" and len(song.split(":")) > 2: # Support sub-search if : is used (per instructions)
|
if (
|
||||||
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_split_by == ":" and len(song.split(":")) > 2
|
||||||
search_subsearch = "".join(song.split(search_split_by)[2:]) # Lyric text from split index 2 and beyond
|
): # 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)
|
return (search_artist, search_song, search_subsearch)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def lyric_search(self, artist: str, song: str,
|
async def lyric_search(
|
||||||
sub: Optional[str] = None) -> Optional[list]:
|
self, artist: str, song: str, sub: Optional[str] = None
|
||||||
|
) -> Optional[list]:
|
||||||
"""
|
"""
|
||||||
Lyric Search
|
Lyric Search
|
||||||
|
|
||||||
@ -93,59 +111,75 @@ class Utility:
|
|||||||
return [("FAIL! Artist/Song not provided",)]
|
return [("FAIL! Artist/Song not provided",)]
|
||||||
|
|
||||||
search_obj: dict = {
|
search_obj: dict = {
|
||||||
'a': artist.strip(),
|
"a": artist.strip(),
|
||||||
's': song.strip(),
|
"s": song.strip(),
|
||||||
'extra': True,
|
"extra": True,
|
||||||
'src': self.api_src,
|
"src": self.api_src,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(song.strip()) < 1:
|
if len(song.strip()) < 1:
|
||||||
search_obj.pop('a')
|
search_obj.pop("a")
|
||||||
search_obj.pop('s')
|
search_obj.pop("s")
|
||||||
search_obj['t'] = artist.strip() # Parse failed, try title without sep
|
search_obj["t"] = artist.strip() # Parse failed, try title without sep
|
||||||
|
|
||||||
if sub and len(sub) >= 2:
|
if sub and len(sub) >= 2:
|
||||||
search_obj['sub'] = sub.strip()
|
search_obj["sub"] = sub.strip()
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with await session.post(self.api_url,
|
async with await session.post(
|
||||||
|
self.api_url,
|
||||||
json=search_obj,
|
json=search_obj,
|
||||||
timeout=aiohttp.ClientTimeout(connect=5, sock_read=10)) as request:
|
timeout=aiohttp.ClientTimeout(connect=5, sock_read=10),
|
||||||
|
) as request:
|
||||||
request.raise_for_status()
|
request.raise_for_status()
|
||||||
response: dict = await request.json()
|
response: dict = await request.json()
|
||||||
if response.get('err'):
|
if response.get("err"):
|
||||||
return [(f"ERR: {response.get('errorText')}",)]
|
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 = {
|
response_obj: dict = {
|
||||||
'artist': response.get('artist'),
|
"artist": response.get("artist"),
|
||||||
'song': response.get('song'),
|
"song": response.get("song"),
|
||||||
'lyrics': out_lyrics,
|
"lyrics": out_lyrics,
|
||||||
'src': response.get('src'),
|
"src": response.get("src"),
|
||||||
'confidence': float(response.get('confidence', 0.0)),
|
"confidence": float(response.get("confidence", 0.0)),
|
||||||
'time': float(response.get('time', -1.0)),
|
"time": float(response.get("time", -1.0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
lyrics = response_obj.get('lyrics')
|
lyrics = response_obj.get("lyrics")
|
||||||
if not lyrics:
|
if not lyrics:
|
||||||
return None
|
return None
|
||||||
response_obj['lyrics'] = textwrap.wrap(text=lyrics.strip(),
|
response_obj["lyrics"] = textwrap.wrap(
|
||||||
width=1500, drop_whitespace=False,
|
text=lyrics.strip(),
|
||||||
replace_whitespace=False, break_long_words=True,
|
width=1500,
|
||||||
break_on_hyphens=True, max_lines=8)
|
drop_whitespace=False,
|
||||||
response_obj['lyrics_short'] = textwrap.wrap(text=lyrics.strip(),
|
replace_whitespace=False,
|
||||||
width=750, drop_whitespace=False,
|
break_long_words=True,
|
||||||
replace_whitespace=False, break_long_words=True,
|
break_on_hyphens=True,
|
||||||
break_on_hyphens=True, max_lines=1)
|
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 [
|
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"{int(response_obj.get('confidence', -1.0))}%",
|
||||||
f"{response_obj.get('time', -666.0):.4f}s",
|
f"{response_obj.get('time', -666.0):.4f}s",
|
||||||
),
|
),
|
||||||
response_obj.get('lyrics'),
|
response_obj.get("lyrics"),
|
||||||
response_obj.get('lyrics_short'),
|
response_obj.get("lyrics_short"),
|
||||||
]
|
]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user