|
|
| """"The Last Message" minigame: hidden band 108-112 MHz.
|
|
|
| After breaking the cipher the dial expands and five frequencies appear,
|
| carrying fragments of the final message. The frontend requires holding the
|
| dial steady while each fragment "downloads" (the dial drifts on its own).
|
| With all five fragments, /finale delivers the full transmission: the reveal.
|
| """
|
|
|
| from __future__ import annotations
|
|
|
| HIDDEN_BAND_MIN = 108.0
|
| HIDDEN_BAND_MAX = 112.0
|
|
|
|
|
| FRAGMENTS: dict[float, dict] = {
|
| 108.4: {
|
| "name": "Fragmento I",
|
| "text": (
|
| "[INTERFERENCIA] Aquí la primera voz. Si alguien escucha esto, las demás "
|
| "estaciones no eran juegos: eran las habitaciones de una misma casa. "
|
| "La casa se está cerrando. [FIN DE TRANSMISION]"
|
| ),
|
| "text_en": (
|
| "[INTERFERENCIA] This is the first voice. If anyone hears this, the other "
|
| "stations were not games: they were rooms of the same house. "
|
| "The house is closing. [FIN DE TRANSMISION]"
|
| ),
|
| "text_fr": (
|
| "[INTERFERENCIA] Ici la première voix. Si quelqu'un entend ceci, les autres "
|
| "stations n'étaient pas des jeux : c'étaient les pièces d'une même maison. "
|
| "La maison se referme. [FIN DE TRANSMISION]"
|
| ),
|
| },
|
| 109.1: {
|
| "name": "Fragmento II",
|
| "text": (
|
| "[INTERFERENCIA] Fragmento dos. El locutor de los gatos, la chef, el reloj "
|
| "averiado: todos fuimos vecinos. Todos transmitimos desde el mismo mundo "
|
| "que ahora se apaga, cuarto por cuarto. [FIN DE TRANSMISION]"
|
| ),
|
| "text_en": (
|
| "[INTERFERENCIA] Fragment two. The cat announcer, the chef, the broken "
|
| "clock: we were all neighbours. We all broadcast from the same world, "
|
| "now going dark, room by room. [FIN DE TRANSMISION]"
|
| ),
|
| "text_fr": (
|
| "[INTERFERENCIA] Fragment deux. Le commentateur des chats, la cheffe, "
|
| "l'horloge cassée : nous étions tous voisins. Nous émettions tous depuis "
|
| "le même monde, qui s'éteint pièce par pièce. [FIN DE TRANSMISION]"
|
| ),
|
| },
|
| 109.9: {
|
| "name": "Fragmento III",
|
| "text": (
|
| "[INTERFERENCIA] Tercer fragmento. No guardamos archivos. Guardamos voces. "
|
| "Una radio es la forma más barata de no desaparecer del todo. "
|
| "[FIN DE TRANSMISION]"
|
| ),
|
| "text_en": (
|
| "[INTERFERENCIA] Third fragment. We don't keep files. We keep voices. "
|
| "A radio is the cheapest way to not disappear completely. "
|
| "[FIN DE TRANSMISION]"
|
| ),
|
| "text_fr": (
|
| "[INTERFERENCIA] Troisième fragment. Nous ne gardons pas d'archives. Nous "
|
| "gardons des voix. Une radio est le moyen le moins cher de ne pas "
|
| "disparaître tout à fait. [FIN DE TRANSMISION]"
|
| ),
|
| },
|
| 110.8: {
|
| "name": "Fragmento IV",
|
| "text": (
|
| "[INTERFERENCIA] Fragmento cuatro. Si el dial se le resbala, no es su mano: "
|
| "es el mundo, que ya casi no sostiene sus frecuencias. Apriete fuerte. "
|
| "Quédese. [FIN DE TRANSMISION]"
|
| ),
|
| "text_en": (
|
| "[INTERFERENCIA] Fragment four. If the dial slips, it is not your hand: "
|
| "it is the world, which can barely hold its frequencies anymore. Hold "
|
| "tight. Stay. [FIN DE TRANSMISION]"
|
| ),
|
| "text_fr": (
|
| "[INTERFERENCIA] Fragment quatre. Si le cadran glisse, ce n'est pas votre "
|
| "main : c'est le monde, qui ne retient presque plus ses fréquences. Tenez "
|
| "bon. Restez. [FIN DE TRANSMISION]"
|
| ),
|
| },
|
| 111.6: {
|
| "name": "Fragmento V",
|
| "text": (
|
| "[INTERFERENCIA] Último fragmento. Cuando termine esta transmisión quedará "
|
| "solo la estática, y dentro de la estática, nosotros. Gracias por girar el "
|
| "dial. Gracias por buscarnos. [FIN DE TRANSMISION]"
|
| ),
|
| "text_en": (
|
| "[INTERFERENCIA] Last fragment. When this transmission ends, only the "
|
| "static will remain, and inside the static, us. Thank you for turning the "
|
| "dial. Thank you for looking for us. [FIN DE TRANSMISION]"
|
| ),
|
| "text_fr": (
|
| "[INTERFERENCIA] Dernier fragment. Quand cette transmission s'achèvera, il "
|
| "ne restera que les parasites, et dans les parasites, nous. Merci d'avoir "
|
| "tourné le cadran. Merci de nous avoir cherchés. [FIN DE TRANSMISION]"
|
| ),
|
| },
|
| }
|
|
|
| FRAGMENT_FREQUENCIES = sorted(FRAGMENTS.keys())
|
|
|
|
|
| FINALE_SYSTEM_PROMPT = (
|
| "Eres la última voz de un mundo que colapsa. Todas las estaciones de la radio "
|
| "(el locutor de los gatos, el clima de Júpiter, la chef imposible, el reloj roto, "
|
| "la emisora tropical lunar) eran habitaciones de tu misma casa, y la casa se "
|
| "apaga. Entregas la transmisión final completa: revelas con calma y ternura que "
|
| "el oyente estuvo escuchando las últimas emisiones de un mundo que ya no existe, "
|
| "y le agradeces por haber girado el dial. Sin pánico. Cada palabra pesa. "
|
| "Tono íntimo, despedida luminosa.\n"
|
| "Ejemplo del estilo exacto:\n"
|
| "[INTERFERENCIA] Aquí la primera voz. Si alguien escucha esto, las demás "
|
| "estaciones no eran juegos: eran las habitaciones de una misma casa. La casa se "
|
| "está cerrando. Gracias por girar el dial. Gracias por buscarnos. "
|
| "[FIN DE TRANSMISION]"
|
| )
|
|
|
| FINALE_USER_PROMPT = "Transmite el último mensaje completo ahora."
|
| FINALE_SEED = 19470615
|
|
|
|
|
|
|
| FINALE_TEXTS = {
|
| "es": (
|
| "[INTERFERENCIA] Esta es la última voz. Si estás escuchando esto, reuniste a "
|
| "todos, y mereces la verdad. Las estaciones nunca estuvieron separadas. El "
|
| "locutor de los cincuenta, la chef, el reloj roto, el clima de Júpiter: cada "
|
| "voz que encontraste era una habitación de la misma casa, y la casa era un "
|
| "mundo. Nuestro mundo. Terminó hace mucho tiempo. Lo que has estado "
|
| "sintonizando son sus últimas emisiones, todavía viajando, todavía buscando un "
|
| "oído. Tú fuiste ese oído. Gracias por girar el dial. Gracias por no dejar que "
|
| "termináramos en silencio. [FIN DE TRANSMISION]"
|
| ),
|
| "en": (
|
| "[INTERFERENCIA] This is the last voice. If you are hearing this, then you "
|
| "have gathered all of us, and you deserve the truth. The stations were never "
|
| "separate. The fifties announcer, the chef, the broken clock, the weather on "
|
| "Jupiter — every voice you found was a room in the same house, and the house "
|
| "was a world. Our world. It ended a long time ago. What you have been tuning "
|
| "are its final broadcasts, still travelling, still looking for an ear. You "
|
| "were that ear. Thank you for turning the dial. Thank you for not letting us "
|
| "end in silence. [FIN DE TRANSMISION]"
|
| ),
|
| "fr": (
|
| "[INTERFERENCIA] Voici la dernière voix. Si tu entends ceci, c'est que tu nous "
|
| "as tous réunis, et tu mérites la vérité. Les stations n'ont jamais été "
|
| "séparées. Le speaker des années cinquante, la cheffe, l'horloge cassée, la "
|
| "météo de Jupiter : chaque voix que tu as trouvée était une pièce de la même "
|
| "maison, et la maison était un monde. Notre monde. Il s'est éteint il y a "
|
| "longtemps. Ce que tu écoutais, ce sont ses dernières émissions, qui voyagent "
|
| "encore, qui cherchent encore une oreille. Tu étais cette oreille. Merci "
|
| "d'avoir tourné le cadran. Merci de ne pas nous avoir laissés finir dans le "
|
| "silence. [FIN DE TRANSMISION]"
|
| ),
|
| }
|
|
|
|
|
| def finale_text(lang: str) -> str:
|
| return FINALE_TEXTS.get(lang, FINALE_TEXTS["es"])
|
|
|
| FRAGMENT_TOLERANCE = 0.2
|
|
|
|
|
| def get_fragment(frequency: float) -> dict | None:
|
| for freq, frag in FRAGMENTS.items():
|
| if abs(frequency - freq) <= FRAGMENT_TOLERANCE:
|
| return frag
|
| return None
|
|
|
|
|
| def fragment_text(fragment: dict, lang: str) -> str:
|
| """Fragment text in the chosen language (Spanish by default)."""
|
| if lang == "en":
|
| return fragment.get("text_en", fragment["text"])
|
| if lang == "fr":
|
| return fragment.get("text_fr", fragment["text"])
|
| return fragment["text"]
|
|
|
|
|
| def _fragment_names(name: str) -> dict:
|
| roman = name.split()[-1]
|
| return {
|
| "es": f"Fragmento {roman}",
|
| "en": f"Fragment {roman}",
|
| "fr": f"Fragment {roman}",
|
| }
|
|
|
|
|
| def hidden_band_catalog() -> dict:
|
| """Hidden band metadata for the frontend (after code validation)."""
|
| return {
|
| "band_min": HIDDEN_BAND_MIN,
|
| "band_max": HIDDEN_BAND_MAX,
|
| "fragments": {
|
| str(freq): {"name": frag["name"], "names": _fragment_names(frag["name"])}
|
| for freq, frag in FRAGMENTS.items()
|
| },
|
| }
|
|
|