meme dupe snitching/misc

This commit is contained in:
codey 2025-05-15 15:49:28 -04:00
parent 6a1fd659e8
commit 3dac803305
8 changed files with 105 additions and 55 deletions

View File

@ -100,7 +100,7 @@ class Util:
top_formatted: str = "" top_formatted: str = ""
for x, item in enumerate(top): for x, item in enumerate(top):
top_formatted += ( top_formatted += (
f"{x+1}. **{discord.utils.escape_markdown(item[0])}**: *{item[1]}*\n" f"{x + 1}. **{discord.utils.escape_markdown(item[0])}**: *{item[1]}*\n"
) )
top_formatted = top_formatted.strip() top_formatted = top_formatted.strip()
embed: discord.Embed = discord.Embed( embed: discord.Embed = discord.Embed(

View File

@ -4,6 +4,9 @@ import json
import io import io
import asyncio import asyncio
import random import random
import copy
from PIL import Image, UnidentifiedImageError
import imagehash
from typing import LiteralString, Optional, Any, Union from typing import LiteralString, Optional, Any, Union
import aiosqlite as sqlite3 import aiosqlite as sqlite3
import logging import logging
@ -132,9 +135,12 @@ class Meme(commands.Cog):
def __init__(self, bot: Havoc) -> None: def __init__(self, bot: Havoc) -> None:
self.bot: Havoc = bot self.bot: Havoc = bot
self.stats_db_path: LiteralString = os.path.join( self.stats_db_path: str = os.path.join(
"/usr/local/share", "sqlite_dbs", "stats.db" "/usr/local/share", "sqlite_dbs", "stats.db"
) )
self.memedb_path: str = os.path.join(
"/usr/local/share", "sqlite_dbs", "meme.db"
)
self.meme_choices: list = [] self.meme_choices: list = []
self.meme_counter: int = 0 self.meme_counter: int = 0
self.THREADS: dict[str, dict[int, list]] = { self.THREADS: dict[str, dict[int, list]] = {
@ -243,6 +249,45 @@ class Meme(commands.Cog):
count = result["count"] count = result["count"]
self.meme_leaderboard[uid] = count self.meme_leaderboard[uid] = count
async def insert_meme(
self, discord_uid: int, timestamp: int, message_id: int, image_url: str
) -> Optional[bool]:
"""
INSERT MEME -> SQLITE DB
"""
try:
_image: io.BytesIO = io.BytesIO(
requests.get(image_url, stream=True, timeout=20).raw.read()
)
image_copy = copy.deepcopy(_image)
image = Image.open(image_copy)
except UnidentifiedImageError:
return None
phash: str = str(imagehash.phash(image))
query: str = "INSERT INTO memes(discord_uid, timestamp, image, message_ids, phash) VALUES(?, ?, ?, ?, ?)"
async with sqlite3.connect(self.memedb_path, timeout=2) as db_conn:
insert = await db_conn.execute_insert(
query, (discord_uid, timestamp, _image.read(), message_id, phash)
)
if insert:
await db_conn.commit()
return True
return None
async def dupe_check(self, image) -> bool | int:
"""
CHECK DB FOR DUPLICATE MEMES!
"""
phash: str = str(imagehash.phash(image))
query: str = "SELECT message_ids FROM memes WHERE phash = ? LIMIT 1"
async with sqlite3.connect(self.memedb_path, timeout=2) as db_conn:
db_conn.row_factory = sqlite3.Row
async with await db_conn.execute(query, (phash,)) as db_cursor:
result = await db_cursor.fetchone()
if result:
return result["message_ids"]
return False
@commands.Cog.listener() @commands.Cog.listener()
async def on_ready(self) -> None: async def on_ready(self) -> None:
"""Run on Bot Ready""" """Run on Bot Ready"""
@ -644,6 +689,7 @@ class Meme(commands.Cog):
Also monitors for messages to #memes-top-10 to autodelete, only Havoc may post in #memes-top-10! Also monitors for messages to #memes-top-10 to autodelete, only Havoc may post in #memes-top-10!
""" """
lb_chanid: int = 1352373745108652145 lb_chanid: int = 1352373745108652145
meme_chanid: int = 1147229098544988261
if not self.bot.user: # No valid client instance if not self.bot.user: # No valid client instance
return return
if not isinstance(message.channel, discord.TextChannel): if not isinstance(message.channel, discord.TextChannel):
@ -666,12 +712,37 @@ class Meme(commands.Cog):
return return
if not message.guild: if not message.guild:
return return
if not message.channel.id == 1147229098544988261: # Not meme channel if message.channel.id not in [
1157529874936909934,
meme_chanid,
]: # Not meme channel
return return
if not message.attachments: # No attachments to consider a meme if not message.attachments: # No attachments to consider a meme
return return
await self.leaderboard_increment(message.author.id) unique_memes: list = []
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()
)
dupe_check = await self.dupe_check(Image.open(image))
if dupe_check:
channel = message.channel
original_message = await channel.fetch_message(dupe_check) # type: ignore
original_message_url = original_message.jump_url
await message.add_reaction(
emoji="<:quietscheentchen:1255956612804247635>"
)
await message.reply(original_message_url)
else:
unique_memes.append(item.url)
if unique_memes:
await self.leaderboard_increment(message.author.id)
for meme_url in unique_memes:
author_id: int = message.author.id
timestamp: int = int(message.created_at.timestamp())
await self.insert_meme(author_id, timestamp, message.id, meme_url)
async def get_top(self, n: int = 10) -> Optional[list[tuple]]: async def get_top(self, n: int = 10) -> Optional[list[tuple]]:
""" """
@ -686,9 +757,7 @@ class Meme(commands.Cog):
out_top: list[tuple[int, int]] = [] out_top: list[tuple[int, int]] = []
async with sqlite3.connect(self.stats_db_path, timeout=2) as db_conn: async with sqlite3.connect(self.stats_db_path, timeout=2) as db_conn:
db_conn.row_factory = sqlite3.Row db_conn.row_factory = sqlite3.Row
query: str = ( query: str = "SELECT discord_uid, count FROM memes WHERE count > 0 ORDER BY count DESC"
"SELECT discord_uid, count FROM memes WHERE count > 0 ORDER BY count DESC"
)
async with db_conn.execute(query) as db_cursor: async with db_conn.execute(query) as db_cursor:
db_result = await db_cursor.fetchall() db_result = await db_cursor.fetchall()
for res in db_result: for res in db_result:
@ -734,9 +803,7 @@ class Meme(commands.Cog):
if not member: if not member:
continue continue
display_name: str = member.display_name display_name: str = member.display_name
top_formatted += ( top_formatted += f"{x + 1}. **{discord.utils.escape_markdown(display_name)}**: *{count}*\n"
f"{x+1}. **{discord.utils.escape_markdown(display_name)}**: *{count}*\n"
)
top_formatted = top_formatted.strip() top_formatted = top_formatted.strip()
embed: discord.Embed = discord.Embed( embed: discord.Embed = discord.Embed(
title=f"Top {n} Memes", description=top_formatted, colour=0x25BD6B title=f"Top {n} Memes", description=top_formatted, colour=0x25BD6B

View File

@ -215,9 +215,7 @@ class Misc(commands.Cog):
logging.debug("Failed to add xmas reaction: %s", str(e)) logging.debug("Failed to add xmas reaction: %s", str(e))
await ctx.respond( await ctx.respond(
f"Only {days} days, {hours} hours, {minutes} minutes,\ f"Only {days} days, {hours} hours, {minutes} minutes,\
{seconds} seconds and {ms} ms left! (UTC)".translate( {seconds} seconds and {ms} ms left! (UTC)".translate(xmas_trans)
xmas_trans
)
) )
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
@ -1292,7 +1290,7 @@ class Misc(commands.Cog):
Satan(!?) Satan(!?)
""" """
out_msg: str = ( out_msg: str = (
"## There is no Satan\n" "### He isn't real.\n" "-# And neither are you." "## There is no Satan\n### He isn't real.\n-# And neither are you."
) )
return await ctx.respond(out_msg) return await ctx.respond(out_msg)
@ -1403,7 +1401,7 @@ class Misc(commands.Cog):
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
return await ctx.respond(f"Error: {str(e)}") return await ctx.respond(f"Error: {str(e)}")
@bridge.bridge_command() @bridge.bridge_command()
async def no(self, ctx) -> None: async def no(self, ctx) -> None:
"""No (As A Service)""" """No (As A Service)"""

View File

@ -94,9 +94,7 @@ class Sing(commands.Cog):
search_result_src, search_result_src,
search_result_confidence, search_result_confidence,
search_result_time_taken, search_result_time_taken,
) = search_result[ ) = search_result[0] # First index is a tuple
0
] # First index is a tuple
search_result_wrapped: list[str] = search_result[ search_result_wrapped: list[str] = search_result[
1 1
] # Second index is the wrapped lyrics ] # Second index is the wrapped lyrics
@ -124,9 +122,7 @@ class Sing(commands.Cog):
# if ctx.guild.id == 1145182936002482196: # if ctx.guild.id == 1145182936002482196:
# section = section.upper() # section = section.upper()
section = regex.sub(r"\p{Vert_Space}", " / ", section.strip()) section = regex.sub(r"\p{Vert_Space}", " / ", section.strip())
msg: str = ( msg: str = f"**{search_result_song}** by **{search_result_artist}**\n-# {section}\n{footer}"
f"**{search_result_song}** by **{search_result_artist}**\n-# {section}\n{footer}"
)
if c > 1: if c > 1:
msg = "\n".join(msg.split("\n")[1:]) msg = "\n".join(msg.split("\n")[1:])
out_messages.append(msg.strip()) out_messages.append(msg.strip())
@ -159,9 +155,7 @@ class Sing(commands.Cog):
f"No activity detected to read for {member_display}.", f"No activity detected to read for {member_display}.",
ephemeral=True, ephemeral=True,
) )
member_id: int = ( member_id: int = member.id # if not(member.id == PODY_ID) else 1234134345497837679 # Use Thomas for Pody!
member.id
) # if not(member.id == PODY_ID) else 1234134345497837679 # Use Thomas for Pody!
activity: Optional[discord.Activity] = None activity: Optional[discord.Activity] = None
if IS_SPAMCHAN: if IS_SPAMCHAN:
await ctx.respond(f"***Reading activity of {member_display}...***") await ctx.respond(f"***Reading activity of {member_display}...***")
@ -199,9 +193,7 @@ class Sing(commands.Cog):
search_result_src, search_result_src,
search_result_confidence, search_result_confidence,
search_result_time_taken, search_result_time_taken,
) = search_result[ ) = search_result[0] # First index is a tuple
0
] # First index is a tuple
search_result_wrapped: list = search_result[ search_result_wrapped: list = search_result[
1 1
] # Second index is the wrapped lyrics ] # Second index is the wrapped lyrics
@ -230,9 +222,7 @@ class Sing(commands.Cog):
# if ctx.guild.id == 1145182936002482196: # if ctx.guild.id == 1145182936002482196:
# section = section.upper() # section = section.upper()
section = regex.sub(r"\p{Vert_Space}", " / ", section.strip()) section = regex.sub(r"\p{Vert_Space}", " / ", section.strip())
msg: str = ( msg: str = f"**{search_result_song}** by **{search_result_artist}**\n-# {section}\n{footer}"
f"**{search_result_song}** by **{search_result_artist}**\n-# {section}\n{footer}"
)
if c > 1: if c > 1:
msg = "\n".join(msg.split("\n")[1:]) msg = "\n".join(msg.split("\n")[1:])
out_messages.append(msg.strip()) out_messages.append(msg.strip())

View File

@ -4,7 +4,7 @@ edge_tts==6.1.12
feedparser==6.0.11 feedparser==6.0.11
Flask==3.0.3 Flask==3.0.3
nvdlib==0.7.7 nvdlib==0.7.7
openai==1.54.3 openai==1.54.3d
requests_async==0.6.2 requests_async==0.6.2
shazamio==0.7.0 shazamio==0.7.0
streamrip==2.0.5 streamrip==2.0.5

View File

@ -52,9 +52,7 @@ class JesusMemeGenerator:
if len(top_line) < 1 or len(bottom_line) < 1: if len(top_line) < 1 or len(bottom_line) < 1:
return None return None
formed_url: str = ( formed_url: str = f"{self.MEMEAPIURL}{meme}&top={top_line.strip()}&bottom={bottom_line.strip()}"
f"{self.MEMEAPIURL}{meme}&top={top_line.strip()}&bottom={bottom_line.strip()}"
)
formed_url = self.url_regex_1.sub( formed_url = self.url_regex_1.sub(
"+", self.url_regex_2.sub("%23", formed_url.strip()) "+", self.url_regex_2.sub("%23", formed_url.strip())
) )

View File

@ -257,7 +257,7 @@ class Util:
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
return (term, f"ERR: {str(e)}") return (term, f"ERR: {str(e)}")
async def get_no(self) -> str: async def get_no(self) -> str:
try: try:
async with ClientSession() as session: async with ClientSession() as session:
@ -266,19 +266,20 @@ class Util:
headers={ headers={
"content-type": "application/json; charset=utf-8", "content-type": "application/json; charset=utf-8",
}, },
timeout=ClientTimeout(connect=5, sock_read=5) timeout=ClientTimeout(connect=5, sock_read=5),
) as request: ) as request:
request.raise_for_status() request.raise_for_status()
response = await request.json() response = await request.json(encoding="utf-8")
no: str = response.get('no', None) no: str = response.get("no", None)
if not no: if not no:
logging.debug("Incorrect response received, JSON keys: %s", logging.debug(
response.keys()) "Incorrect response received, JSON keys: %s",
response.keys(),
)
return "No." return "No."
return no return no
except Exception as e: except Exception as e:
logging.debug("Exception: %s", logging.debug("Exception: %s", str(e))
str(e))
return "No." return "No."
async def get_insult(self, recipient: str) -> str: async def get_insult(self, recipient: str) -> str:
@ -323,13 +324,11 @@ class Util:
if not whisky_db: if not whisky_db:
return None return None
async with sqlite3.connect(database=whisky_db, timeout=2) as db_conn: async with sqlite3.connect(database=whisky_db, timeout=2) as db_conn:
db_query: str = ( db_query: str = "SELECT name, category, description FROM whiskeys ORDER BY random() LIMIT 1"
"SELECT name, category, description FROM whiskeys ORDER BY random() LIMIT 1"
)
async with await db_conn.execute(db_query) as db_cursor: async with await db_conn.execute(db_query) as db_cursor:
db_result: Optional[Union[sqlite3.Row, tuple]] = ( db_result: Optional[
await db_cursor.fetchone() Union[sqlite3.Row, tuple]
) ] = await db_cursor.fetchone()
if not db_result: if not db_result:
return None return None
(name, category, description) = db_result (name, category, description) = db_result
@ -398,9 +397,7 @@ class Util:
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 db_params: Optional[tuple] = None
if not strain: if not strain:
db_query: str = ( db_query: str = "SELECT name, description FROM strains_w_desc ORDER BY random() LIMIT 1"
"SELECT name, description FROM strains_w_desc ORDER BY random() LIMIT 1"
)
else: else:
db_query = ( db_query = (
"SELECT name, description FROM strains_w_desc WHERE name LIKE ?" "SELECT name, description FROM strains_w_desc WHERE name LIKE ?"
@ -442,9 +439,7 @@ class Util:
if not rjokes_db: if not rjokes_db:
return None return None
async with sqlite3.connect(database=rjokes_db, timeout=2) as db_conn: async with sqlite3.connect(database=rjokes_db, timeout=2) as db_conn:
db_query: str = ( db_query: str = "SELECT title, body, score FROM jokes WHERE score >= 100 ORDER BY RANDOM() LIMIT 1'"
"SELECT title, body, score FROM jokes WHERE score >= 100 ORDER BY RANDOM() LIMIT 1'"
)
async with await db_conn.execute(db_query) as cursor: async with await db_conn.execute(db_query) as cursor:
(title, body, score) = await cursor.fetchone() (title, body, score) = await cursor.fetchone()
return (title, body, score) return (title, body, score)

View File

@ -82,7 +82,9 @@ class Utility:
if ( if (
search_split_by == ":" and len(song.split(":")) > 2 search_split_by == ":" and len(song.split(":")) > 2
): # Support sub-search if : is used (per instructions) ): # Support sub-search if : is used (per instructions)
search_song = song.split(search_split_by)[ search_song = song.split(
search_split_by
)[
1 1
].strip() # Reduce search_song to only the 2nd split of : [the rest is meant to be lyric text] ].strip() # Reduce search_song to only the 2nd split of : [the rest is meant to be lyric text]
search_subsearch = "".join( search_subsearch = "".join(