aboutsummaryrefslogtreecommitdiffstats
path: root/contessa/commands.py
diff options
context:
space:
mode:
authorAhmed <git@gumx.cc>2026-06-14 01:46:29 +0300
committerAhmed <git@gumx.cc>2026-06-14 01:46:29 +0300
commit5a8d568931d9b23ce0df1265d05259a7012081c9 (patch)
tree4ea19b8bb1763a38246f342f172c285f6579e09b /contessa/commands.py
init: mostly vibed
Diffstat (limited to 'contessa/commands.py')
-rw-r--r--contessa/commands.py191
1 files changed, 191 insertions, 0 deletions
diff --git a/contessa/commands.py b/contessa/commands.py
new file mode 100644
index 0000000..ac5be72
--- /dev/null
+++ b/contessa/commands.py
@@ -0,0 +1,191 @@
+"""
+Sopel plugin: contessa - recipe bot
+
+Egyptian, Middle Eastern and Western recipes.
+
+ !breakfast [list] - random breakfast dish or list all
+ !lunch [list] - random lunch dish or list all
+ !dinner [list] - random dinner dish or list all
+ !snack [list] - random snack or list all
+ !recipe <name> - full recipe (up to 3 lines)
+
+ !join / !part / !chans / !help - owner commands
+"""
+
+import json
+import os
+import random
+
+from sopel import plugin
+from sopel.config.types import FilenameAttribute, StaticSection
+
+
+class ContessaSection(StaticSection):
+ recipes_path = FilenameAttribute("recipes_path", relative=False,
+ default="/var/contessa/recipes.json")
+
+
+def setup(bot):
+ bot.config.define_section("contessa", ContessaSection, validate=False)
+ try:
+ path = bot.config.contessa.recipes_path
+ except Exception:
+ path = "/var/contessa/recipes.json"
+
+ try:
+ with open(path) as f:
+ bot.memory["ct_recipes"] = json.load(f)
+ except Exception:
+ bot.memory["ct_recipes"] = {"breakfast": [], "lunch": [], "dinner": [], "snack": []}
+
+
+def _is_owner(bot, trigger):
+ return trigger.nick == bot.settings.core.owner
+
+
+def _fmt_random(category, dish):
+ return (
+ f"{category.capitalize()} suggestion: "
+ f"{dish['name']} | {dish['description']}. "
+ f"Type !recipe {dish['name'].lower()} for the full recipe."
+ )
+
+
+def _fmt_list(category, dishes):
+ names = ", ".join(d["name"] for d in dishes)
+ return f"{category.capitalize()} dishes ({len(dishes)}): {names}"
+
+
+def _fmt_recipe(dish):
+ line1 = f"{dish['name']} | {dish['description']}"
+ line2 = "Ingredients: " + ", ".join(dish["ingredients"])
+ steps = " ".join(
+ f"{i+1}. {s.rstrip('.')}." for i, s in enumerate(dish["steps"])
+ )
+ line3 = steps
+ return line1, line2, line3
+
+
+def _all_dishes(bot):
+ recipes = bot.memory.get("ct_recipes", {})
+ for category, dishes in recipes.items():
+ for dish in dishes:
+ yield category, dish
+
+
+def _find_dish(bot, query):
+ q = query.strip().lower()
+ for _, dish in _all_dishes(bot):
+ if dish["name"].lower() == q:
+ return dish
+ for _, dish in _all_dishes(bot):
+ if q in dish["name"].lower():
+ return dish
+ return None
+
+
+def _meal_cmd(bot, trigger, category):
+ arg = (trigger.group(2) or "").strip().lower()
+ recipes = bot.memory.get("ct_recipes", {})
+ dishes = recipes.get(category, [])
+
+ if not dishes:
+ bot.say(f"no {category} recipes loaded", trigger.sender)
+ return
+
+ if arg == "list":
+ bot.say(_fmt_list(category, dishes), trigger.sender)
+ return
+
+ dish = random.choice(dishes)
+ bot.say(_fmt_random(category, dish), trigger.sender)
+
+
+@plugin.command("breakfast")
+@plugin.require_chanmsg
+def cmd_breakfast(bot, trigger):
+ _meal_cmd(bot, trigger, "breakfast")
+
+
+@plugin.command("lunch")
+@plugin.require_chanmsg
+def cmd_lunch(bot, trigger):
+ _meal_cmd(bot, trigger, "lunch")
+
+
+@plugin.command("dinner")
+@plugin.require_chanmsg
+def cmd_dinner(bot, trigger):
+ _meal_cmd(bot, trigger, "dinner")
+
+
+@plugin.command("snack")
+@plugin.require_chanmsg
+def cmd_snack(bot, trigger):
+ _meal_cmd(bot, trigger, "snack")
+
+
+@plugin.command("recipe")
+@plugin.require_chanmsg
+def cmd_recipe(bot, trigger):
+ query = (trigger.group(2) or "").strip()
+ if not query:
+ bot.say("usage: !recipe <dish name>", trigger.sender)
+ return
+
+ dish = _find_dish(bot, query)
+ if not dish:
+ bot.say(f"no recipe found for '{query}'", trigger.sender)
+ return
+
+ line1, line2, line3 = _fmt_recipe(dish)
+ bot.say(line1, trigger.sender)
+ bot.say(line2, trigger.sender)
+ bot.say(line3, trigger.sender)
+
+
+@plugin.command("join")
+def cmd_join(bot, trigger):
+ if not _is_owner(bot, trigger):
+ return
+ channel = (trigger.group(2) or "").strip()
+ if not channel.startswith("#"):
+ bot.say("usage: !join #channel", trigger.nick)
+ return
+ bot.join(channel)
+
+
+@plugin.command("part")
+def cmd_part(bot, trigger):
+ if not _is_owner(bot, trigger):
+ return
+ channel = (trigger.group(2) or "").strip() or str(trigger.sender)
+ bot.part(channel)
+
+
+@plugin.command("chans")
+def cmd_chans(bot, trigger):
+ if not _is_owner(bot, trigger):
+ return
+ chans = sorted(str(c) for c in bot.channels)
+ bot.say(("in: " + " ".join(chans)) if chans else "not in any channels", trigger.nick)
+
+
+@plugin.command("help")
+def cmd_help(bot, trigger):
+ lines = [
+ "!breakfast [list] - random breakfast dish or list all",
+ "!lunch [list] - random lunch dish or list all",
+ "!dinner [list] - random dinner dish or list all",
+ "!snack [list] - random snack or list all",
+ "!recipe <name> - full recipe (3 lines)",
+ ]
+ if _is_owner(bot, trigger):
+ lines += [
+ "!join #channel - join a channel",
+ "!part [#channel] - leave a channel",
+ "!chans - list channels",
+ ]
+ lines.append("!help - this message")
+ for line in lines:
+ bot.say(line, trigger.nick)