Added search feature to `!funfact`
- Users can now search for fun facts using `!funfact search [keywords]` without brackets. Returns best match. - Also added some new facts - Minor tweaks to force-sync of Discord slash commandskami_dev
parent
0f1077778f
commit
6da1744990
|
@ -12,7 +12,7 @@ import modules.utility
|
||||||
from modules.db import log_message, lookup_user, log_bot_event
|
from modules.db import log_message, lookup_user, log_bot_event
|
||||||
|
|
||||||
|
|
||||||
primary_guild = globals.constants.primary_discord_guild()["object"]
|
primary_guild = globals.constants.primary_discord_guild()
|
||||||
|
|
||||||
class DiscordBot(commands.Bot):
|
class DiscordBot(commands.Bot):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -86,7 +86,13 @@ class DiscordBot(commands.Bot):
|
||||||
await ctx.reply(_result)
|
await ctx.reply(_result)
|
||||||
|
|
||||||
async def on_message(self, message):
|
async def on_message(self, message):
|
||||||
globals.log(f"Message detected by '{message.author.name}' in '{message.author.guild.name}' - #'{message.channel.name}'", "DEBUG")
|
if message.guild:
|
||||||
|
guild_name = message.guild.name
|
||||||
|
channel_name = message.channel.name
|
||||||
|
else:
|
||||||
|
guild_name = "DM"
|
||||||
|
channel_name = "Direct Message"
|
||||||
|
globals.log(f"Message detected by '{message.author.name}' in '{guild_name}' - #'{channel_name}'", "DEBUG")
|
||||||
#globals.log(f"Message body:\n{message}\nMessage content: {message.content}", "DEBUG") # Full message debug
|
#globals.log(f"Message body:\n{message}\nMessage content: {message.content}", "DEBUG") # Full message debug
|
||||||
globals.log(f"Message content: '{message.content}'", "DEBUG") # Partial message debug (content only)
|
globals.log(f"Message content: '{message.content}'", "DEBUG") # Partial message debug (content only)
|
||||||
globals.log(f"Attempting UUI lookup on '{message.author.name}' ...", "DEBUG")
|
globals.log(f"Attempting UUI lookup on '{message.author.name}' ...", "DEBUG")
|
||||||
|
@ -106,15 +112,15 @@ class DiscordBot(commands.Bot):
|
||||||
user_is_bot=is_bot
|
user_is_bot=is_bot
|
||||||
)
|
)
|
||||||
|
|
||||||
globals.log(f"... UUI lookup complete", "DEBUG")
|
|
||||||
|
|
||||||
user_data = lookup_user(db_conn=self.db_conn, identifier=user_id, identifier_type="discord_user_id")
|
user_data = lookup_user(db_conn=self.db_conn, identifier=user_id, identifier_type="discord_user_id")
|
||||||
user_uuid = user_data["UUID"] if user_data else "UNKNOWN"
|
user_uuid = user_data["UUID"] if user_data else "UNKNOWN"
|
||||||
|
globals.log(f"... UUI lookup complete", "DEBUG")
|
||||||
|
|
||||||
if user_uuid:
|
if user_uuid:
|
||||||
# The "platform" can be e.g. "discord" or you can store the server name
|
# The "platform" can be e.g. "discord" or you can store the server name
|
||||||
platform_str = f"discord-{message.guild.name}" if message.guild else "discord-DM"
|
platform_str = f"discord-{guild_name}"
|
||||||
# The channel name can be message.channel.name or "DM" if it's a private channel
|
# The channel name can be message.channel.name or "DM" if it's a private channel
|
||||||
channel_str = message.channel.name if hasattr(message.channel, "name") else "DM"
|
channel_str = channel_name
|
||||||
|
|
||||||
# If you have attachments, you could gather them as links.
|
# If you have attachments, you could gather them as links.
|
||||||
try:
|
try:
|
||||||
|
@ -183,20 +189,22 @@ class DiscordBot(commands.Bot):
|
||||||
# Sync slash commands globally
|
# Sync slash commands globally
|
||||||
#await self.tree.sync()
|
#await self.tree.sync()
|
||||||
#globals.log("Discord slash commands synced.")
|
#globals.log("Discord slash commands synced.")
|
||||||
primary_guild_int = int(self.config["discord_guilds"][0])
|
|
||||||
num_guilds = len(self.config["discord_guilds"])
|
num_guilds = len(self.config["discord_guilds"])
|
||||||
cmd_tree_result = (await self.tree.sync(guild=primary_guild))
|
cmd_tree_result = (await self.tree.sync(guild=primary_guild["object"]))
|
||||||
command_names = [command.name for command in cmd_tree_result] if cmd_tree_result else None
|
command_names = [command.name for command in cmd_tree_result] if cmd_tree_result else None
|
||||||
try:
|
if primary_guild["id"]:
|
||||||
guild_info = await modules.utility.get_guild_info(self, primary_guild_int)
|
try:
|
||||||
primary_guild_name = guild_info["name"]
|
guild_info = await modules.utility.get_guild_info(self, primary_guild["id"])
|
||||||
except Exception as e:
|
primary_guild_name = guild_info["name"]
|
||||||
primary_guild_name = f"{primary_guild_int} (id)"
|
except Exception as e:
|
||||||
globals.log(f"Guild lookup failed: {e}", "ERROR")
|
primary_guild_name = f"{primary_guild["id"]}"
|
||||||
|
globals.log(f"Guild lookup failed: {e}", "ERROR")
|
||||||
_log_message = f"{num_guilds} guilds (global)" if num_guilds > 1 else f"guild: {primary_guild_name}"
|
|
||||||
globals.log(f"Discord slash commands force synced to {_log_message}")
|
_log_message = f"{num_guilds} guilds (global)" if num_guilds > 1 else f"guild: {primary_guild_name}"
|
||||||
globals.log(f"Discord slash commands that got synced: {command_names}")
|
globals.log(f"Discord slash commands force synced to {_log_message}")
|
||||||
|
globals.log(f"Discord slash commands that got synced: {command_names}")
|
||||||
|
else:
|
||||||
|
globals.log("Discord commands synced globally.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
globals.log(f"Unable to sync Discord slash commands: {e}")
|
globals.log(f"Unable to sync Discord slash commands: {e}")
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import time
|
||||||
from modules import utility
|
from modules import utility
|
||||||
import globals
|
import globals
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
from modules import db
|
from modules import db
|
||||||
|
|
||||||
|
@ -873,7 +874,51 @@ async def send_message(ctx, text):
|
||||||
await ctx.send(text)
|
await ctx.send(text)
|
||||||
|
|
||||||
# Common backend function to get a random fun fact
|
# Common backend function to get a random fun fact
|
||||||
def get_fun_fact():
|
def get_fun_fact(keywords=None):
|
||||||
|
"""
|
||||||
|
If keywords is None or empty, returns a random fun fact.
|
||||||
|
Otherwise, searches for the best matching fun fact in dictionary/funfacts.json.
|
||||||
|
For each fun fact:
|
||||||
|
- Awards 2 points for each keyword found as a whole word.
|
||||||
|
- Awards 1 point for each keyword found as a partial match.
|
||||||
|
- Subtracts 1 point for each keyword provided.
|
||||||
|
In the event of a tie, one fun fact is chosen at random.
|
||||||
|
"""
|
||||||
with open('dictionary/funfacts.json', 'r') as f:
|
with open('dictionary/funfacts.json', 'r') as f:
|
||||||
facts = json.load(f)
|
facts = json.load(f)
|
||||||
return random.choice(facts)
|
|
||||||
|
# If no keywords provided, return a random fact.
|
||||||
|
if not keywords:
|
||||||
|
return random.choice(facts)
|
||||||
|
|
||||||
|
if len(keywords) < 2:
|
||||||
|
return "If you want to search, please append the command with `search [keywords]` without brackets."
|
||||||
|
|
||||||
|
keywords = keywords[1:]
|
||||||
|
lower_keywords = [kw.lower() for kw in keywords]
|
||||||
|
best_score = None
|
||||||
|
best_facts = []
|
||||||
|
|
||||||
|
for fact in facts:
|
||||||
|
score_total = 0
|
||||||
|
fact_lower = fact.lower()
|
||||||
|
|
||||||
|
# For each keyword, check for whole word and partial matches.
|
||||||
|
for kw in lower_keywords:
|
||||||
|
if re.search(r'\b' + re.escape(kw) + r'\b', fact_lower):
|
||||||
|
score_total += 2
|
||||||
|
elif kw in fact_lower:
|
||||||
|
score_total += 1
|
||||||
|
|
||||||
|
# Apply penalty for each keyword.
|
||||||
|
final_score = score_total - len(lower_keywords)
|
||||||
|
|
||||||
|
if best_score is None or final_score > best_score:
|
||||||
|
best_score = final_score
|
||||||
|
best_facts = [fact]
|
||||||
|
elif final_score == best_score:
|
||||||
|
best_facts.append(fact)
|
||||||
|
|
||||||
|
if not best_facts:
|
||||||
|
return "No matching fun facts found."
|
||||||
|
return random.choice(best_facts)
|
|
@ -35,9 +35,10 @@ def setup(bot):
|
||||||
config_data = globals.load_config_file()
|
config_data = globals.load_config_file()
|
||||||
|
|
||||||
@bot.command(name='funfact', aliases=['fun-fact'])
|
@bot.command(name='funfact', aliases=['fun-fact'])
|
||||||
async def funfact_command(ctx):
|
async def funfact_command(ctx, *keywords):
|
||||||
fact = cc.get_fun_fact()
|
# keywords is a tuple of strings from the command arguments.
|
||||||
# Replies to the invoking user
|
fact = cc.get_fun_fact(list(keywords))
|
||||||
|
# Reply to the invoking user.
|
||||||
await ctx.reply(fact)
|
await ctx.reply(fact)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,11 @@ def setup(bot, db_conn=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@commands.command(name='funfact', aliases=['fun-fact'])
|
@commands.command(name='funfact', aliases=['fun-fact'])
|
||||||
async def funfact_command(self, ctx: commands.Context):
|
async def funfact_command(self, ctx: commands.Context, *keywords: str):
|
||||||
fact = cc.get_fun_fact()
|
# Convert keywords tuple to list and pass to get_fun_fact.
|
||||||
# Replies to the invoking user by prefixing their name
|
fact = cc.get_fun_fact(list(keywords))
|
||||||
await ctx.reply(fact)
|
# Reply to the invoking user by prefixing their name.
|
||||||
|
await ctx.reply(f"@{ctx.author.name} {fact}")
|
||||||
|
|
||||||
@bot.command(name="greet")
|
@bot.command(name="greet")
|
||||||
async def cmd_greet(ctx: commands.Context):
|
async def cmd_greet(ctx: commands.Context):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"discord_guilds": [896713616089309184],
|
"discord_guilds": [896713616089309184],
|
||||||
|
"sync_commands_globally": true,
|
||||||
"twitch_channels": ["OokamiKunTV", "ookamipup"],
|
"twitch_channels": ["OokamiKunTV", "ookamipup"],
|
||||||
"command_modules": ["cmd_discord", "cmd_twitch", "cmd_common"],
|
"command_modules": ["cmd_discord", "cmd_twitch", "cmd_common"],
|
||||||
"logging": {
|
"logging": {
|
||||||
|
|
|
@ -506,6 +506,28 @@
|
||||||
"The historical miniaturization of transistors has been a key driver in the rapid evolution of computing hardware.",
|
"The historical miniaturization of transistors has been a key driver in the rapid evolution of computing hardware.",
|
||||||
"Advances in image processing now enable computers to interpret and analyze visual data with remarkable precision.",
|
"Advances in image processing now enable computers to interpret and analyze visual data with remarkable precision.",
|
||||||
"Some nuclear power plants use naturally sourced water for cooling, challenging the notion that all require artificial systems.",
|
"Some nuclear power plants use naturally sourced water for cooling, challenging the notion that all require artificial systems.",
|
||||||
"Webster Lake, aka 'Chargoggagoggmanchauggagoggchaubunagungamaugg', can be translated to 'You fish on your side, I'll fish on my side, and no one shall fish in the middle.'"
|
"Webster Lake, aka 'Chargoggagoggmanchauggagoggchaubunagungamaugg', can be translated to 'You fish on your side, I'll fish on my side, and no one shall fish in the middle.'",
|
||||||
]
|
"Cows, along with other ruminants like sheep, deer and giraffes, have 4 'stomachs', or compartments within the stomach, called the rumen, the reticulum, the omasum and the abomasum.",
|
||||||
|
"It is proposed that cheese have existed for around 10.000 years, with the earliest archaelogical record being almost 8.000 years old.",
|
||||||
|
"Wolves have been observed exhibiting mourning behaviors, lingering near the remains of fallen pack members in a manner that suggests they experience grief.",
|
||||||
|
"Recent research reveals that the unique patterns in wolf howls serve as a vocal fingerprint, conveying detailed information about an individual's identity and emotional state.",
|
||||||
|
"Contrary to the classic 'alpha' myth, many wolf packs function as family units where the parents lead the group and even subordinate wolves may reproduce.",
|
||||||
|
"Wolves display remarkable flexibility in their hunting strategies, adapting their techniques based on the prey available and the terrain, demonstrating problem-solving abilities rarely attributed to wild carnivores.",
|
||||||
|
"In certain wolf populations, individuals have been seen sharing food with non-pack members, challenging the long-held view of wolves as strictly territorial and competitive.",
|
||||||
|
"Studies show that a wolf's sense of smell is so acute that it can detect prey from several miles away and even follow scent trails that are days old.",
|
||||||
|
"Cows can recognize and remember the faces of up to 50 individuals, both other cows and humans, indicating a surprisingly complex memory capacity.",
|
||||||
|
"Beyond their docile reputation, cows have been found to experience a range of emotions, displaying signs of depression when isolated and apparent joy when in the company of their close companions.",
|
||||||
|
"Research has demonstrated that cows can solve simple puzzles for a reward, challenging the stereotype that they are only passive grazers.",
|
||||||
|
"Cows communicate through a rich array of vocalizations and subtle body language, suggesting they convey complex emotional states that scientists are only beginning to decipher.",
|
||||||
|
"Cheese may have been discovered accidentally when milk was stored in containers lined with rennet-rich animal stomachs, causing it to curdle.",
|
||||||
|
"There are over 1,800 distinct varieties of cheese worldwide, far more than the few types most people are familiar with.",
|
||||||
|
"Aged cheeses contain virtually no lactose, which explains why many lactose-intolerant individuals can enjoy them without discomfort.",
|
||||||
|
"Cheese rolling, an annual event in Gloucestershire, England, is not just a quirky tradition but an ancient competitive sport with a history spanning centuries.",
|
||||||
|
"The characteristic holes in Swiss cheese, known as 'eyes,' are formed by gas bubbles released by bacteria during the fermentation process.",
|
||||||
|
"Pule cheese, made from the milk of Balkan donkeys, is one of the world's most expensive cheeses, costing over a thousand euros per kilo due to its rarity and labor-intensive production.",
|
||||||
|
"Cheese may have addictive qualities: during digestion, casomorphins are released from milk proteins, interacting with brain receptors in a way similar to opiates.",
|
||||||
|
"Some artisanal cheeses are aged in natural caves, where unique microclimates contribute to complex flavors that are difficult to replicate in modern facilities.",
|
||||||
|
"While Cheddar cheese is now a global staple, it originally came from the small English village of Cheddar, and its production methods have evolved dramatically over time.",
|
||||||
|
"Modern cheese production often uses vegetarian rennet, derived from microbial or plant sources, challenging the common belief that all cheese is made with animal-derived enzymes.",
|
||||||
|
"Cows are related to whales, as they both evolved from land-dwelling, even-toed ungulates, hence why a baby whale is called a 'calf'."
|
||||||
|
]
|
14
globals.py
14
globals.py
|
@ -214,10 +214,18 @@ class Constants:
|
||||||
guild is defined; otherwise, `None`.
|
guild is defined; otherwise, `None`.
|
||||||
- "id": The integer ID of the primary guild if available; otherwise, `None`.
|
- "id": The integer ID of the primary guild if available; otherwise, `None`.
|
||||||
"""
|
"""
|
||||||
|
# Checks for a True/False value in the config file to determine if commands sync should be global or single-guild
|
||||||
|
# If this is 'true' in config, it will
|
||||||
|
sync_commands_globally = config_data["sync_commands_globally"]
|
||||||
primary_guild_object = None
|
primary_guild_object = None
|
||||||
primary_guild_int = int(config_data["discord_guilds"][0]) if len(config_data["discord_guilds"]) == 1 else None
|
primary_guild_int = None
|
||||||
if primary_guild_int:
|
|
||||||
primary_guild_object = discord.Object(id=primary_guild_int)
|
if not sync_commands_globally:
|
||||||
|
log("Discord commands sync set to single-guild in config")
|
||||||
|
primary_guild_int = int(config_data["discord_guilds"][0]) if len(config_data["discord_guilds"]) > 0 else None
|
||||||
|
if primary_guild_int:
|
||||||
|
primary_guild_object = discord.Object(id=primary_guild_int)
|
||||||
|
|
||||||
return_dict = {"object": primary_guild_object, "id": primary_guild_int}
|
return_dict = {"object": primary_guild_object, "id": primary_guild_int}
|
||||||
return return_dict
|
return return_dict
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue