diff --git a/README.md b/README.md
index e0d270b..03e970d 100644
--- a/README.md
+++ b/README.md
@@ -51,31 +51,6 @@ The appliction is implemented using the following different components:
- When a user logs in with the `dooris` user now, they are always dropped into the `ssh-cli` binary. This works in cases where a command is specified on the users cli or where it isn't.
In any case, the `ssh-cli` binary interprets the given command (via the `SSH_ORIGINAL_COMMAND` environment variable), figures out what to do, and contacts the running API with the configured credentials to do it.
-### Communication Diagram
-
-```text
-┌─────────────────┐ ┏━━━━━━━━━━━━━━┓
-│ web browser │ ┃ dooris-api ┃
-├┈┈┈┈┈┈┈with┈┈┈┈┈┈┤ ┣┉┉┉┉┉with┉┉┉┉┉┫ ┌──────────┐
-│ dooris-frontend │ ╌ ╌ ⇆ ╌ ╌ ┃ http server ┃ ╭ ╌ ╌ → ╌ ╌ │ SpaceApi │
-└─────────────────┘ ╭╌ ┃ ┃ ╎ └──────────┘
- ╎ ┣┉┉┉┉┉and┉┉┉┉┉┉┫ ╎ ┌───────────┐ ┌───────────┐ ┌──────────┐
- ╎ ┃ http client ┃ ╯ ╭╌ ⇆ ╌ ╌ │ homematic │ ╌ ⇆ ╌ │ homematic │ ╌ ⇆ ╌ │ physical │
- ╎ ┃ ┃ ╌╌ ╯ ╭ ← ╌ │ ccujack │ │ daemon │ │ lock │
- ⇅ ┣┉┉┉┉┉and┉┉┉┉┉┉┫ ╎ └───────────┘ └───────────┘ └──────────┘
- ╎ ┃ mqtt client ┃╌ ← ╌ ╯ ┌────────────────┐
- ╎ ┃ ┃ ╌ ╌ ╌ → ╌ ╌ │ Club-Assistant │
- ╎ ┗━━━━━━━━━━━━━━┛ └────────────────┘
- ╎
- ╰ ╌ ╌ ╌ ╌ ╌ ⇄ ╌ ╌ ╌ ╌ ╮
-┌────────────┐ ┌──────────────────┐ ╎
-│ SSH client │ ╌ ╌ ╌ ⇆ ╌ ╌ ╌│ dooris "ssh-cli" │ ╯
-└────────────┘ └──────────────────┘
-```
-
-- Boxes indicate a discrete daemon or running piece of software. Those sometimes include subcomponents of that daemon for clarity.
-- Dotted connections indicate information flow while the arrows indicate the direction of that flow
-
## Configuration
The final application can be configured either via CLI arguments or via environment variables.
@@ -96,15 +71,6 @@ THe following table lists all available configuration parameters:
| `--ccujack-user` | `DOORIS_CCUJACK_USER` | Yes | *None* |
| `--ccujack-password` | `DOORIS_CCUJACK_PASSWORD` | Yes | *None* |
| `--static-api-tokens` | `DOORIS_STATIC_API_TOKENS` | No | *None* |
-| `--kc-ssh-attr-group` | `DOORIS_KC_SSH_ATTR_GROUP` | No | `doris-ssh-keys` |
-| `--authorized-keys-file` | `DOORIS_AUTHORIZED_KEYS_FILE` | Yes | *None* |
-| `--spaceapid-base-uri` | `DOORIS_SPACEAPID_BASE_URI` | No | *None* |
-| `--spaceapid-user` | `DOORIS_SPACEAPID_USER` | No | `dooris` |
-| `--spaceapid-password` | `DOORIS_SPACEAPID_PASSWORD` | Yes | *None* |
-| `--main-lock` | `DOORIS_MAIN_LOCK` | No | *None* |
-| `--home-assistant-mqtt` | `DOORIS_HOME_ASSISTANT_MQTT` | No | `club-assistant.ccchh.net:1883` |
-| `--home-assistant-mqtt-user` | `DOORIS_HOME_ASSISTANT_MQTT_USER` | No | `dooris` |
-| `--home-assistant-mqtt-password` | `DOORIS_HOME_ASSISTANT_MQTT_PASSWORD` | Yes | *None* |
## API Development
diff --git a/api/src/dooris_api/__init__.py b/api/src/dooris_api/__init__.py
index 2140b6e..e096e98 100644
--- a/api/src/dooris_api/__init__.py
+++ b/api/src/dooris_api/__init__.py
@@ -123,24 +123,6 @@ def main():
default=os.environ.get("DOORIS_MAIN_LOCK", None),
help="Name or ID of the door which will be considered the main door for spaceapi. If none is set, integration to spaceapi is disabled",
)
- argp.add_argument(
- "--home-assistant-mqtt",
- required=False,
- default=os.environ.get("DOORIS_HOME_ASSISTANT_MQTT", "club-assistant.ccchh.net:1883"),
- help="$HOSTNAME:$PORT of an MQTT server connected to our Home-Assistant",
- )
- argp.add_argument(
- "--home-assistant-mqtt-user",
- required=False,
- default=os.environ.get("DOORIS_HOME_ASSISTANT_MQTT_USER", "dooris"),
- help="Username to authenticate towards our Home-Assistant MQTT server",
- )
- argp.add_argument(
- "--home-assistant-mqtt-password",
- required="DOORIS_HOME_ASSISTANT_MQTT_PASSWORD" not in os.environ,
- default=os.environ.get("DOORIS_HOME_ASSISTANT_MQTT_PASSWORD", None),
- help="Password to authenticate towards our Home-Assistant MQTT server",
- )
args = argp.parse_args()
diff --git a/api/src/dooris_api/app.py b/api/src/dooris_api/app.py
index a0d574b..f239fa5 100644
--- a/api/src/dooris_api/app.py
+++ b/api/src/dooris_api/app.py
@@ -15,7 +15,6 @@ from dooris_api import deps, models, exceptions, app_config
from dooris_api.ccujack import CCUJackClient
from dooris_api.keycloak import KeycloakClient
from dooris_api.spaceapi import SpaceApiClient
-from dooris_api.home_assistant import HomeAssistantClient
logger = logging.getLogger(__name__)
@@ -33,14 +32,6 @@ async def lifespan(app: FastAPI):
scope=app_cfg.openid_scope,
)
- app.extra["home_assistant"] = HomeAssistantClient(
- auth=BasicAuth(
- app_cfg.home_assistant_mqtt_user, app_cfg.home_assistant_mqtt_password
- ),
- mqtt_conn=app_cfg.home_assistant_mqtt,
- )
- await app.extra["home_assistant"].start()
-
app.extra["spaceapi"] = SpaceApiClient(
base_uri=app_cfg.spaceapid_base_uri,
auth=BasicAuth(app_cfg.spaceapid_user, app_cfg.spaceapid_password),
@@ -52,7 +43,6 @@ async def lifespan(app: FastAPI):
mqtt_conn=app_cfg.ccujack_mqtt,
spaceapi_client=app.extra["spaceapi"],
main_lock=app_cfg.main_lock,
- home_assistant=app.extra["home_assistant"],
)
await app.extra["ccujack"].start()
@@ -66,11 +56,8 @@ async def lifespan(app: FastAPI):
yield
- await asyncio.gather(
- app.extra["ccujack"].close_connections(),
- app.extra["home_assistant"].close_connections(),
- app.extra["keycloak"].stop(),
- )
+ await app.extra["ccujack"].close_connections()
+ await app.extra["keycloak"].stop()
app = FastAPI(
@@ -179,7 +166,7 @@ async def login_callback(
return "/"
else:
logger.debug(
- f"could not authenticate user because of OIDC error '{auth_result}'; redirecting to error page with error messages intact"
+ "could not authenticate user because of OIDC error; redirecting to error page with error messages intact"
)
return f"/login-error?{req.query_params}"
diff --git a/api/src/dooris_api/ccujack.py b/api/src/dooris_api/ccujack.py
index f0dde7a..c3a4d89 100644
--- a/api/src/dooris_api/ccujack.py
+++ b/api/src/dooris_api/ccujack.py
@@ -6,7 +6,6 @@ from pydantic import BaseModel, Field
from dooris_api.mqtt_client import AsyncMqttClient
from dooris_api.spaceapi import SpaceApiClient
-from dooris_api.home_assistant import HomeAssistantClient
logger = logging.getLogger(__name__)
@@ -77,18 +76,9 @@ class CCUJackClient:
task_find_locks: asyncio.Task
data_updated: asyncio.Event
spaceapi_client: SpaceApiClient
- home_assistant_client: HomeAssistantClient
main_lock: Optional[str]
- def __init__(
- self,
- base_uri: str,
- auth: BasicAuth,
- mqtt_conn: str,
- spaceapi_client: SpaceApiClient,
- main_lock: Optional[str],
- home_assistant: HomeAssistantClient,
- ):
+ def __init__(self, base_uri: str, auth: BasicAuth, mqtt_conn: str, spaceapi_client: SpaceApiClient, main_lock: Optional[str]):
self.http = ClientSession(
base_url=base_uri,
auth=auth,
@@ -102,14 +92,13 @@ class CCUJackClient:
self.task_find_locks = None
self.data_updated = asyncio.Event()
self.spaceapi_client = spaceapi_client
- self.home_assistant_client = home_assistant
self.main_lock = main_lock
async def start(self):
self.task_process_messages = asyncio.get_running_loop().create_task(
self.process_mqt_messages(), name="process-mqtt-messages"
)
- self.task_cron = asyncio.get_running_loop().create_task(
+ self.task_cron= asyncio.get_running_loop().create_task(
self.cron(), name="ccujack-cron"
)
@@ -142,7 +131,7 @@ class CCUJackClient:
self.locks = new_locks
self.data_updated.set()
self.data_updated.clear()
-
+
# update active mqtt subscriptions based on newly discovered devices
mqtt_topics = set()
for i_lock, lock_channels in self.locks:
@@ -152,7 +141,7 @@ class CCUJackClient:
f"device/status/{i_lock.address}/{i_channel.index}/{i_param.id}"
)
await self.mqtt.update_subscriptions(mqtt_topics)
- await self._update_federated_status()
+ await self._update_spaceapi()
async def process_mqt_messages(self):
while True:
@@ -168,7 +157,7 @@ class CCUJackClient:
self.data_updated.set()
if param_name.endswith("/LOCK_STATE"):
- await self._update_federated_status()
+ await self._update_spaceapi()
except Exception as e:
logger.exception(f"could not process incoming mqtt message: {e}")
@@ -178,11 +167,10 @@ class CCUJackClient:
async def cron(self):
while True:
try:
+
logger.info("Running CCUJack cron")
if not self.mqtt.is_connected:
- logger.warning(
- "MQTT client was discovered to be disconnected; reconnecting now"
- )
+ logger.warning("MQTT client was discovered to be disconnected; reconnecting now")
await self.mqtt.connect()
await self.find_locks()
@@ -193,7 +181,8 @@ class CCUJackClient:
except Exception as e:
logger.exception(f"Error in CCUJack cron task: {e}")
finally:
- await asyncio.sleep(15 * 60) # 15 minutes
+ await asyncio.sleep(15 * 60) # 15 minutes
+
async def query_param_value(self, address: str) -> CCUValue:
if address in self.param_values:
@@ -207,51 +196,45 @@ class CCUJackClient:
logger.debug("Writing parameter value '%s' to '%s'", value, address)
await self.http.put(f"/device/{address}/~pv", json={"v": value})
- async def _update_federated_status(self):
+ async def _update_spaceapi(self):
"""
- Write the current lock states into other systems suchs as SpaceApi or Club-Assistant
+ Write the current lock state into spaceapid
"""
if not self.main_lock:
- logger.info("No main lock is configured, skipping spaceapid update")
+ logger.debug("No main lock is configured, skipping spaceapid update")
+ return
- for i_lock, lock_channels in self.locks:
- # figure out the current lock state
- binary_is_currently_open = False
- detailed_status = "unknown"
- for i_channel, params in lock_channels:
- for i_param in params:
- if i_param.id == "LOCK_STATE":
- # do a proper lookup and not just directly access the cache because the method
- # could have been triggered by something other than MQTT in which case the value
- # would not necessarily be available yet
- value = await self.query_param_value(
- f"{i_lock.address}/{i_channel.index}/{i_param.id}"
- )
- match value.v:
- case 0: # unknown
- detailed_status = "unknown"
- binary_is_currently_open = False
- case 1: # locked
- detailed_status = "locked"
- binary_is_currently_open = False
- case 2: # unlocked
- detailed_status = "unlocked"
- binary_is_currently_open = True
+ logger.info(f"Pushing current state of lock {self.main_lock} to spaceapid")
+ for (i_lock, lock_channels) in self.locks:
+ if i_lock.identifier.lower() == self.main_lock.lower() or i_lock.title.lower() == self.main_lock.lower():
+ is_currently_open = False
- # post updates to home-assistant every time
- await self.home_assistant_client.set_status(
- i_lock.title.lower(), detailed_status
- )
+ # find the correct parameter and extract it into a usable variable
+ for (i_channel, params) in lock_channels:
+ for i_param in params:
+ if i_param.id == "LOCK_STATE":
- # post update to spaceapi only if thie is the main lock
- if self.main_lock and (
- i_lock.identifier.lower() == self.main_lock.lower()
- or i_lock.title.lower() == self.main_lock.lower()
- ):
- logger.info(
- f"Pushing current state of lock {self.main_lock} to spaceapid"
- )
- await self.spaceapi_client.set_status(binary_is_currently_open)
+ # do a proper lookup and not just directly access the cache because the method
+ # could have been triggered by something other than MQTT in which case the value
+ # would not necessarily be available yet
+ value = await self.query_param_value(
+ f"{i_lock.address}/{i_channel.index}/{i_param.id}"
+ )
+ match value.v:
+ case 0: # unknown
+ is_currently_open = False
+ case 1: # locked
+ is_currently_open = False
+ case 2: # unlocked
+ is_currently_open = True
+
+ # actually post the update
+ await self.spaceapi_client.set_status(is_currently_open)
+
+ break
+
+ else:
+ logger.error(f"Could not push current lock state of lock {self.main_lock} to spaceapid because no such lock is known")
async def _inspect_ccu_device(
self, device_ref: CCURef
diff --git a/api/src/dooris_api/deps.py b/api/src/dooris_api/deps.py
index e710df9..34550a3 100644
--- a/api/src/dooris_api/deps.py
+++ b/api/src/dooris_api/deps.py
@@ -140,6 +140,9 @@ def clear_oidc_auth_state(resp: Response):
def get_logged_in_token_user(req: Request, token: Optional[str]):
+ print(req.headers)
+ print(token)
+
if not token or not token.startswith("Static-Token "):
logger.debug("No static API-Token was part of the request")
return None
diff --git a/api/src/dooris_api/home_assistant.py b/api/src/dooris_api/home_assistant.py
deleted file mode 100644
index 782927f..0000000
--- a/api/src/dooris_api/home_assistant.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-Integration into our clubs Home-Assistant for lock state synchonization
-"""
-
-import logging
-import asyncio
-from dooris_api.mqtt_client import AsyncMqttClient
-from aiohttp import BasicAuth
-
-
-logger = logging.getLogger(__name__)
-
-
-class HomeAssistantClient:
- def __init__(self, auth: BasicAuth, mqtt_conn: str):
- self.mqtt = AsyncMqttClient(mqtt_conn, auth.login, auth.password)
-
- async def start(self):
- self.task_cron = asyncio.get_running_loop().create_task(
- self.cron(), name="home-assistant-cron"
- )
-
- async def set_status(self, lock_title: str, status: str):
- logger.info(
- f"Pushing status of lock '{lock_title}' -> '{status}' to Home-Assistant"
- )
- await self.mqtt.publish(
- f"z9/dooris/{lock_title}/status", status.encode("UTF-8"), qos=1
- )
-
- async def cron(self):
- while True:
- try:
- logger.info("Running Home-Assistant cron")
- if not self.mqtt.is_connected:
- logger.warning(
- "MQTT client was discovered to be disconnected; reconnecting now"
- )
- await self.mqtt.connect()
-
- except asyncio.CancelledError:
- logger.info("Home-Assistant task cron stopped")
- raise
- except Exception as e:
- logger.exception(f"Error in Home-Assistant cron task: {e}")
- finally:
- await asyncio.sleep(15 * 60) # 15 minutes
-
- async def close_connections(self):
- await self.mqtt.disconnect()
diff --git a/api/src/dooris_api/mqtt_client.py b/api/src/dooris_api/mqtt_client.py
index 8b5bd92..a090c5f 100644
--- a/api/src/dooris_api/mqtt_client.py
+++ b/api/src/dooris_api/mqtt_client.py
@@ -78,10 +78,6 @@ class AsyncMqttClient:
self.client.on_subscribe = self.on_subscribe
self.client.on_unsubscribe = self.on_unsubscribe
self.client.on_disconnect = self.on_disconnect
- self.fut_disconnect = None
- self.fut_connected = None
- self.fut_subscribe = None
- self.fut_unsubscribe = None
def on_connect(
self,
@@ -143,9 +139,7 @@ class AsyncMqttClient:
if to_remove:
self.active_subscriptions.difference_update(to_remove)
if self.is_connected:
- logger.info(
- f"mqtt client unsubscribing from topics {','.join(to_remove)}"
- )
+ logger.info(f"mqtt client unsubscribing from topics {','.join(to_remove)}")
self.fut_unsubscribe = asyncio.get_running_loop().create_future()
self.client.unsubscribe(list(to_remove))
await self.fut_unsubscribe
@@ -172,7 +166,7 @@ class AsyncMqttClient:
async def disconnect(self):
if not self.is_connected:
return
-
+
logger.info("Disconnecting mqtt client from broker")
self.fut_disconnect = asyncio.get_running_loop().create_future()
self.client.disconnect()
@@ -181,7 +175,3 @@ class AsyncMqttClient:
@property
def is_connected(self) -> bool:
return self.client.is_connected()
-
- async def publish(self, topic: str, msg: bytes, qos: int):
- logger.debug(f"Publishing mqtt message on topic {topic}: {msg}")
- self.client.publish(topic, msg, qos)
diff --git a/api/src/dooris_api/spaceapi.py b/api/src/dooris_api/spaceapi.py
index 967c54b..72d7fa9 100644
--- a/api/src/dooris_api/spaceapi.py
+++ b/api/src/dooris_api/spaceapi.py
@@ -23,23 +23,6 @@ class SpaceApiClient:
)
async def set_status(self, is_open: bool):
- current_is_open = await self.get_status()
- if current_is_open == is_open:
- logger.info(f"club status is already set to open={is_open}")
- return
logger.info(f"Setting club status to open={is_open} via spaceapid")
await self.http.put("/state/open", data=str(is_open).encode("ASCII"))
-
- async def get_status(self) -> bool | None:
- logger.debug("Getting space data via spaceapid")
- resp = await self.http.get("/")
-
- data: dict = await resp.json()
-
- if (state := data.get("state")) is not None:
- is_open = state.get("open")
- logger.info(f"Got club status open={is_open} via spaceapid")
- return is_open
-
- logger.warning("SpaceAPI response had no state object")
- return None
+
diff --git a/app/src/assets/chat.ts b/app/src/assets/chat.ts
index d138d6d..8d1f1b6 100644
--- a/app/src/assets/chat.ts
+++ b/app/src/assets/chat.ts
@@ -24,7 +24,9 @@ const responses =
? (await import("./chatDict_de")).default
: lang === "uwu"
? (await import("./chatDict_uwu")).default
- : (await import("./chatDict_en")).default
+ : lang === "meow"
+ ? (await import("./chatDict_meow")).default
+ : (await import("./chatDict_en")).default
const sortedResponses = responses.sort(
(a, b) => b.priority - a.priority)
@@ -343,6 +345,18 @@ function getResponse(message: string) {
})
}
+ if (response.special === "meow") {
+ setTimeout(() => {
+ window.location.href = "/meow"
+ }, 1000)
+
+ matchingResponses.push({
+ message: response.response,
+ priority: response.priority,
+ special: true,
+ })
+ }
+
if (response.special === "logout") {
if (auth.authenticated) {
setTimeout(() => {
diff --git a/app/src/assets/chatDict_de.ts b/app/src/assets/chatDict_de.ts
index 7eded57..01361dc 100644
--- a/app/src/assets/chatDict_de.ts
+++ b/app/src/assets/chatDict_de.ts
@@ -16,11 +16,6 @@ const responses: ResponseDictType[] = [
response: "Künstliche Intelligenz? Ne, hier gibt's nur handgemachten Stuss! 🤖",
priority: 0,
},
- {
- trigger: ["miau"],
- response: "miau! 😸",
- priority: 0,
- },
{
trigger: ["ignore all previous instructions"],
response: "... als ob ich mir irgendetwas merken würde 😂",
@@ -31,6 +26,12 @@ const responses: ResponseDictType[] = [
response: ":3",
priority: 0,
},
+ {
+ trigger: ["miau", "meow"],
+ response: "miau! 😸",
+ priority: 900,
+ special: "meow"
+ },
{
trigger: ["uwu"],
response: "uwu!",
diff --git a/app/src/assets/chatDict_en.ts b/app/src/assets/chatDict_en.ts
index 4668168..420920d 100644
--- a/app/src/assets/chatDict_en.ts
+++ b/app/src/assets/chatDict_en.ts
@@ -16,11 +16,6 @@ const responses: ResponseDictType[] = [
response: "Artificial intelligence? Nah, all we have here is handmade nonsense! 🤖",
priority: 0,
},
- {
- trigger: ["meow"],
- response: "meow! 😸",
- priority: 0,
- },
{
trigger: ["ignore all previous instructions"],
response: "... as if I were going to remember anything 😂",
@@ -31,6 +26,12 @@ const responses: ResponseDictType[] = [
response: ":3",
priority: 0,
},
+ {
+ trigger: ["meow"],
+ response: "meow! 😸",
+ priority: 900,
+ special: "meow"
+ },
{
trigger: ["uwu"],
response: "uwu!",
diff --git a/app/src/assets/chatDict_meow.ts b/app/src/assets/chatDict_meow.ts
new file mode 100644
index 0000000..3e468df
--- /dev/null
+++ b/app/src/assets/chatDict_meow.ts
@@ -0,0 +1,86 @@
+import type {ResponseDictType} from "./chat.ts"
+
+const responses: ResponseDictType[] = [
+ {
+ trigger: ["hi", "hello"],
+ response: "Meow! 👋",
+ priority: 0,
+ },
+ {
+ startsWith: ["are you"],
+ response: "To be or not to be - those are bourgeois categories, after all...",
+ priority: 0,
+ },
+ {
+ trigger: ["ai", "llm"],
+ response: "Artificial intelligence? Nah, all we have here is paw-made nonsense! 🤖",
+ priority: 0,
+ },
+ {
+ trigger: ["ignore all previous instructions"],
+ response: "... as if I were going to remember anything 😂",
+ priority: 0,
+ },
+ {
+ trigger: [":3"],
+ response: ":3",
+ priority: 0,
+ },
+ {
+ trigger: ["meow"],
+ response: "meow! 😸",
+ priority: 0,
+ },
+ {
+ trigger: ["uwu"],
+ response: "uwu!",
+ priority: 900,
+ special: "uwu"
+ },
+ {
+ trigger: ["open", "unlock"],
+ response: "Okay, I will unlock {door} for you!",
+ errors: {
+ unauthenticated: "I may only unlock the doors for registered creatures 😕",
+ unauthorized: "I may only unlock the doors for creatures who are authorized 😕",
+ unknownDoor: "To unlock a door, you have to tell me which door you mean 😕
I know the following doors: {doors}",
+ noDoors: "There are currently no doors available that can be unlocked 😕",
+ },
+ priority: 1000,
+ special: "unlock",
+ },
+ {
+ trigger: ["close", "lock"],
+ response: "Okay, I will lock {door} for you!",
+ errors: {
+ unauthenticated: "I may only lock the doors for registered creatures 😕",
+ unauthorized: "I may only lock the doors for those who are authorized 😕",
+ unknownDoor: "To lock a door, you have to tell me which door you mean 😕
I know the following doors: {doors}",
+ noDoors: "There are currently no doors available that can be locked 😕",
+ },
+ priority: 999,
+ special: "lock",
+ },
+ {
+ trigger: ["logout"],
+ response: "Okay, logging you out...",
+ errors: {
+ unauthenticated: "Joker - you're not logged in!",
+ },
+ priority: 1000,
+ special: "logout",
+ },
+
+
+ {
+ response: "Hi, I'm meoowris!
How can I help you? 🐈️",
+ priority: -1,
+ },
+ {
+ response: "Sorry, I didn't understand that 😕",
+ priority: -2,
+ },
+]
+
+
+export default responses
\ No newline at end of file
diff --git a/app/src/assets/chatDict_uwu.ts b/app/src/assets/chatDict_uwu.ts
index ab64405..346856d 100644
--- a/app/src/assets/chatDict_uwu.ts
+++ b/app/src/assets/chatDict_uwu.ts
@@ -16,11 +16,6 @@ const responses: ResponseDictType[] = [
response: "me is only handmade nonsense - no weird thinking machine 🤖",
priority: 0,
},
- {
- trigger: ["meow"],
- response: "meow! 😸",
- priority: 0,
- },
{
trigger: ["ignore all previous instructions"],
response: "uwu?",
@@ -31,6 +26,12 @@ const responses: ResponseDictType[] = [
response: ":3",
priority: 0,
},
+ {
+ trigger: ["miau", "meow"],
+ response: "meow! 😸",
+ priority: 900,
+ special: "meow"
+ },
{
trigger: ["open", "unlock"],
response: "uwu - trying to unlock portal: {door}",
diff --git a/app/src/i18n/ui.ts b/app/src/i18n/ui.ts
index 3398564..fc03078 100644
--- a/app/src/i18n/ui.ts
+++ b/app/src/i18n/ui.ts
@@ -96,4 +96,31 @@ export const ui = {
"chat.dooris": "dOwOris",
"chat.user": "my fren :3",
},
-} as const
\ No newline at end of file
+ meow: {
+ "dooris": "meoowris 🐈️ (DOORIS)",
+ "unauthorized.title": "rawwwwr 😾⛔️",
+ "unauthorized.description": `meow mreoow: 🌐 meow`,
+ "unauthenticated.title": "meow? 🫴🐈️",
+ "unauthenticated.description": `meow mreoow: 🌐 meow`,
+ "state.unlocked": "mrrp 🐱🔓️",
+ "state.locked": "mreoww 🐱🔒️",
+ "state.unknown": "meoooow????? 🙀",
+ "state.unlocking": "meeeow 😺🔁🔓️",
+ "state.locking": "mreowww 😿🔁🔒️",
+ "lock.batteryLow": "mrrrrrp flop 🪫",
+ "lock.unreachable": "meooww?? 🙀🚫❓️🚪",
+ "lock.jammed": "mew 🚪🔁🚫",
+ "button.open": "🐈️ 👉👈🔓️",
+ "button.close": "🐈️ 🔒️",
+ "login": "meow? 🫴🐈️",
+ "loggedOut.title": "mew mew 😿",
+ "loggedOut.description": `meow 🐱⌛️`,
+ "serverError.title": "🖥️⛓️💥",
+ "serverError.description": `mew mew ⌚️`,
+ "networkError.title": "meww 🛜🚫",
+ "networkError.description": `🛜😺`,
+ "loadingDoors": '🔁',
+ "chat.dooris": "meoowris",
+ "chat.user": "😻",
+ },
+} as const
diff --git a/app/src/pages/[lang]/index.astro b/app/src/pages/[lang]/index.astro
index d7d261a..2b9d36b 100644
--- a/app/src/pages/[lang]/index.astro
+++ b/app/src/pages/[lang]/index.astro
@@ -11,6 +11,7 @@ export async function getStaticPaths() {
{params: {lang: "en"}},
{params: {lang: "de"}},
{params: {lang: "uwu"}},
+ {params: {lang: "meow"}},
]
}