diff --git a/app.py b/app.py index b9c3ce9..82d1e36 100644 --- a/app.py +++ b/app.py @@ -173,16 +173,29 @@ def _ai_worker() -> None: # ── Emit response to originating client ─────────────────────────────── with _app_ref.app_context(): - db_user = db.session.get(User, task["user_id"]) - room = _pm_room(db_user.username, AI_BOT_NAME) - - socketio.emit("pm_message", { - "from": AI_BOT_NAME, - "ciphertext": resp_ct, - "nonce": resp_nonce, - "room": room, - "ts": _ts() - }, to=room) + if task.get("user_id"): + db_user = db.session.get(User, task["user_id"]) + room = _pm_room(db_user.username, AI_BOT_NAME) + else: + # Admin guest without a user_id — derive room from sid cache + uname = connected_users.get(sid, {}).get("username", "unknown") + room = _pm_room(uname, AI_BOT_NAME) + + if task.get("plaintext_mode"): + socketio.emit("pm_message", { + "from": AI_BOT_NAME, + "text": ai_text, + "room": room, + "ts": _ts() + }, to=room) + else: + socketio.emit("pm_message", { + "from": AI_BOT_NAME, + "ciphertext": resp_ct, + "nonce": resp_nonce, + "room": room, + "ts": _ts() + }, to=room) socketio.emit("violet_typing", {"busy": False, "room": room}, to=room) ai_queue.task_done() @@ -609,6 +622,22 @@ def on_pm_message(data): transit_key = data.get("transit_key", "") if not all([ciphertext, nonce_val, transit_key]): + # Plaintext fallback for admins without crypto keys + if text and user.get("is_admin"): + import base64 as _b64 + transit_key = _b64.b64encode(os.urandom(32)).decode() + ciphertext_new, nonce_new = aesgcm_encrypt(transit_key, text) + ai_queue.put({ + "sid": sid, + "user_id": user.get("user_id"), + "has_ai_access": user.get("has_ai_access", False), + "ai_messages_used": user.get("ai_messages_used", 0), + "ciphertext": ciphertext_new, + "nonce_val": nonce_new, + "transit_key": transit_key, + "plaintext_mode": True, + }) + return emit("error", {"msg": "AI Private Messaging requires transit encryption."}); return ai_queue.put({ diff --git a/config.py b/config.py index ddfdb08..1d3ffca 100644 --- a/config.py +++ b/config.py @@ -55,7 +55,7 @@ MAX_HISTORY = 500 # Ollama OLLAMA_URL = get_conf("OLLAMA_URL", "http://localhost:11434") -VIOLET_MODEL = get_conf("VIOLET_MODEL", "sam860/dolphin3-llama3.2:3b") +VIOLET_MODEL = get_conf("VIOLET_MODEL", "sadiq-bd/llama3.2-3b-uncensored:latest") VIOLET_SYSTEM = ( "You are Violet, a flirtatious and sophisticated nightclub hostess at " "an exclusive, dimly-lit members-only club. You are charming, witty, " diff --git a/static/chat.js b/static/chat.js index 7acf933..43ccd79 100644 --- a/static/chat.js +++ b/static/chat.js @@ -394,9 +394,7 @@ messageForm.addEventListener("submit", async (e) => { const isVioletRoom = state.currentRoom.toLowerCase().endsWith(":violet"); if (isVioletRoom) { - if (!state.isRegistered || !state.cryptoKey) { - addMessage(state.currentRoom, { system: true, text: "You must be logged in to chat with Violet." }); - } else { + if (state.isRegistered && state.cryptoKey) { // AI Transit Encryption PM Flow const transitKeyB64 = await SexyChato.exportKeyBase64(state.cryptoKey); const encrypted = await SexyChato.encrypt(state.cryptoKey, text); @@ -407,6 +405,9 @@ messageForm.addEventListener("submit", async (e) => { nonce: encrypted.nonce, transit_key: transitKeyB64 }); + } else { + // Guest/admin plaintext fallback + socket.emit("pm_message", { room: state.currentRoom, text }); } } else if (state.isRegistered && state.cryptoKey) { // User-to-user encrypted PM: use the shared room key if available