Files
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
12 KiB
JSON
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "abawe444/carsspacebot", "files": {"public/js/setup.js": {"c9702418341d3f44467d62c6cfbb641187770d98": {"t": 1786067877.6377082, "n": 11598, "h": "213ee605d882fdbb", "c": "let currentStep = 1;\nconst maxStep = 5;\n\nfunction showToast(message, type = 'info') {\n const toast = document.getElementById('toast');\n if (!toast) return;\n toast.textContent = message;\n toast.classList.remove('hidden');\n toast.style.background = type === 'error' ? '#b72626' : type === 'warning' ? '#a8670b' : '#1f6feb';\n setTimeout(() => toast.classList.add('hidden'), 2600);\n}\n\nasync function api(url, options = {}) {\n const response = await fetch(url, {\n headers: { 'Content-Type': 'application/json' },\n ...options\n });\n const data = await response.json().catch(() => ({}));\n if (!response.ok || data.success === false) {\n const error = new Error(data.message || data.error || 'فشل الطلب');\n error.status = response.status;\n error.statusText = response.statusText;\n error.data = data;\n error.details = data?.data || null;\n throw error;\n }\n return data;\n}\n\nfunction formatProviderErrorForUi(error) {\n const details = error?.details || {};\n const status = details.responseStatus || error?.status || '';\n const statusText = details.responseStatusText || error?.statusText || '';\n const rawError = details.error || error?.message || 'تعذر الاتصال بمزود الذكاء';\n const responseData = details.responseData ? JSON.stringify(details.responseData, null, 2) : 'null';\n const friendly = details.error_ar || error?.message || 'تعذر الاتصال بمزود الذكاء';\n\n return {\n friendly,\n full: `السبب: ${friendly}\nHTTP: ${status || '-'} ${statusText || ''}\nBackend: ${rawError}\nResponse: ${responseData}`\n };\n}\n\nfunction setStep(step) {\n currentStep = Math.min(maxStep, Math.max(1, step));\n\n for (let i = 1; i <= maxStep; i += 1) {\n const panel = document.getElementById(`step${i}`);\n panel?.classList.toggle('hidden-step', i !== currentStep);\n }\n\n document.querySelectorAll('.wiz-step').forEach((btn) => {\n btn.classList.toggle('active', Number(btn.getAttribute('data-step')) === currentStep);\n });\n\n document.getElementById('prevStepBtn').disabled = currentStep === 1;\n document.getElementById('nextStepBtn').textContent = currentStep === maxStep ? 'الانتقال للوحة' : 'التالي';\n}\n\nfunction setWhatsappStatus(status) {\n const badge = document.getElementById('setupWhatsappStatus');\n const text = document.getElementById('setupQrStatusText');\n const qrArea = document.getElementById('setupQrArea');\n\n const map = {\n connected: { label: 'متصل وجاهز للرد', cls: 'success' },\n qr: { label: 'بانتظار مسح QR', cls: 'warning' },\n reconnecting: { label: 'إعادة الاتصال', cls: 'warning' },\n disconnected: { label: 'غير متصل بواتساب', cls: 'danger' }\n };\n\n const item = map[status?.status] || map.disconnected;\n if (badge) {\n badge.textContent = item.label;\n badge.className = `status-pill ${item.cls}`;\n }\n if (text) text.textContent = item.label;\n\n if (!qrArea) return;\n if (status?.qr) {\n qrArea.innerHTML = `<img src=\"${status.qr}\" alt=\"QR\" />`;\n } else if (status?.status === 'connected') {\n qrArea.innerHTML = '<div class=\"empty\">تم الربط بنجاح ✅</div>';\n } else {\n qrArea.innerHTML = '<div class=\"empty\">بانتظار مسح QR</div>';\n }\n}\n\nfunction setProviderStatus(ok) {\n const badge = document.getElementById('setupProviderStatus');\n if (!badge) return;\n if (ok) {\n badge.textContent = 'تم الاتصال بمزود الذكاء بنجاح';\n badge.className = 'status-pill success';\n } else {\n badge.textContent = 'تعذر الاتصال بمزود الذكاء';\n badge.className = 'status-pill danger';\n }\n}\n\nfunction fillForm(formId, values = {}) {\n const form = document.getElementById(formId);\n if (!form) return;\n Object.entries(values).forEach(([key, value]) => {\n const el = form.querySelector(`[name=\"${key}\"]`);\n if (el && value !== undefined && value !== null) {\n el.value = value;\n }\n });\n}\n\nfunction serializeForm(formId) {\n const form = document.getElementById(formId);\n const payload = {};\n form.querySelectorAll('[name]').forEach((el) => {\n payload[el.name] = el.value;\n });\n return payload;\n}\n\nconst providerDefaults = {\n rules_only: { baseUrl: '', model: '' },\n openai: { baseUrl: 'https://api.openai.com/v1', model: 'gpt-4.1-mini' },\n openrouter: { baseUrl: 'https://openrouter.ai/api/v1', model: 'openai/gpt-4o-mini' },\n deepseek: { baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat' },\n grok: { baseUrl: 'https://api.x.ai/v1', model: 'grok-3-mini' },\n google: { baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai', model: 'gemini-2.5-flash' },\n custom_openai_compatible: { baseUrl: '', model: '' }\n};\n\nfunction onProviderChange() {\n const provider = document.getElementById('providerSelect').value;\n const defaults = providerDefaults[provider] || providerDefaults.rules_only;\n const baseEl = document.getElementById('providerBaseUrl');\n const modelEl = document.getElementById('providerModel');\n\n if (!baseEl.value) baseEl.value = defaults.baseUrl;\n if (!modelEl.value) modelEl.value = defaults.model;\n}\n\nasync function loadSetupData() {\n const { data } = await api('/api/setup/data');\n\n fillForm('setupCompanyForm', {\n company_name: data.company?.company_name || data.settings?.company_name,\n contact_number: data.company?.contact_number || data.settings?.company_contact_number,\n business_description: data.company?.business_description || data.settings?.company_field,\n general_manager: data.company?.general_manager || data.settings?.management_general_manager,\n company_responsible: data.company?.company_responsible || data.settings?.management_company_manager,\n center_manager: data.company?.center_manager || data.settings?.management_center_manager\n });\n\n fillForm('setupLocationsForm', {\n company_location_title: data.company?.company_location_title || data.settings?.location_company_name,\n company_location_url: data.company?.company_location_url || data.settings?.location_company_map_url,\n center_location_title: data.company?.center_location_title || data.settings?.location_center_name,\n center_location_url: data.company?.center_location_url || data.settings?.location_center_map_url\n });\n\n fillForm('setupProviderForm', {\n provider: data.provider?.provider || 'rules_only',\n baseUrl: data.provider?.base_url || '',\n model: data.provider?.model || '',\n temperature: data.provider?.temperature || 0.3,\n maxOutputTokens: data.provider?.max_output_tokens || 250,\n aiMode: data.provider?.ai_mode || 'rules_first',\n enabled: data.provider?.enabled ? 'true' : 'false'\n });\n\n fillForm('setupAssistantForm', {\n assistant_name: data.settings?.assistant_name,\n assistant_tone: data.settings?.assistant_tone,\n assistant_prompt: data.settings?.assistant_prompt\n });\n\n setWhatsappStatus(data.whatsapp || { status: 'disconnected' });\n}\n\nasync function saveCompany(e) {\n e.preventDefault();\n const payload = serializeForm('setupCompanyForm');\n await api('/api/setup/company', { method: 'POST', body: JSON.stringify(payload) });\n showToast('تم حفظ بيانات الشركة');\n}\n\nasync function saveLocations(e) {\n e.preventDefault();\n const payload = serializeForm('setupLocationsForm');\n await api('/api/setup/locations', { method: 'POST', body: JSON.stringify(payload) });\n showToast('تم حفظ بيانات المواقع');\n}\n\nasync function saveProvider(e) {\n e.preventDefault();\n const payload = serializeForm('setupProviderForm');\n payload.enabled = payload.enabled === 'true';\n await api('/api/setup/provider', { method: 'POST', body: JSON.stringify(payload) });\n showToast('تم حفظ إعدادات مزود الذكاء');\n}\n\nasync function testProvider() {\n const payload = serializeForm('setupProviderForm');\n const apiKeyInput = document.querySelector('#setupProviderForm [name=\"apiKey\"]');\n const rawApiKey = String(apiKeyInput?.value || '').trim();\n const isMasked = /\\*{3,}/.test(rawApiKey);\n const apiKey = isMasked ? '' : rawApiKey;\n const useSavedKey = !apiKey || isMasked;\n const requestPayload = {\n provider: payload.provider,\n baseUrl: payload.baseUrl,\n model: payload.model,\n apiKey,\n useSavedKey,\n temperature: payload.temperature,\n maxOutputTokens: payload.maxOutputTokens,\n aiMode: payload.aiMode\n };\n console.debug('Provider test payload:', {\n provider: requestPayload.provider,\n baseUrl: requestPayload.baseUrl,\n model: requestPayload.model,\n hasApiKey: !!requestPayload.apiKey,\n useSavedKey: requestPayload.useSavedKey,\n temperature: requestPayload.temperature,\n maxOutputTokens: requestPayload.maxOutputTokens\n });\n const output = document.getElementById('providerTestResult');\n output.textContent = 'جارٍ اختبار الاتصال...';\n\n try {\n const result = await api('/api/provider/test', {\n method: 'POST',\n body: JSON.stringify(requestPayload)\n });\n output.innerHTML = `<strong>${result.message}</strong><br/>الرد: ${(result.data?.text || '').replace(/\\n/g, '<br/>')}`;\n setProviderStatus(true);\n showToast('تم الاتصال بمزود الذكاء بنجاح');\n } catch (error) {\n const formatted = formatProviderErrorForUi(error);\n output.innerHTML = `<strong style=\"color:#b42318\">فشل اختبار مزود الذكاء</strong><br/><pre style=\"white-space:pre-wrap;direction:ltr;text-align:left\">${formatted.full}</pre>`;\n setProviderStatus(false);\n showToast(formatted.friendly, 'error');\n }\n}\n\nasync function saveAssistant(e) {\n e.preventDefault();\n const payload = serializeForm('setupAssistantForm');\n await api('/api/setup/assistant', { method: 'POST', body: JSON.stringify(payload) });\n showToast('تم حفظ شخصية المساعد');\n}\n\nasync function finishSetup() {\n await api('/api/setup/complete', { method: 'POST' });\n showToast('تم إنهاء الإعداد بنجاح');\n setTimeout(() => {\n window.location.href = '/';\n }, 600);\n}\n\nfunction bindEvents() {\n document.querySelectorAll('.wiz-step').forEach((btn) => {\n btn.addEventListener('click', () => setStep(Number(btn.getAttribute('data-step'))));\n });\n\n document.getElementById('prevStepBtn')?.addEventListener('click', () => setStep(currentStep - 1));\n document.getElementById('nextStepBtn')?.addEventListener('click', () => {\n if (currentStep === maxStep) {\n window.location.href = '/';\n return;\n }\n setStep(currentStep + 1);\n });\n\n document.getElementById('providerSelect')?.addEventListener('change', onProviderChange);\n document.getElementById('setupCompanyForm')?.addEventListener('submit', saveCompany);\n document.getElementById('setupLocationsForm')?.addEventListener('submit', saveLocations);\n document.getElementById('setupProviderForm')?.addEventListener('submit', saveProvider);\n document.getElementById('setupAssistantForm')?.addEventListener('submit', saveAssistant);\n document.getElementById('testProviderBtn')?.addEventListener('click', testProvider);\n document.getElementById('finishSetupBtn')?.addEventListener('click', finishSetup);\n\n document.getElementById('setupReconnectBtn')?.addEventListener('click', async () => {\n await api('/api/connection/restart', { method: 'POST' });\n showToast('جارٍ إعادة الاتصال بواتساب');\n });\n\n document.getElementById('setupResetSessionBtn')?.addEventListener('click', async () => {\n if (!confirm('هل تريد حذف الجلسة الحالية؟')) return;\n await api('/api/session/reset', { method: 'POST' });\n showToast('تم حذف الجلسة');\n });\n}\n\nfunction initSocket() {\n const socket = io();\n socket.on('whatsapp:status', (status) => {\n setWhatsappStatus(status);\n });\n}\n\ndocument.addEventListener('DOMContentLoaded', async () => {\n bindEvents();\n setStep(1);\n initSocket();\n try {\n await loadSetupData();\n } catch (error) {\n showToast(error.message, 'error');\n }\n});\n"}}}}