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
|
||||
|
||||
|
||||
primary_guild = globals.constants.primary_discord_guild()["object"]
|
||||
primary_guild = globals.constants.primary_discord_guild()
|
||||
|
||||
class DiscordBot(commands.Bot):
|
||||
def __init__(self):
|
||||
|
@ -86,7 +86,13 @@ class DiscordBot(commands.Bot):
|
|||
await ctx.reply(_result)
|
||||
|
||||
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 content: '{message.content}'", "DEBUG") # Partial message debug (content only)
|
||||
globals.log(f"Attempting UUI lookup on '{message.author.name}' ...", "DEBUG")
|
||||
|
@ -106,15 +112,15 @@ class DiscordBot(commands.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_uuid = user_data["UUID"] if user_data else "UNKNOWN"
|
||||
globals.log(f"... UUI lookup complete", "DEBUG")
|
||||
|
||||
if user_uuid:
|
||||
# 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
|
||||
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.
|
||||
try:
|
||||
|
@ -183,20 +189,22 @@ class DiscordBot(commands.Bot):
|
|||
# Sync slash commands globally
|
||||
#await self.tree.sync()
|
||||
#globals.log("Discord slash commands synced.")
|
||||
primary_guild_int = int(self.config["discord_guilds"][0])
|
||||
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
|
||||
if primary_guild["id"]:
|
||||
try:
|
||||
guild_info = await modules.utility.get_guild_info(self, primary_guild_int)
|
||||
guild_info = await modules.utility.get_guild_info(self, primary_guild["id"])
|
||||
primary_guild_name = guild_info["name"]
|
||||
except Exception as e:
|
||||
primary_guild_name = f"{primary_guild_int} (id)"
|
||||
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}")
|
||||
globals.log(f"Discord slash commands that got synced: {command_names}")
|
||||
else:
|
||||
globals.log("Discord commands synced globally.")
|
||||
except Exception as e:
|
||||
globals.log(f"Unable to sync Discord slash commands: {e}")
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import time
|
|||
from modules import utility
|
||||
import globals
|
||||
import json
|
||||
import re
|
||||
|
||||
from modules import db
|
||||
|
||||
|
@ -873,7 +874,51 @@ async def send_message(ctx, text):
|
|||
await ctx.send(text)
|
||||
|
||||
# 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:
|
||||
facts = json.load(f)
|
||||
|
||||
# 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()
|
||||
|
||||
@bot.command(name='funfact', aliases=['fun-fact'])
|
||||
async def funfact_command(ctx):
|
||||
fact = cc.get_fun_fact()
|
||||
# Replies to the invoking user
|
||||
async def funfact_command(ctx, *keywords):
|
||||
# keywords is a tuple of strings from the command arguments.
|
||||
fact = cc.get_fun_fact(list(keywords))
|
||||
# Reply to the invoking user.
|
||||
await ctx.reply(fact)
|
||||
|
||||
|
||||
|
|
|
@ -15,10 +15,11 @@ def setup(bot, db_conn=None):
|
|||
"""
|
||||
|
||||
@commands.command(name='funfact', aliases=['fun-fact'])
|
||||
async def funfact_command(self, ctx: commands.Context):
|
||||
fact = cc.get_fun_fact()
|
||||
# Replies to the invoking user by prefixing their name
|
||||
await ctx.reply(fact)
|
||||
async def funfact_command(self, ctx: commands.Context, *keywords: str):
|
||||
# Convert keywords tuple to list and pass to get_fun_fact.
|
||||
fact = cc.get_fun_fact(list(keywords))
|
||||
# Reply to the invoking user by prefixing their name.
|
||||
await ctx.reply(f"@{ctx.author.name} {fact}")
|
||||
|
||||
@bot.command(name="greet")
|
||||
async def cmd_greet(ctx: commands.Context):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"discord_guilds": [896713616089309184],
|
||||
"sync_commands_globally": true,
|
||||
"twitch_channels": ["OokamiKunTV", "ookamipup"],
|
||||
"command_modules": ["cmd_discord", "cmd_twitch", "cmd_common"],
|
||||
"logging": {
|
||||
|
|
|
@ -506,6 +506,28 @@
|
|||
"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.",
|
||||
"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'."
|
||||
]
|
||||
|
10
globals.py
10
globals.py
|
@ -214,10 +214,18 @@ class Constants:
|
|||
guild is defined; 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_int = int(config_data["discord_guilds"][0]) if len(config_data["discord_guilds"]) == 1 else None
|
||||
primary_guild_int = None
|
||||
|
||||
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 return_dict
|
||||
|
||||
|
|
Loading…
Reference in New Issue