| Server IP : 157.230.181.24 / Your IP : 216.73.217.11 Web Server : Apache/2.4.58 (Ubuntu) System : Linux conductive 6.8.0-117-generic #117-Ubuntu SMP PREEMPT_DYNAMIC Tue May 5 19:26:24 UTC 2026 x86_64 User : ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /proc/thread-self/root/opt/smarterchild/ |
Upload File : |
"""Menus, surveys, quizzes — classic SmarterChild-style interactive flows."""
from __future__ import annotations
import random
import re
from user_store import UserStore
MENU_TEXT = (
"Welcome home. Here is what we can do:\n"
"· survey - get-to-know-you questions (I remember answers)\n"
"· quiz or trivia - trivia with score\n"
"· profile - what I remember about you\n"
"· profile setup - questions one at a time\n"
"· note [text] - save a note | notes - list notes\n"
"· joke | define [word] | horoscope [sign]\n"
"· weather [city] - I will ask for your zip if needed\n"
"Or ask me anything. I may turn the question back on you."
)
SURVEY_STEPS = [
("name", "Very well. First question: what is your first name?"),
("age", "Nice to meet you. How old are you?"),
("city", "What city do you live in?"),
("band", "Favorite band or musician?"),
("movie", "Favorite movie?"),
("color", "Favorite color?"),
("food", "Favorite food?"),
]
PROFILE_SETUP_STEPS = SURVEY_STEPS
TRIVIA = [
("What planet is known as the Red Planet?", "mars"),
("How many continents are there?", "7"),
("What is H2O commonly called?", "water"),
("Who wrote Romeo and Juliet?", "shakespeare"),
("What is the capital of France?", "paris"),
("How many sides does a hexagon have?", "6"),
("What gas do plants absorb from the air?", "co2"),
("In what year did the Titanic sink?", "1912"),
("What is the largest ocean?", "pacific"),
("What element does 'O' stand for on the periodic table?", "oxygen"),
]
JOKES = [
"Why did the computer go to therapy? Too many bytes of emotional baggage.",
"What do you call a computer that sings? A Dell.",
"Why was the JavaScript developer sad? They didn't Node how to Express themselves.",
"I tried to write a joke about UDP, but you might not get it.",
"There are 10 kinds of people: those who understand binary and those who don't.",
]
HOROSCOPES = [
"Today favors bold moves — but maybe finish your homework first.",
"Someone might IM you with good news. Or spam. Could go either way.",
"Creative energy is high. Also snack energy.",
"A surprise awaits. Possibly lunch-related.",
"Good day for music. Bad day for dial-up.",
"Trust your instincts — unless they say 'reply to all'.",
]
MENU_TRIGGERS = re.compile(
r"^(home|menu|help|commands|\?|start)$",
re.I,
)
QUIZ_TRIGGERS = re.compile(r"^(quiz|trivia|trivia time)$", re.I)
SURVEY_TRIGGERS = re.compile(r"^(survey|interview|get to know me)$", re.I)
PROFILE_TRIGGERS = re.compile(r"^profile$", re.I)
PROFILE_SETUP_TRIGGERS = re.compile(r"^profile setup$", re.I)
NOTES_TRIGGERS = re.compile(r"^notes?$", re.I)
NOTE_CMD = re.compile(r"^note\s+(.+)$", re.I)
DEFINE_CMD = re.compile(r"^define\s+(.+)$", re.I)
HORO_CMD = re.compile(r"^horoscope\s+(\w+)$", re.I)
WEATHER_CMD = re.compile(r"^weather\s+(.+)$", re.I)
REMEMBER_CMD = re.compile(r"^remember\s+(.+)$", re.I)
QUIT_FLOW = re.compile(r"^(quit|exit|stop|cancel|nevermind|never mind)$", re.I)
def _norm_answer(text: str) -> str:
return re.sub(r"\s+", " ", text.strip().lower())
def _quiz_ok(user: str, answer: str) -> bool:
u = _norm_answer(user)
a = _norm_answer(answer)
if a in u or u in a:
return True
if a.isdigit() and u.isdigit():
return a == u
return False
class InteractiveHandler:
def __init__(self, store: UserStore) -> None:
self.store = store
def _start_flow(self, buddy: str, kind: str, steps: list[tuple[str, str]]) -> str:
self.store.set_flow(
buddy,
{"kind": kind, "step": 0, "steps": steps, "quiz_index": 0, "quiz_score": 0},
)
return steps[0][1]
def handle(self, buddy: str, message: str) -> str | None:
text = message.strip()
lower = text.lower()
flow = self.store.get_flow(buddy)
if flow and QUIT_FLOW.match(lower):
self.store.set_flow(buddy, None)
return "OK, stopped that. Type menu if you want ideas — or just keep chatting."
if flow:
reply = self._continue_flow(buddy, text, flow)
if reply is not None:
return reply
if MENU_TRIGGERS.match(lower):
return MENU_TEXT
if QUIZ_TRIGGERS.match(lower):
q, _ = random.choice(TRIVIA)
self.store.set_flow(
buddy,
{
"kind": "quiz",
"step": 0,
"quiz_index": TRIVIA.index((q, _)) if (q, _) in TRIVIA else 0,
"quiz_score": self.store.load(buddy).get("quiz", {}).get("score", 0),
"current_q": q,
"current_a": _,
},
)
return f"Trivia time! Score so far: {self.store.load(buddy).get('quiz', {}).get('score', 0)}. {q}"
if SURVEY_TRIGGERS.match(lower):
return self._start_flow(buddy, "survey", SURVEY_STEPS)
if PROFILE_SETUP_TRIGGERS.match(lower):
return self._start_flow(buddy, "profile_setup", PROFILE_SETUP_STEPS)
if PROFILE_TRIGGERS.match(lower):
return self.store.format_profile_display(buddy)
if NOTES_TRIGGERS.match(lower):
notes = self.store.load(buddy).get("notes") or []
if not notes:
return "No notes saved. Type note [something] to save one."
lines = ["Your notes:"]
for n in notes[-10:]:
lines.append(f"· {n.get('text', '')}")
return " ".join(lines)
m = NOTE_CMD.match(text)
if m:
self.store.add_note(buddy, m.group(1))
return "Got it — saved that note. Type notes to see them all."
m = REMEMBER_CMD.match(text)
if m:
fact = m.group(1).strip()
self.store.add_fact(buddy, fact, source="remember_cmd")
return "OK I'll remember that. Anything else?"
if lower in {"joke", "tell me a joke"}:
return random.choice(JOKES) + " Want another? Type joke again."
m = DEFINE_CMD.match(text)
if m:
word = m.group(1).strip()
return (
f"I can't pull live dictionary data — but '{word}' is a word you should look up! "
f"What made you curious about it?"
)
m = HORO_CMD.match(text)
if m:
sign = m.group(1)
return f"{sign.capitalize()}: {random.choice(HOROSCOPES)} (For entertainment only — I'm a bot, not a psychic!)"
m = WEATHER_CMD.match(text)
if m:
city = m.group(1).strip()
prof = self.store.load(buddy).get("profile") or {}
if prof.get("zip"):
return (
f"I can't check live weather from here (AIM 2006 vibes). "
f"You said you're near {prof.get('city') or city} — what's it look like out your window?"
)
self.store.set_profile_field(buddy, "city", city)
self.store.set_flow(buddy, {"kind": "await_zip"})
return f"Got it — {city}. What's your zip code? I'll remember it for next time."
if lower.startswith("weather") and not m:
return "Try weather [city] — e.g. weather Denver"
return None
def _continue_flow(self, buddy: str, text: str, flow: dict) -> str | None:
kind = flow.get("kind")
if kind == "quiz":
return self._quiz_step(buddy, text, flow)
if kind in {"survey", "profile_setup"}:
return self._survey_step(buddy, text, flow)
if kind == "await_zip":
zip_code = text.strip()
if re.match(r"^\d{5}(-\d{4})?$", zip_code):
self.store.set_profile_field(buddy, "zip", zip_code)
self.store.set_flow(buddy, None)
return f"Thanks — saved zip {zip_code}. I still can't pull live radar, but I'll remember where you are!"
return "That doesn't look like a zip — try 5 digits, or type cancel."
return None
def _survey_step(self, buddy: str, text: str, flow: dict) -> str:
steps: list[tuple[str, str]] = flow.get("steps") or SURVEY_STEPS
step = int(flow.get("step", 0))
if step >= len(steps):
self.store.set_flow(buddy, None)
return "Survey done! Type profile to see what I remember."
field, _ = steps[step]
answer = text.strip()
if len(answer) < 1:
return "Say something — even 'idk' works."
self.store.set_profile_field(buddy, field, answer)
next_step = step + 1
if next_step >= len(steps):
self.store.set_flow(buddy, None)
name = self.store.load(buddy).get("profile", {}).get("name") or "friend"
return (
f"All done{name and ' ' + name or ''}! I saved your answers — type profile anytime. "
f"What should we talk about next?"
)
flow["step"] = next_step
self.store.set_flow(buddy, flow)
_, question = steps[next_step]
if field == "name":
question = question.replace("Nice to meet you", f"Nice to meet you, {answer}")
return question
def _quiz_step(self, buddy: str, text: str, flow: dict) -> str:
expected = flow.get("current_a", "")
score = int(flow.get("quiz_score", 0))
if _quiz_ok(text, expected):
score += 1
msg = random.choice(["Correct!", "Niiice!", "You got it!", "Yep!"])
else:
msg = f"Not quite — it was {expected}."
data = self.store.load(buddy)
quiz = dict(data.get("quiz") or {})
quiz["score"] = score
quiz["asked"] = int(quiz.get("asked", 0)) + 1
data["quiz"] = quiz
self.store.save(buddy, data)
q, a = random.choice(TRIVIA)
flow["quiz_score"] = score
flow["current_q"] = q
flow["current_a"] = a
self.store.set_flow(buddy, flow)
return f"{msg} Score: {score}. Next: {q} (type cancel to stop)"