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