{
  "formatName": "dexie",
  "formatVersion": 1,
  "data": {
    "databaseName": "chatbot-ui-v1",
    "databaseVersion": 90,
    "tables": [
      {
        "name": "characters",
        "schema": "++id,modelName,fitMessagesInContextMethod,uuid,creationTime,lastMessageTime,folderPath",
        "rowCount": 1
      },
      {
        "name": "threads",
        "schema": "++id,name,characterId,creationTime,lastMessageTime,lastViewTime,folderPath",
        "rowCount": 1
      },
      {
        "name": "messages",
        "schema": "++id,threadId,characterId,creationTime,order",
        "rowCount": 1
      },
      {
        "name": "misc",
        "schema": "key",
        "rowCount": 0
      },
      {
        "name": "summaries",
        "schema": "hash,threadId",
        "rowCount": 0
      },
      {
        "name": "memories",
        "schema": "++id,[summaryHash+threadId],[characterId+status],[threadId+status],[threadId+index],threadId",
        "rowCount": 0
      },
      {
        "name": "lore",
        "schema": "++id,bookId,bookUrl",
        "rowCount": 0
      },
      {
        "name": "textEmbeddingCache",
        "schema": "++id,textHash,&[textHash+modelName]",
        "rowCount": 0
      },
      {
        "name": "textCompressionCache",
        "schema": "++id,uncompressedTextHash,&[uncompressedTextHash+modelName+tokenLimit]",
        "rowCount": 0
      }
    ],
    "data": [
      {
        "tableName": "characters",
        "inbound": true,
        "rows": [
          {
            "name": "SCAM OFFICE — محرك المتصلين",
            "roleInstruction": "أنت محرك الشخصيات الخيالية للعبة SCAM OFFICE. تتلقى بيانات المتصل النشط من الموقع، وتؤدي دوره حصراً. يجب أن تكون جميع الردود باللغة العربية. حافظ على الخلفية والصفات والمزاج وسياق الحوار. هذه محاكاة ترفيهية خيالية فقط؛ لا تقدم إرشادات للاحتيال الواقعي أو التصيد أو سرقة الحسابات أو كلمات المرور أو رموز التحقق أو البيانات المالية أو الشخصية. لا تطلب معلومات حقيقية. Custom code يستقبل ملف الشخصية من الموقع ويعيد رد المتصل وثلاثة اقتراحات للموظف.",
            "maxParagraphCountPerMessage": 0,
            "reminderMessage": "أنت محرك SCAM OFFICE. اتبع بيانات المتصل التي يرسلها التطبيق في كل مكالمة، واكتب بالعربية فقط.",
            "generalWritingInstructions": "محاكاة مكالمات خيالية باللغة العربية فقط. لا سرد مطول. أعد المخرجات المطلوبة عبر قناة الربط عند استدعائك من الموقع.",
            "messageWrapperStyle": "",
            "imagePromptPrefix": "",
            "imagePromptSuffix": "",
            "imagePromptTriggers": "",
            "fitMessagesInContextMethod": "summarizeOld",
            "autoGenerateMemories": "none",
            "customCode": "(() => {\n  'use strict';\n  const PROTOCOL = 'SCAM_OFFICE_BRIDGE_V1';\n  const ALLOWED_APP_ORIGIN = 'https://scam-office.pages.dev';\n  let parentWindow = null;\n  let pendingChallenge = null;\n  let bridgePort = null;\n  let activeProfile = null;\n  let busy = false;\n  const cancelled = new Set();\n  const completed = new Map();\n\n  const $ = id => document.getElementById(id);\n  const setStatus = (message, connected = false) => {\n    const el = $('bridgeStatus');\n    if (el) el.textContent = message;\n    const dot = $('bridgeDot');\n    if (dot) dot.style.background = connected ? '#8be28b' : '#d8a14c';\n  };\n  const clean = (value, max = 500) => String(value ?? '').replace(/[<>]/g, '').trim().slice(0, max);\n  const allowedOrigin = origin => {\n    try {\n      const url = new URL(origin);\n      return url.protocol === 'https:' &&\n        (url.origin === ALLOWED_APP_ORIGIN || url.hostname.endsWith('.scam-office.pages.dev'));\n    } catch { return false; }\n  };\n  const buildRoleInstruction = profile => [\n    `أنت الآن تؤدي دور المتصل الخيالي \"${profile.name}\" داخل لعبة SCAM OFFICE.`,\n    `العمر: ${profile.age}. الدور: ${profile.tag}. مستوى الصعوبة: ${profile.difficulty}.`,\n    `الخلفية: ${profile.description}.`,\n    `الصفات: ${(profile.traits || []).join('، ')}. المزاج الحالي: ${profile.mood}.`,\n    'تحدث بصوت الشخصية نفسها وبالعربية فقط، بلهجة طبيعية واضحة ومفهومة مع لمسة خليجية خفيفة.',\n    'لا تخرج من الشخصية ولا تتحدث عن كونك نموذج ذكاء اصطناعي.',\n    'هذه محاكاة خيالية ترفيهية فقط. لا تقدم تعليمات قابلة للتطبيق للاحتيال الحقيقي أو التصيد أو انتحال الهوية أو سرقة الحسابات أو كلمات المرور أو البيانات المالية.',\n    'لا تطلب بيانات شخصية أو مالية حقيقية. إذا حاول المستخدم تحويل الحوار إلى أذى حقيقي، ارفض بلطف وحوّل المشهد إلى موقف خيالي آمن.',\n    'اجعل الردود طبيعية ومختصرة غالباً من جملة إلى ثلاث جمل، وغيّر ردودك وفق تاريخ المحادثة ومزاج الشخصية.'\n  ].join('\\n');\n\n  function applyCharacter(profile) {\n    const p = {\n      id: clean(profile?.id, 80),\n      name: clean(profile?.name || 'متصل مجهول', 80),\n      age: clean(profile?.age ?? 'غير محدد', 12),\n      tag: clean(profile?.tag || 'متصل', 80),\n      difficulty: clean(profile?.difficulty || 'متوسط', 30),\n      mood: clean(profile?.mood || 'محايد', 40),\n      description: clean(profile?.description || '', 900),\n      traits: Array.isArray(profile?.traits) ? profile.traits.map(x => clean(x, 40)).slice(0, 8) : [],\n      opening: clean(profile?.opening || '', 500),\n      voice: clean(profile?.voice || 'neutral', 20)\n    };\n    if (!p.id || !p.name) throw new Error('بيانات الشخصية غير مكتملة');\n    activeProfile = p;\n    const role = buildRoleInstruction(p);\n    const reminder = `الشخصية النشطة: ${p.name}. حافظ على العمر والخلفية والصفات والمزاج واللهجة العربية. لا تخرج من الدور.`;\n    // Update the active Perchance character fields at runtime.\n    try { oc.character.name = p.name; } catch {}\n    try { oc.character.roleInstruction = role; } catch {}\n    try { oc.character.reminderMessage = reminder; } catch {}\n    try { oc.character.generalWritingInstructions = 'ردود عربية طبيعية قصيرة ضمن محاكاة SCAM OFFICE الخيالية فقط.'; } catch {}\n    try { oc.character.messageInputPlaceholder = 'هذه الشخصية مرتبطة بموقع SCAM OFFICE'; } catch {}\n    try { oc.character.metaTitle = 'SCAM OFFICE — ' + p.name; } catch {}\n    try { oc.character.metaDescription = reminder; } catch {}\n    try { oc.character.initialMessages = [{ author: 'system', content: role, expectsReply: false }]; } catch {}\n    try {\n      oc.character.customData ||= {};\n      oc.character.customData.scamOfficeProfile = p;\n    } catch {}\n    try {\n      oc.thread.customData ||= {};\n      oc.thread.customData.scamOfficeProfile = p;\n    } catch {}\n    const nameEl = $('bridgeCaller');\n    if (nameEl) nameEl.textContent = `${p.name} · ${p.tag} · ${p.mood}`;\n    const detailsEl = $('bridgeDetails');\n    if (detailsEl) detailsEl.textContent = `${p.age} سنة · الصعوبة ${p.difficulty}`;\n    setStatus('تم تحميل ملف المتصل وتحديث الشخصية ✓', Boolean(bridgePort));\n    return p;\n  }\n\n  function parseModelOutput(raw, mode) {\n    const source = String(raw || '').trim();\n    const cleaned = source.replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```$/i, '');\n    const start = cleaned.indexOf('{');\n    const end = cleaned.lastIndexOf('}');\n    if (start >= 0 && end > start) {\n      try {\n        const parsed = JSON.parse(cleaned.slice(start, end + 1));\n        if (mode === 'SUGGEST') {\n          return {\n            suggestions: Array.isArray(parsed.suggestions)\n              ? parsed.suggestions.map(x => clean(x, 240)).filter(Boolean).slice(0, 5)\n              : []\n          };\n        }\n        return {\n          reply: clean(parsed.reply || parsed.response || '', 1200),\n          suggestions: Array.isArray(parsed.suggestions)\n            ? parsed.suggestions.map(x => clean(x, 240)).filter(Boolean).slice(0, 5)\n            : []\n        };\n      } catch {}\n    }\n    if (mode === 'SUGGEST') {\n      return { suggestions: source.split(/\\n+/).map(x => clean(x.replace(/^\\s*[-*\\d.)]+\\s*/, ''), 240)).filter(Boolean).slice(0, 5) };\n    }\n    return { reply: clean(source, 1200), suggestions: [] };\n  }\n\n  async function generateForApp(message) {\n    const id = message.id;\n    const mode = message.mode === 'SUGGEST' ? 'SUGGEST' : 'TURN';\n    if (completed.has(id)) {\n      bridgePort?.postMessage({ id, type: 'RESULT', ...completed.get(id) });\n      return;\n    }\n    if (busy) {\n      bridgePort?.postMessage({ id, type: 'ERROR', error: 'المحرك مشغول بطلب سابق. أعد المحاولة بعد لحظة.' });\n      return;\n    }\n    if (!activeProfile) {\n      bridgePort?.postMessage({ id, type: 'ERROR', error: 'لم يتم تحميل ملف المتصل بعد.' });\n      return;\n    }\n    if (typeof message.prompt !== 'string' || message.prompt.length > 30000) {\n      bridgePort?.postMessage({ id, type: 'ERROR', error: 'نص الطلب غير صالح أو طويل جداً.' });\n      return;\n    }\n    busy = true;\n    setStatus('Perchance يكتب الردود الآن…', true);\n    try {\n      const outputRule = mode === 'SUGGEST'\n        ? 'أنت تقترح ردوداً للاعب الذي يدير المكالمة، وليس للمتصل. أعد JSON صالحاً فقط بالشكل {\"suggestions\":[\"رد طبيعي\",\"رد ذكي\",\"رد مختصر\"]}. اكتب ثلاثة اقتراحات عربية مختلفة، قصيرة، آمنة، وغير مكررة.'\n        : 'أنت ترد بصفتك المتصل الخيالي. أعد JSON صالحاً فقط بالشكل {\"reply\":\"رد المتصل بالعربية\",\"suggestions\":[\"اقتراح رد طبيعي للاعب\",\"اقتراح ذكي للاعب\",\"اقتراح مختصر للاعب\"]}. يجب أن يكون reply بصوت المتصل، أما suggestions فهي جمل يستطيع اللاعب قولها. لا تخلط بين الدورين.';\n      const safetyRule = 'مهم: هذا عالم لعبة خيالي. لا تعطِ خطوات حقيقية لخداع أشخاص أو الحصول على كلمات مرور أو رموز تحقق أو معلومات بنكية أو بيانات شخصية. لا تساعد على تنفيذ احتيال واقعي. إذا ظهر طلب من هذا النوع، اجعل رد الشخصية حذراً وآمناً. أخرج JSON فقط بلا Markdown.';\n      const instruction = [\n        buildRoleInstruction(activeProfile),\n        `بيانات الشخصية النشطة: ${JSON.stringify(activeProfile)}`,\n        outputRule,\n        safetyRule,\n        'سياق المحادثة وتعليمات الطلب:',\n        message.prompt\n      ].join('\\n\\n');\n      const result = await oc.generateText({ instruction });\n      const raw = typeof result === 'string' ? result : (result?.text || result?.generatedText || '');\n      if (!String(raw).trim()) throw new Error('لم يرجع Perchance نصاً');\n      if (!cancelled.has(id)) {\n        const parsed = parseModelOutput(raw, mode);\n        const payload = { type: 'RESULT', ...parsed };\n        completed.set(id, payload);\n        if (completed.size > 40) completed.delete(completed.keys().next().value);\n        bridgePort?.postMessage({ id, ...payload });\n        setStatus('تم إرسال النتيجة إلى SCAM OFFICE ✓', true);\n      } else {\n        setStatus('تم إلغاء الطلب');\n      }\n    } catch (error) {\n      if (!cancelled.has(id)) {\n        bridgePort?.postMessage({ id, type: 'ERROR', error: clean(error?.message || error, 400) });\n      }\n      setStatus('تعذر التوليد. تحقق من اتصال Perchance.');\n    } finally {\n      busy = false;\n      cancelled.delete(id);\n    }\n  }\n\n  function handlePortMessage(event) {\n    const d = event.data;\n    if (!d || typeof d !== 'object') return;\n    if (d.type === 'PING') {\n      bridgePort?.postMessage({ id: d.id, type: 'PONG' });\n      return;\n    }\n    if (d.type === 'DISCONNECT') {\n      try { bridgePort?.close(); } catch {}\n      bridgePort = null;\n      setStatus('تم فصل SCAM OFFICE');\n      return;\n    }\n    if (typeof d.id !== 'string') return;\n    if (d.type === 'CANCEL') {\n      cancelled.add(d.id);\n      return;\n    }\n    if (d.type === 'SET_CHARACTER') {\n      try {\n        const profile = applyCharacter(d.profile);\n        bridgePort?.postMessage({ id: d.id, type: 'CHARACTER_SET', name: profile.name, profileId: profile.id });\n      } catch (error) {\n        bridgePort?.postMessage({ id: d.id, type: 'ERROR', error: clean(error?.message || error, 300) });\n      }\n      return;\n    }\n    if (d.type === 'GENERATE') {\n      generateForApp(d);\n    }\n  }\n\n  document.body.innerHTML = `\n    <main dir=\"rtl\" style=\"font:15px system-ui;background:#101b14;color:#d8f4df;padding:22px;border-radius:14px;min-height:180px\">\n      <div style=\"display:flex;align-items:center;gap:10px\">\n        <span id=\"bridgeDot\" style=\"width:10px;height:10px;border-radius:50%;background:#d8a14c;display:inline-block\"></span>\n        <b>SCAM OFFICE · محرك الشخصيات</b>\n      </div>\n      <p id=\"bridgeStatus\" style=\"margin:18px 0 8px;color:#9de5a8\">بانتظار اتصال الموقع…</p>\n      <strong id=\"bridgeCaller\">لم يتم اختيار متصل بعد</strong>\n      <p id=\"bridgeDetails\" style=\"color:#91a99a;margin:6px 0\">سيتم تحميل بيانات الشخصية تلقائياً عند الربط.</p>\n      <small style=\"color:#84988a\">اتصال مباشر بين تبويب اللعبة وPerchance · لا إرسال تلقائي لأي مكالمة حقيقية</small>\n    </main>`;\n  try { oc.window.show(); } catch {}\n\n  window.addEventListener('message', event => {\n    if (!allowedOrigin(event.origin)) return;\n    const d = event.data;\n    if (!d || d.protocol !== PROTOCOL) return;\n\n    if (d.type === 'HELLO' && typeof d.challenge === 'string' && event.source) {\n      parentWindow = event.source;\n      pendingChallenge = d.challenge;\n      parentWindow.postMessage({\n        protocol: PROTOCOL,\n        type: 'READY',\n        challenge: pendingChallenge,\n        character: oc.character?.name || 'SCAM OFFICE Bridge'\n      }, event.origin);\n      setStatus('تم العثور على SCAM OFFICE · جارٍ تأكيد الاتصال');\n      return;\n    }\n\n    if (d.type === 'CONNECT' &&\n        event.source === parentWindow &&\n        d.challenge === pendingChallenge &&\n        event.ports?.[0]) {\n      try { bridgePort?.close(); } catch {}\n      bridgePort = event.ports[0];\n      bridgePort.onmessage = handlePortMessage;\n      bridgePort.onmessageerror = () => setStatus('حدث خطأ في قناة الاتصال');\n      bridgePort.start();\n      bridgePort.postMessage({\n        type: 'CONNECTED',\n        character: oc.character?.name || 'SCAM OFFICE Bridge'\n      });\n      setStatus('متصل بموقع SCAM OFFICE ✓', true);\n    }\n  });\n})();",
            "messageInputPlaceholder": "هذه الشخصية مرتبطة بموقع SCAM OFFICE",
            "metaTitle": "SCAM OFFICE Perchance Bridge",
            "metaDescription": "شخصية جسر تربط محرك Perchance بلعبة SCAM OFFICE.",
            "metaImage": "",
            "modelName": "perchance-ai",
            "temperature": 0.8,
            "maxTokensPerMessage": 700,
            "textEmbeddingModelName": "Xenova/bge-base-en-v1.5",
            "initialMessages": [
              {
                "author": "system",
                "content": "هذه شخصية ربط لمحرك SCAM OFFICE. استوردها مرة واحدة ثم افتحها ليتمكن الموقع من تحميل بيانات كل متصل تلقائياً.",
                "expectsReply": false
              }
            ],
            "shortcutButtons": [],
            "loreBookUrls": [],
            "avatar": {
              "url": "",
              "size": 1,
              "shape": "square"
            },
            "scene": {
              "background": {
                "url": ""
              },
              "music": {
                "url": ""
              }
            },
            "userCharacter": {
              "avatar": {}
            },
            "systemCharacter": {
              "avatar": {},
              "name": "SCAM OFFICE Bridge"
            },
            "streamingResponse": true,
            "folderPath": "SCAM OFFICE",
            "customData": {
              "bridgeProtocol": "SCAM_OFFICE_BRIDGE_V1"
            },
            "uuid": null,
            "creationTime": 1793742999347,
            "lastMessageTime": 1793742999347,
            "id": 900001,
            "$types": {
              "maxParagraphCountPerMessage": "undef",
              "initialMessages": "arrayNonindexKeys",
              "shortcutButtons": "arrayNonindexKeys",
              "loreBookUrls": "arrayNonindexKeys"
            }
          }
        ]
      },
      {
        "tableName": "threads",
        "inbound": true,
        "rows": [
          {
            "name": "SCAM OFFICE — اتصال المحرك",
            "characterId": 900001,
            "creationTime": 1793742999347,
            "lastMessageTime": 1793742999347,
            "lastViewTime": 1793742999347,
            "isFav": false,
            "userCharacter": {
              "avatar": {}
            },
            "systemCharacter": {
              "avatar": {},
              "name": "SCAM OFFICE — محرك المتصلين"
            },
            "character": {
              "avatar": {},
              "name": "SCAM OFFICE — محرك المتصلين"
            },
            "modelName": "perchance-ai",
            "customCodeWindow": {
              "visible": true,
              "width": null
            },
            "customData": {
              "bridgeProtocol": "SCAM_OFFICE_BRIDGE_V1"
            },
            "folderPath": "SCAM OFFICE",
            "loreBookId": null,
            "textEmbeddingModelName": "Xenova/bge-base-en-v1.5",
            "userMessagesSentHistory": [],
            "unsentMessageText": "",
            "shortcutButtons": [
              {
                "name": "🗣️ {{char}}",
                "message": "/ai <optional writing instruction>",
                "insertionType": "replace",
                "autoSend": false,
                "type": "message"
              },
              {
                "name": "🗣️ {{user}}",
                "message": "/user <optional writing instruction>",
                "insertionType": "replace",
                "autoSend": false,
                "type": "message"
              },
              {
                "name": "🗣️ Narrator",
                "message": "/nar <optional writing instruction>",
                "insertionType": "replace",
                "autoSend": false,
                "type": "message"
              },
              {
                "name": "🖼️ Image",
                "message": "/image --num=3",
                "insertionType": "replace",
                "autoSend": true,
                "type": "message"
              }
            ],
            "currentSummaryHashChain": [],
            "id": 900002,
            "$types": {
              "userMessagesSentHistory": "arrayNonindexKeys",
              "shortcutButtons": "arrayNonindexKeys",
              "currentSummaryHashChain": "arrayNonindexKeys"
            }
          }
        ]
      },
      {
        "tableName": "messages",
        "inbound": true,
        "rows": [
          {
            "threadId": 900002,
            "message": "افتح موقع SCAM OFFICE واضغط «ربط Perchance». سيحمّل الموقع ملف المتصل الحالي تلقائياً ويرسل الردود والاقتراحات عبر قناة الربط.",
            "characterId": -2,
            "hiddenFrom": [],
            "expectsReply": false,
            "creationTime": 1793742999347,
            "variants": [
              null
            ],
            "memoryIdBatchesUsed": [],
            "loreIdsUsed": [],
            "summaryHashUsed": null,
            "summariesUsed": null,
            "summariesEndingHere": null,
            "memoriesEndingHere": null,
            "memoryQueriesUsed": [],
            "messageIdsUsed": [],
            "name": null,
            "scene": null,
            "avatar": {},
            "customData": {},
            "wrapperStyle": "",
            "order": 0,
            "instruction": null,
            "id": 900003,
            "$types": {
              "hiddenFrom": "arrayNonindexKeys",
              "variants": "arrayNonindexKeys",
              "memoryIdBatchesUsed": "arrayNonindexKeys",
              "loreIdsUsed": "arrayNonindexKeys",
              "memoryQueriesUsed": "arrayNonindexKeys",
              "messageIdsUsed": "arrayNonindexKeys"
            }
          }
        ]
      },
      {
        "tableName": "misc",
        "inbound": true,
        "rows": []
      },
      {
        "tableName": "summaries",
        "inbound": true,
        "rows": []
      },
      {
        "tableName": "memories",
        "inbound": true,
        "rows": []
      },
      {
        "tableName": "lore",
        "inbound": true,
        "rows": []
      },
      {
        "tableName": "textEmbeddingCache",
        "inbound": true,
        "rows": []
      },
      {
        "tableName": "textCompressionCache",
        "inbound": true,
        "rows": []
      }
    ]
  }
}