machinaos 0.0.1 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.template +71 -71
- package/LICENSE +21 -21
- package/README.md +145 -87
- package/bin/cli.js +62 -106
- package/client/.dockerignore +45 -45
- package/client/Dockerfile +68 -68
- package/client/dist/assets/index-DFSC53FP.css +1 -0
- package/client/dist/assets/index-fJ-1gTf5.js +613 -0
- package/client/dist/index.html +14 -0
- package/client/eslint.config.js +34 -16
- package/client/nginx.conf +66 -66
- package/client/package.json +61 -48
- package/client/src/App.tsx +27 -27
- package/client/src/Dashboard.tsx +1200 -1172
- package/client/src/ParameterPanel.tsx +302 -300
- package/client/src/components/AIAgentNode.tsx +315 -321
- package/client/src/components/APIKeyValidator.tsx +117 -117
- package/client/src/components/ClaudeChatModelNode.tsx +17 -17
- package/client/src/components/CredentialsModal.tsx +1200 -306
- package/client/src/components/GeminiChatModelNode.tsx +17 -17
- package/client/src/components/GenericNode.tsx +356 -356
- package/client/src/components/LocationParameterPanel.tsx +153 -153
- package/client/src/components/ModelNode.tsx +285 -285
- package/client/src/components/OpenAIChatModelNode.tsx +17 -17
- package/client/src/components/OutputPanel.tsx +470 -470
- package/client/src/components/ParameterRenderer.tsx +1873 -1873
- package/client/src/components/SkillEditorModal.tsx +3 -3
- package/client/src/components/SquareNode.tsx +812 -796
- package/client/src/components/ToolkitNode.tsx +365 -365
- package/client/src/components/auth/LoginPage.tsx +247 -247
- package/client/src/components/auth/ProtectedRoute.tsx +59 -59
- package/client/src/components/base/BaseChatModelNode.tsx +270 -270
- package/client/src/components/icons/AIProviderIcons.tsx +50 -50
- package/client/src/components/maps/GoogleMapsPicker.tsx +136 -136
- package/client/src/components/maps/MapsPreviewPanel.tsx +109 -109
- package/client/src/components/maps/index.ts +25 -25
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -1094
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +64 -64
- package/client/src/components/parameterPanel/MapsSection.tsx +91 -91
- package/client/src/components/parameterPanel/MiddleSection.tsx +867 -571
- package/client/src/components/parameterPanel/OutputSection.tsx +80 -80
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +81 -81
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -436
- package/client/src/components/parameterPanel/index.ts +41 -41
- package/client/src/components/shared/DataPanel.tsx +142 -142
- package/client/src/components/shared/JSONTreeRenderer.tsx +105 -105
- package/client/src/components/ui/AIResultModal.tsx +203 -203
- package/client/src/components/ui/ApiKeyInput.tsx +93 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -81
- package/client/src/components/ui/CollapsibleSection.tsx +87 -87
- package/client/src/components/ui/ComponentItem.tsx +153 -153
- package/client/src/components/ui/ComponentPalette.tsx +320 -320
- package/client/src/components/ui/ConsolePanel.tsx +151 -43
- package/client/src/components/ui/ErrorBoundary.tsx +195 -195
- package/client/src/components/ui/InputNodesPanel.tsx +203 -203
- package/client/src/components/ui/MapSelector.tsx +313 -313
- package/client/src/components/ui/Modal.tsx +151 -148
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -1150
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -381
- package/client/src/components/ui/QRCodeDisplay.tsx +182 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -736
- package/client/src/components/ui/WorkflowSidebar.tsx +293 -293
- package/client/src/config/antdTheme.ts +186 -186
- package/client/src/contexts/AuthContext.tsx +221 -221
- package/client/src/contexts/ThemeContext.tsx +42 -42
- package/client/src/contexts/WebSocketContext.tsx +2144 -1971
- package/client/src/factories/baseChatModelFactory.ts +255 -255
- package/client/src/hooks/useAndroidOperations.ts +118 -164
- package/client/src/hooks/useApiKeyValidation.ts +106 -106
- package/client/src/hooks/useApiKeys.ts +238 -238
- package/client/src/hooks/useAppTheme.ts +17 -17
- package/client/src/hooks/useComponentPalette.ts +50 -50
- package/client/src/hooks/useDragAndDrop.ts +123 -123
- package/client/src/hooks/useDragVariable.ts +88 -88
- package/client/src/hooks/useExecution.ts +319 -313
- package/client/src/hooks/useParameterPanel.ts +176 -176
- package/client/src/hooks/useReactFlowNodes.ts +188 -188
- package/client/src/hooks/useToolSchema.ts +209 -209
- package/client/src/hooks/useWhatsApp.ts +196 -196
- package/client/src/hooks/useWorkflowManagement.ts +45 -45
- package/client/src/index.css +314 -314
- package/client/src/nodeDefinitions/aiAgentNodes.ts +335 -335
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -340
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -383
- package/client/src/nodeDefinitions/chatNodes.ts +135 -135
- package/client/src/nodeDefinitions/codeNodes.ts +54 -54
- package/client/src/nodeDefinitions/index.ts +14 -14
- package/client/src/nodeDefinitions/locationNodes.ts +462 -462
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -220
- package/client/src/nodeDefinitions/skillNodes.ts +17 -5
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -284
- package/client/src/nodeDefinitions/whatsappNodes.ts +821 -865
- package/client/src/nodeDefinitions.ts +101 -103
- package/client/src/services/dynamicParameterService.ts +95 -95
- package/client/src/services/execution/aiAgentExecutionService.ts +34 -34
- package/client/src/services/executionService.ts +227 -231
- package/client/src/services/workflowApi.ts +91 -91
- package/client/src/store/useAppStore.ts +578 -581
- package/client/src/styles/theme.ts +513 -508
- package/client/src/styles/zIndex.ts +16 -16
- package/client/src/types/ComponentTypes.ts +38 -38
- package/client/src/types/INodeProperties.ts +287 -287
- package/client/src/types/NodeTypes.ts +27 -27
- package/client/src/utils/formatters.ts +32 -32
- package/client/src/utils/googleMapsLoader.ts +139 -139
- package/client/src/utils/locationUtils.ts +84 -84
- package/client/src/utils/nodeUtils.ts +30 -30
- package/client/src/utils/workflow.ts +29 -29
- package/client/src/vite-env.d.ts +12 -12
- package/client/tailwind.config.js +59 -59
- package/client/tsconfig.json +25 -25
- package/client/vite.config.js +35 -35
- package/package.json +78 -70
- package/scripts/build.js +153 -45
- package/scripts/clean.js +40 -40
- package/scripts/start.js +234 -210
- package/scripts/stop.js +301 -325
- package/server/.dockerignore +44 -44
- package/server/Dockerfile +45 -45
- package/server/constants.py +244 -249
- package/server/core/cache.py +460 -460
- package/server/core/config.py +127 -127
- package/server/core/container.py +98 -98
- package/server/core/database.py +1296 -1210
- package/server/core/logging.py +313 -313
- package/server/main.py +288 -288
- package/server/middleware/__init__.py +5 -5
- package/server/middleware/auth.py +89 -89
- package/server/models/auth.py +52 -52
- package/server/models/cache.py +24 -24
- package/server/models/database.py +235 -210
- package/server/models/nodes.py +435 -455
- package/server/pyproject.toml +75 -72
- package/server/requirements.txt +83 -83
- package/server/routers/android.py +294 -294
- package/server/routers/auth.py +203 -203
- package/server/routers/database.py +150 -150
- package/server/routers/maps.py +141 -141
- package/server/routers/nodejs_compat.py +288 -288
- package/server/routers/webhook.py +90 -90
- package/server/routers/websocket.py +2239 -2127
- package/server/routers/whatsapp.py +761 -761
- package/server/routers/workflow.py +199 -199
- package/server/services/ai.py +2444 -2414
- package/server/services/android_service.py +588 -588
- package/server/services/auth.py +130 -130
- package/server/services/chat_client.py +160 -160
- package/server/services/deployment/manager.py +706 -706
- package/server/services/event_waiter.py +675 -785
- package/server/services/execution/executor.py +1351 -1351
- package/server/services/execution/models.py +1 -1
- package/server/services/handlers/__init__.py +122 -126
- package/server/services/handlers/ai.py +390 -355
- package/server/services/handlers/android.py +69 -260
- package/server/services/handlers/code.py +278 -278
- package/server/services/handlers/http.py +193 -193
- package/server/services/handlers/tools.py +146 -32
- package/server/services/handlers/triggers.py +107 -107
- package/server/services/handlers/utility.py +822 -822
- package/server/services/handlers/whatsapp.py +423 -476
- package/server/services/maps.py +288 -288
- package/server/services/memory_store.py +103 -103
- package/server/services/node_executor.py +372 -375
- package/server/services/scheduler.py +155 -155
- package/server/services/skill_loader.py +1 -1
- package/server/services/status_broadcaster.py +834 -826
- package/server/services/temporal/__init__.py +23 -23
- package/server/services/temporal/activities.py +344 -344
- package/server/services/temporal/client.py +76 -76
- package/server/services/temporal/executor.py +147 -147
- package/server/services/temporal/worker.py +251 -251
- package/server/services/temporal/workflow.py +355 -355
- package/server/services/temporal/ws_client.py +236 -236
- package/server/services/text.py +110 -110
- package/server/services/user_auth.py +172 -172
- package/server/services/websocket_client.py +29 -29
- package/server/services/workflow.py +597 -597
- package/server/skills/android-skill/SKILL.md +4 -4
- package/server/skills/code-skill/SKILL.md +123 -89
- package/server/skills/maps-skill/SKILL.md +3 -3
- package/server/skills/memory-skill/SKILL.md +1 -1
- package/server/skills/web-search-skill/SKILL.md +154 -0
- package/server/skills/whatsapp-skill/SKILL.md +3 -3
- package/server/uv.lock +461 -100
- package/server/whatsapp-rpc/.dockerignore +30 -30
- package/server/whatsapp-rpc/Dockerfile +44 -44
- package/server/whatsapp-rpc/Dockerfile.web +17 -17
- package/server/whatsapp-rpc/README.md +139 -139
- package/server/whatsapp-rpc/bin/whatsapp-rpc-server +0 -0
- package/server/whatsapp-rpc/cli.js +95 -95
- package/server/whatsapp-rpc/configs/config.yaml +6 -6
- package/server/whatsapp-rpc/docker-compose.yml +35 -35
- package/server/whatsapp-rpc/docs/API.md +410 -410
- package/server/whatsapp-rpc/node_modules/.package-lock.json +259 -0
- package/server/whatsapp-rpc/node_modules/chalk/license +9 -0
- package/server/whatsapp-rpc/node_modules/chalk/package.json +83 -0
- package/server/whatsapp-rpc/node_modules/chalk/readme.md +297 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/index.d.ts +325 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/index.js +225 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/utilities.js +33 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/server/whatsapp-rpc/node_modules/commander/LICENSE +22 -0
- package/server/whatsapp-rpc/node_modules/commander/Readme.md +1148 -0
- package/server/whatsapp-rpc/node_modules/commander/esm.mjs +16 -0
- package/server/whatsapp-rpc/node_modules/commander/index.js +26 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/argument.js +145 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/command.js +2179 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/error.js +43 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/help.js +462 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/option.js +329 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/suggestSimilar.js +100 -0
- package/server/whatsapp-rpc/node_modules/commander/package-support.json +16 -0
- package/server/whatsapp-rpc/node_modules/commander/package.json +80 -0
- package/server/whatsapp-rpc/node_modules/commander/typings/esm.d.mts +3 -0
- package/server/whatsapp-rpc/node_modules/commander/typings/index.d.ts +884 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/README.md +89 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/index.js +39 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/enoent.js +59 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/parse.js +91 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/escape.js +47 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/readShebang.js +23 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/resolveCommand.js +52 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/package.json +73 -0
- package/server/whatsapp-rpc/node_modules/execa/index.d.ts +955 -0
- package/server/whatsapp-rpc/node_modules/execa/index.js +309 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/command.js +119 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/error.js +87 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/kill.js +102 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/pipe.js +42 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/promise.js +36 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/stdio.js +49 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/stream.js +133 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/verbose.js +19 -0
- package/server/whatsapp-rpc/node_modules/execa/license +9 -0
- package/server/whatsapp-rpc/node_modules/execa/package.json +90 -0
- package/server/whatsapp-rpc/node_modules/execa/readme.md +822 -0
- package/server/whatsapp-rpc/node_modules/get-stream/license +9 -0
- package/server/whatsapp-rpc/node_modules/get-stream/package.json +53 -0
- package/server/whatsapp-rpc/node_modules/get-stream/readme.md +291 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/array-buffer.js +84 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/array.js +32 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/buffer.js +20 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/contents.js +101 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/index.d.ts +119 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/index.js +5 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/string.js +36 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/utils.js +11 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/README.md +95 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/index.js +97 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/package.json +36 -0
- package/server/whatsapp-rpc/node_modules/human-signals/LICENSE +201 -0
- package/server/whatsapp-rpc/node_modules/human-signals/README.md +168 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/core.js +273 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.d.ts +73 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.js +70 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/realtime.js +16 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/signals.js +34 -0
- package/server/whatsapp-rpc/node_modules/human-signals/package.json +61 -0
- package/server/whatsapp-rpc/node_modules/is-stream/index.d.ts +81 -0
- package/server/whatsapp-rpc/node_modules/is-stream/index.js +29 -0
- package/server/whatsapp-rpc/node_modules/is-stream/license +9 -0
- package/server/whatsapp-rpc/node_modules/is-stream/package.json +44 -0
- package/server/whatsapp-rpc/node_modules/is-stream/readme.md +60 -0
- package/server/whatsapp-rpc/node_modules/isexe/LICENSE +15 -0
- package/server/whatsapp-rpc/node_modules/isexe/README.md +51 -0
- package/server/whatsapp-rpc/node_modules/isexe/index.js +57 -0
- package/server/whatsapp-rpc/node_modules/isexe/mode.js +41 -0
- package/server/whatsapp-rpc/node_modules/isexe/package.json +31 -0
- package/server/whatsapp-rpc/node_modules/isexe/test/basic.js +221 -0
- package/server/whatsapp-rpc/node_modules/isexe/windows.js +42 -0
- package/server/whatsapp-rpc/node_modules/kill-port/.editorconfig +12 -0
- package/server/whatsapp-rpc/node_modules/kill-port/.gitattributes +1 -0
- package/server/whatsapp-rpc/node_modules/kill-port/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/kill-port/README.md +140 -0
- package/server/whatsapp-rpc/node_modules/kill-port/cli.js +25 -0
- package/server/whatsapp-rpc/node_modules/kill-port/example.js +21 -0
- package/server/whatsapp-rpc/node_modules/kill-port/index.js +46 -0
- package/server/whatsapp-rpc/node_modules/kill-port/logo.png +0 -0
- package/server/whatsapp-rpc/node_modules/kill-port/package.json +41 -0
- package/server/whatsapp-rpc/node_modules/kill-port/pnpm-lock.yaml +4606 -0
- package/server/whatsapp-rpc/node_modules/kill-port/test.js +16 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/README.md +78 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/index.js +41 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/package.json +19 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/index.d.ts +52 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/index.js +71 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/license +9 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/package.json +45 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/readme.md +90 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/index.d.ts +90 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/index.js +52 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/license +9 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.d.ts +31 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.js +12 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/license +9 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/package.json +41 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/readme.md +57 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/package.json +49 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/readme.md +104 -0
- package/server/whatsapp-rpc/node_modules/onetime/index.d.ts +59 -0
- package/server/whatsapp-rpc/node_modules/onetime/index.js +41 -0
- package/server/whatsapp-rpc/node_modules/onetime/license +9 -0
- package/server/whatsapp-rpc/node_modules/onetime/package.json +45 -0
- package/server/whatsapp-rpc/node_modules/onetime/readme.md +94 -0
- package/server/whatsapp-rpc/node_modules/path-key/index.d.ts +40 -0
- package/server/whatsapp-rpc/node_modules/path-key/index.js +16 -0
- package/server/whatsapp-rpc/node_modules/path-key/license +9 -0
- package/server/whatsapp-rpc/node_modules/path-key/package.json +39 -0
- package/server/whatsapp-rpc/node_modules/path-key/readme.md +61 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/index.js +19 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/license +9 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/package.json +34 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/readme.md +34 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/index.d.ts +22 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/index.js +2 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/license +9 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/package.json +35 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/readme.md +33 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/README.md +60 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/index.js +47 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/package.json +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/LICENSE.txt +16 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/README.md +74 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts +12 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js +10 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts +48 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js +279 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/package.json +3 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js +42 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts +12 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js +4 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts +48 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js +275 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/package.json +3 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js +39 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/package.json +106 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/index.js +14 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/license +9 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/package.json +43 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/readme.md +35 -0
- package/server/whatsapp-rpc/node_modules/which/CHANGELOG.md +166 -0
- package/server/whatsapp-rpc/node_modules/which/LICENSE +15 -0
- package/server/whatsapp-rpc/node_modules/which/README.md +54 -0
- package/server/whatsapp-rpc/node_modules/which/bin/node-which +52 -0
- package/server/whatsapp-rpc/node_modules/which/package.json +43 -0
- package/server/whatsapp-rpc/node_modules/which/which.js +125 -0
- package/server/whatsapp-rpc/package-lock.json +272 -0
- package/server/whatsapp-rpc/package.json +30 -30
- package/server/whatsapp-rpc/schema.json +1294 -1294
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -66
- package/server/whatsapp-rpc/scripts/cli.js +162 -162
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -166
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -15
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -4
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -427
- package/server/whatsapp-rpc/web/app.py +609 -609
- package/server/whatsapp-rpc/web/requirements.txt +6 -6
- package/server/whatsapp-rpc/web/rpc_client.py +427 -427
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -59
- package/server/whatsapp-rpc/web/templates/base.html +149 -149
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -240
- package/server/whatsapp-rpc/web/templates/dashboard.html +319 -319
- package/server/whatsapp-rpc/web/templates/groups.html +328 -328
- package/server/whatsapp-rpc/web/templates/messages.html +465 -465
- package/server/whatsapp-rpc/web/templates/messaging.html +680 -680
- package/server/whatsapp-rpc/web/templates/send.html +258 -258
- package/server/whatsapp-rpc/web/templates/settings.html +459 -459
- package/client/src/components/ui/AndroidSettingsPanel.tsx +0 -401
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +0 -345
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +0 -140
- package/docker-compose.prod.yml +0 -107
- package/docker-compose.yml +0 -104
- package/docs-MachinaOs/README.md +0 -85
- package/docs-MachinaOs/deployment/docker.mdx +0 -228
- package/docs-MachinaOs/deployment/production.mdx +0 -345
- package/docs-MachinaOs/docs.json +0 -75
- package/docs-MachinaOs/faq.mdx +0 -309
- package/docs-MachinaOs/favicon.svg +0 -5
- package/docs-MachinaOs/installation.mdx +0 -160
- package/docs-MachinaOs/introduction.mdx +0 -114
- package/docs-MachinaOs/logo/dark.svg +0 -6
- package/docs-MachinaOs/logo/light.svg +0 -6
- package/docs-MachinaOs/nodes/ai-agent.mdx +0 -216
- package/docs-MachinaOs/nodes/ai-models.mdx +0 -240
- package/docs-MachinaOs/nodes/android.mdx +0 -411
- package/docs-MachinaOs/nodes/overview.mdx +0 -181
- package/docs-MachinaOs/nodes/schedulers.mdx +0 -316
- package/docs-MachinaOs/nodes/webhooks.mdx +0 -330
- package/docs-MachinaOs/nodes/whatsapp.mdx +0 -305
- package/docs-MachinaOs/quickstart.mdx +0 -119
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +0 -177
- package/docs-MachinaOs/tutorials/android-automation.mdx +0 -242
- package/docs-MachinaOs/tutorials/first-workflow.mdx +0 -134
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +0 -185
- package/nul +0 -0
- package/scripts/check-ports.ps1 +0 -33
- package/scripts/kill-port.ps1 +0 -154
|
@@ -1,681 +1,681 @@
|
|
|
1
|
-
{% extends "base.html" %}
|
|
2
|
-
{% block title %}Enhanced Messaging - WhatsApp Controller{% endblock %}
|
|
3
|
-
|
|
4
|
-
{% block content %}
|
|
5
|
-
<div class="space-y-8">
|
|
6
|
-
<!-- Page Header -->
|
|
7
|
-
<div>
|
|
8
|
-
<h1 class="text-3xl font-bold text-gray-900">Enhanced Messaging</h1>
|
|
9
|
-
<p class="text-gray-600 mt-1">Send messages with advanced features: media, location, stickers, contact cards, replies, and more</p>
|
|
10
|
-
|
|
11
|
-
<!-- Connection Status Badge -->
|
|
12
|
-
<div id="connection-status" class="mt-3 inline-flex items-center px-4 py-2 rounded-lg">
|
|
13
|
-
<span class="mr-2">Checking connection...</span>
|
|
14
|
-
</div>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
<!-- Enhanced Message Composer -->
|
|
18
|
-
<div class="bg-white rounded-lg shadow-md p-6">
|
|
19
|
-
<h2 class="text-xl font-semibold mb-6">Compose Enhanced Message</h2>
|
|
20
|
-
|
|
21
|
-
<form id="enhanced-message-form" class="space-y-6">
|
|
22
|
-
<!-- Recipient -->
|
|
23
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
24
|
-
<div>
|
|
25
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Send To</label>
|
|
26
|
-
<div class="flex space-x-4">
|
|
27
|
-
<label class="flex items-center">
|
|
28
|
-
<input type="radio" name="recipient_type" value="phone" checked class="mr-2">
|
|
29
|
-
<span>Phone Number</span>
|
|
30
|
-
</label>
|
|
31
|
-
<label class="flex items-center">
|
|
32
|
-
<input type="radio" name="recipient_type" value="group" class="mr-2">
|
|
33
|
-
<span>Group</span>
|
|
34
|
-
</label>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
40
|
-
<div id="phone-input">
|
|
41
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Phone Number</label>
|
|
42
|
-
<input type="text" id="phone" placeholder="919876543210"
|
|
43
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
44
|
-
</div>
|
|
45
|
-
<div id="group-input" class="hidden">
|
|
46
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Select Group</label>
|
|
47
|
-
<div class="flex space-x-2">
|
|
48
|
-
<select id="group_id" class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
49
|
-
<option value="">-- Select a group --</option>
|
|
50
|
-
</select>
|
|
51
|
-
<button type="button" onclick="loadGroupsDropdown()" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 whitespace-nowrap">
|
|
52
|
-
Load Groups
|
|
53
|
-
</button>
|
|
54
|
-
</div>
|
|
55
|
-
<p id="groups-status" class="text-xs text-gray-500 mt-1"></p>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
|
|
59
|
-
<!-- Message Type -->
|
|
60
|
-
<div>
|
|
61
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Message Type</label>
|
|
62
|
-
<select id="message-type" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
63
|
-
<option value="text">Text Message</option>
|
|
64
|
-
<option value="image">Image</option>
|
|
65
|
-
<option value="video">Video</option>
|
|
66
|
-
<option value="audio">Audio</option>
|
|
67
|
-
<option value="document">Document</option>
|
|
68
|
-
<option value="sticker">Sticker</option>
|
|
69
|
-
<option value="location">Location</option>
|
|
70
|
-
<option value="contact">Contact Card</option>
|
|
71
|
-
</select>
|
|
72
|
-
</div>
|
|
73
|
-
|
|
74
|
-
<!-- Text Message -->
|
|
75
|
-
<div id="text-section">
|
|
76
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Message</label>
|
|
77
|
-
<textarea id="message-text" placeholder="Enter your message..."
|
|
78
|
-
rows="4" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"></textarea>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<!-- Media Section -->
|
|
82
|
-
<div id="media-section" class="hidden space-y-4">
|
|
83
|
-
<div>
|
|
84
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Select Media File</label>
|
|
85
|
-
<input type="file" id="media-file-input" accept="image/*,video/*,audio/*,.pdf,.doc,.docx"
|
|
86
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
|
|
87
|
-
<p class="text-sm text-gray-500 mt-1">Choose a file from your computer</p>
|
|
88
|
-
<div id="messaging-file-preview" class="mt-2 hidden">
|
|
89
|
-
<div class="flex items-center gap-2 text-sm text-gray-600 bg-gray-50 p-2 rounded">
|
|
90
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
91
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
92
|
-
</svg>
|
|
93
|
-
<span id="messaging-file-name">No file selected</span>
|
|
94
|
-
<span id="messaging-file-size" class="text-xs text-gray-500"></span>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<div>
|
|
100
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Caption (optional)</label>
|
|
101
|
-
<textarea id="media-caption" placeholder="Enter caption for media..."
|
|
102
|
-
rows="2" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"></textarea>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<!-- Location Section -->
|
|
107
|
-
<div id="location-section" class="hidden space-y-4">
|
|
108
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
109
|
-
<div>
|
|
110
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Latitude</label>
|
|
111
|
-
<input type="number" id="latitude" step="any" placeholder="19.0760"
|
|
112
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
113
|
-
</div>
|
|
114
|
-
<div>
|
|
115
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Longitude</label>
|
|
116
|
-
<input type="number" id="longitude" step="any" placeholder="72.8777"
|
|
117
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
121
|
-
<div>
|
|
122
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Location Name (optional)</label>
|
|
123
|
-
<input type="text" id="location-name" placeholder="Mumbai, India"
|
|
124
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
125
|
-
</div>
|
|
126
|
-
<div>
|
|
127
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Address (optional)</label>
|
|
128
|
-
<input type="text" id="location-address" placeholder="Full address"
|
|
129
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
|
|
134
|
-
<!-- Contact Card Section -->
|
|
135
|
-
<div id="contact-section" class="hidden space-y-4">
|
|
136
|
-
<div>
|
|
137
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Display Name</label>
|
|
138
|
-
<input type="text" id="contact-display-name" placeholder="John Doe"
|
|
139
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
140
|
-
</div>
|
|
141
|
-
<div>
|
|
142
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">vCard Data</label>
|
|
143
|
-
<textarea id="contact-vcard" placeholder="BEGIN:VCARD VERSION:3.0 FN:John Doe TEL:+1234567890 EMAIL:john@example.com END:VCARD"
|
|
144
|
-
rows="6" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 font-mono text-sm"></textarea>
|
|
145
|
-
<p class="text-sm text-gray-500 mt-1">Enter vCard format contact information</p>
|
|
146
|
-
</div>
|
|
147
|
-
<div class="bg-blue-50 p-4 rounded-lg border border-blue-200">
|
|
148
|
-
<h4 class="font-semibold text-blue-900 mb-2">Quick vCard Template</h4>
|
|
149
|
-
<button type="button" onclick="fillVCardTemplate()" class="text-sm bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">
|
|
150
|
-
Use Template
|
|
151
|
-
</button>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
|
|
155
|
-
<!-- Reply Section -->
|
|
156
|
-
<div class="border-t pt-6">
|
|
157
|
-
<div class="flex items-center justify-between mb-4">
|
|
158
|
-
<label class="flex items-center">
|
|
159
|
-
<input type="checkbox" id="enable-reply" class="mr-2">
|
|
160
|
-
<span class="font-medium text-gray-700">Reply to Message</span>
|
|
161
|
-
</label>
|
|
162
|
-
</div>
|
|
163
|
-
|
|
164
|
-
<div id="reply-section" class="hidden space-y-4 bg-gray-50 p-4 rounded-lg">
|
|
165
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
166
|
-
<div>
|
|
167
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Original Message ID</label>
|
|
168
|
-
<input type="text" id="reply-message-id" placeholder="Message ID to reply to"
|
|
169
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
170
|
-
</div>
|
|
171
|
-
<div>
|
|
172
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Original Sender</label>
|
|
173
|
-
<input type="text" id="reply-sender" placeholder="Sender JID"
|
|
174
|
-
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
175
|
-
</div>
|
|
176
|
-
</div>
|
|
177
|
-
<div>
|
|
178
|
-
<label class="block text-sm font-medium text-gray-700 mb-2">Original Message Content</label>
|
|
179
|
-
<textarea id="reply-content" placeholder="Content of the message being replied to"
|
|
180
|
-
rows="2" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"></textarea>
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
|
|
185
|
-
<!-- Actions -->
|
|
186
|
-
<div class="flex flex-wrap gap-4 pt-6 border-t">
|
|
187
|
-
<button type="submit" class="bg-green-600 text-white px-6 py-2 rounded-lg hover:bg-green-700 flex items-center">
|
|
188
|
-
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
189
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path>
|
|
190
|
-
</svg>
|
|
191
|
-
Send Enhanced Message
|
|
192
|
-
</button>
|
|
193
|
-
<button type="button" id="send-typing" class="bg-blue-500 text-white px-6 py-2 rounded-lg hover:bg-blue-600 flex items-center">
|
|
194
|
-
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
195
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
|
196
|
-
</svg>
|
|
197
|
-
Send Typing...
|
|
198
|
-
</button>
|
|
199
|
-
<button type="button" id="clear-form" class="bg-gray-600 text-white px-6 py-2 rounded-lg hover:bg-gray-700">
|
|
200
|
-
Clear Form
|
|
201
|
-
</button>
|
|
202
|
-
</div>
|
|
203
|
-
</form>
|
|
204
|
-
</div>
|
|
205
|
-
|
|
206
|
-
<!-- Message Status -->
|
|
207
|
-
<div id="message-status" class="hidden">
|
|
208
|
-
<div class="bg-white rounded-lg shadow-md p-6">
|
|
209
|
-
<h2 class="text-xl font-semibold mb-4">Message Status</h2>
|
|
210
|
-
<div id="status-content" class="space-y-2"></div>
|
|
211
|
-
</div>
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
{% endblock %}
|
|
215
|
-
|
|
216
|
-
{% block scripts %}
|
|
217
|
-
<script>
|
|
218
|
-
const mimeTypes = {
|
|
219
|
-
image: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
|
|
220
|
-
video: ['video/mp4', 'video/3gpp', 'video/quicktime', 'video/avi'],
|
|
221
|
-
audio: ['audio/mpeg', 'audio/ogg', 'audio/wav', 'audio/aac'],
|
|
222
|
-
document: ['application/pdf', 'text/plain', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
let messagingFileBase64 = null;
|
|
226
|
-
let messagingFileName = '';
|
|
227
|
-
let messagingFileMimeType = '';
|
|
228
|
-
|
|
229
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
230
|
-
// Event listeners
|
|
231
|
-
document.getElementById('enhanced-message-form').addEventListener('submit', sendEnhancedMessage);
|
|
232
|
-
document.getElementById('message-type').addEventListener('change', handleMessageTypeChange);
|
|
233
|
-
document.getElementById('enable-reply').addEventListener('change', toggleReplySection);
|
|
234
|
-
document.getElementById('clear-form').addEventListener('click', clearForm);
|
|
235
|
-
document.getElementById('send-typing').addEventListener('click', sendTypingIndicator);
|
|
236
|
-
|
|
237
|
-
// File selection handler
|
|
238
|
-
document.getElementById('media-file-input').addEventListener('change', handleFileSelection);
|
|
239
|
-
|
|
240
|
-
// Recipient type change
|
|
241
|
-
document.querySelectorAll('input[name="recipient_type"]').forEach(radio => {
|
|
242
|
-
radio.addEventListener('change', handleRecipientTypeChange);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// Initialize
|
|
246
|
-
handleMessageTypeChange();
|
|
247
|
-
|
|
248
|
-
// Check connection status on load and periodically
|
|
249
|
-
updateConnectionStatus();
|
|
250
|
-
setInterval(updateConnectionStatus, 5000); // Update every 5 seconds
|
|
251
|
-
|
|
252
|
-
// Listen for typing result
|
|
253
|
-
socket.on('typing_result', function(data) {
|
|
254
|
-
if (data.success) {
|
|
255
|
-
showStatus('Typing indicator sent', 'success');
|
|
256
|
-
} else {
|
|
257
|
-
showStatus('Failed: ' + data.error, 'error');
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
async function updateConnectionStatus() {
|
|
263
|
-
try {
|
|
264
|
-
const response = await fetch('/api/status');
|
|
265
|
-
const status = await response.json();
|
|
266
|
-
|
|
267
|
-
const statusBadge = document.getElementById('connection-status');
|
|
268
|
-
|
|
269
|
-
if (status.data && status.data.connected) {
|
|
270
|
-
statusBadge.className = 'mt-3 inline-flex items-center px-4 py-2 rounded-lg bg-green-100 border border-green-300';
|
|
271
|
-
statusBadge.innerHTML = `
|
|
272
|
-
<svg class="w-4 h-4 mr-2 text-green-600" fill="currentColor" viewBox="0 0 20 20">
|
|
273
|
-
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
|
274
|
-
</svg>
|
|
275
|
-
<span class="text-green-800 font-medium">WhatsApp Connected</span>
|
|
276
|
-
`;
|
|
277
|
-
} else {
|
|
278
|
-
statusBadge.className = 'mt-3 inline-flex items-center px-4 py-2 rounded-lg bg-red-100 border border-red-300';
|
|
279
|
-
statusBadge.innerHTML = `
|
|
280
|
-
<svg class="w-4 h-4 mr-2 text-red-600" fill="currentColor" viewBox="0 0 20 20">
|
|
281
|
-
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
|
|
282
|
-
</svg>
|
|
283
|
-
<span class="text-red-800 font-medium">WhatsApp Not Connected - <a href="/" class="underline">Go to Dashboard</a></span>
|
|
284
|
-
`;
|
|
285
|
-
}
|
|
286
|
-
} catch (error) {
|
|
287
|
-
const statusBadge = document.getElementById('connection-status');
|
|
288
|
-
statusBadge.className = 'mt-3 inline-flex items-center px-4 py-2 rounded-lg bg-yellow-100 border border-yellow-300';
|
|
289
|
-
statusBadge.innerHTML = `
|
|
290
|
-
<svg class="w-4 h-4 mr-2 text-yellow-600" fill="currentColor" viewBox="0 0 20 20">
|
|
291
|
-
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
|
|
292
|
-
</svg>
|
|
293
|
-
<span class="text-yellow-800 font-medium">Unable to check connection status</span>
|
|
294
|
-
`;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function handleFileSelection(e) {
|
|
299
|
-
const file = e.target.files[0];
|
|
300
|
-
if (!file) {
|
|
301
|
-
messagingFileBase64 = null;
|
|
302
|
-
messagingFileName = '';
|
|
303
|
-
messagingFileMimeType = '';
|
|
304
|
-
document.getElementById('messaging-file-preview').classList.add('hidden');
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
messagingFileName = file.name;
|
|
309
|
-
messagingFileMimeType = file.type;
|
|
310
|
-
|
|
311
|
-
// Update preview
|
|
312
|
-
document.getElementById('messaging-file-name').textContent = file.name;
|
|
313
|
-
document.getElementById('messaging-file-size').textContent = formatFileSize(file.size);
|
|
314
|
-
document.getElementById('messaging-file-preview').classList.remove('hidden');
|
|
315
|
-
|
|
316
|
-
// Read file as base64
|
|
317
|
-
const reader = new FileReader();
|
|
318
|
-
reader.onload = function(event) {
|
|
319
|
-
messagingFileBase64 = event.target.result.split(',')[1];
|
|
320
|
-
showStatus('File loaded: ' + file.name, 'success');
|
|
321
|
-
};
|
|
322
|
-
reader.onerror = function() {
|
|
323
|
-
showStatus('Failed to read file', 'error');
|
|
324
|
-
messagingFileBase64 = null;
|
|
325
|
-
};
|
|
326
|
-
reader.readAsDataURL(file);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function formatFileSize(bytes) {
|
|
330
|
-
if (bytes === 0) return '0 Bytes';
|
|
331
|
-
const k = 1024;
|
|
332
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
333
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
334
|
-
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function handleRecipientTypeChange() {
|
|
338
|
-
const recipientType = document.querySelector('input[name="recipient_type"]:checked').value;
|
|
339
|
-
|
|
340
|
-
if (recipientType === 'phone') {
|
|
341
|
-
document.getElementById('phone-input').classList.remove('hidden');
|
|
342
|
-
document.getElementById('group-input').classList.add('hidden');
|
|
343
|
-
} else {
|
|
344
|
-
document.getElementById('phone-input').classList.add('hidden');
|
|
345
|
-
document.getElementById('group-input').classList.remove('hidden');
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function handleMessageTypeChange() {
|
|
350
|
-
const messageType = document.getElementById('message-type').value;
|
|
351
|
-
|
|
352
|
-
// Hide all sections
|
|
353
|
-
document.getElementById('text-section').classList.add('hidden');
|
|
354
|
-
document.getElementById('media-section').classList.add('hidden');
|
|
355
|
-
document.getElementById('location-section').classList.add('hidden');
|
|
356
|
-
document.getElementById('contact-section').classList.add('hidden');
|
|
357
|
-
|
|
358
|
-
// Show relevant section
|
|
359
|
-
if (messageType === 'text') {
|
|
360
|
-
document.getElementById('text-section').classList.remove('hidden');
|
|
361
|
-
} else if (['image', 'video', 'audio', 'document', 'sticker'].includes(messageType)) {
|
|
362
|
-
document.getElementById('media-section').classList.remove('hidden');
|
|
363
|
-
|
|
364
|
-
// Update file accept attribute based on type
|
|
365
|
-
const fileInput = document.getElementById('media-file-input');
|
|
366
|
-
if (messageType === 'sticker') {
|
|
367
|
-
fileInput.accept = 'image/webp,image/png';
|
|
368
|
-
} else if (messageType === 'image') {
|
|
369
|
-
fileInput.accept = 'image/*';
|
|
370
|
-
} else if (messageType === 'video') {
|
|
371
|
-
fileInput.accept = 'video/*';
|
|
372
|
-
} else if (messageType === 'audio') {
|
|
373
|
-
fileInput.accept = 'audio/*';
|
|
374
|
-
} else if (messageType === 'document') {
|
|
375
|
-
fileInput.accept = '.pdf,.doc,.docx,.txt,.zip';
|
|
376
|
-
}
|
|
377
|
-
} else if (messageType === 'location') {
|
|
378
|
-
document.getElementById('location-section').classList.remove('hidden');
|
|
379
|
-
} else if (messageType === 'contact') {
|
|
380
|
-
document.getElementById('contact-section').classList.remove('hidden');
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function toggleReplySection() {
|
|
385
|
-
const enabled = document.getElementById('enable-reply').checked;
|
|
386
|
-
const section = document.getElementById('reply-section');
|
|
387
|
-
|
|
388
|
-
if (enabled) {
|
|
389
|
-
section.classList.remove('hidden');
|
|
390
|
-
} else {
|
|
391
|
-
section.classList.add('hidden');
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
async function sendEnhancedMessage(e) {
|
|
396
|
-
e.preventDefault();
|
|
397
|
-
|
|
398
|
-
// Check WhatsApp connection status first
|
|
399
|
-
try {
|
|
400
|
-
const statusResponse = await fetch('/api/status');
|
|
401
|
-
const status = await statusResponse.json();
|
|
402
|
-
|
|
403
|
-
if (!status.data || !status.data.connected) {
|
|
404
|
-
showStatus('WhatsApp is not connected. Please connect first from the dashboard.', 'error');
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
} catch (error) {
|
|
408
|
-
showStatus('Failed to check connection status. Please try again.', 'error');
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const messageType = document.getElementById('message-type').value;
|
|
413
|
-
const recipientType = document.querySelector('input[name="recipient_type"]:checked').value;
|
|
414
|
-
|
|
415
|
-
// Build message data
|
|
416
|
-
const messageData = {
|
|
417
|
-
type: messageType
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
// Set recipient
|
|
421
|
-
if (recipientType === 'phone') {
|
|
422
|
-
messageData.phone = document.getElementById('phone').value.trim();
|
|
423
|
-
} else {
|
|
424
|
-
messageData.group_id = document.getElementById('group_id').value.trim();
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Add content based on type
|
|
428
|
-
if (messageType === 'text') {
|
|
429
|
-
messageData.message = document.getElementById('message-text').value.trim();
|
|
430
|
-
} else if (['image', 'video', 'audio', 'document', 'sticker'].includes(messageType)) {
|
|
431
|
-
if (!messagingFileBase64) {
|
|
432
|
-
showStatus('Please select a file first', 'error');
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
const caption = document.getElementById('media-caption').value.trim();
|
|
437
|
-
|
|
438
|
-
messageData.media_data = {
|
|
439
|
-
data: messagingFileBase64,
|
|
440
|
-
mime_type: messagingFileMimeType,
|
|
441
|
-
filename: messagingFileName,
|
|
442
|
-
caption: caption
|
|
443
|
-
};
|
|
444
|
-
} else if (messageType === 'location') {
|
|
445
|
-
messageData.location = {
|
|
446
|
-
latitude: parseFloat(document.getElementById('latitude').value),
|
|
447
|
-
longitude: parseFloat(document.getElementById('longitude').value),
|
|
448
|
-
name: document.getElementById('location-name').value.trim(),
|
|
449
|
-
address: document.getElementById('location-address').value.trim()
|
|
450
|
-
};
|
|
451
|
-
} else if (messageType === 'contact') {
|
|
452
|
-
const displayName = document.getElementById('contact-display-name').value.trim();
|
|
453
|
-
const vcard = document.getElementById('contact-vcard').value.trim();
|
|
454
|
-
|
|
455
|
-
if (!displayName || !vcard) {
|
|
456
|
-
showStatus('Please enter display name and vCard data', 'error');
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
messageData.contact = {
|
|
461
|
-
display_name: displayName,
|
|
462
|
-
vcard: vcard
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Add reply data if enabled
|
|
467
|
-
if (document.getElementById('enable-reply').checked) {
|
|
468
|
-
messageData.reply = {
|
|
469
|
-
message_id: document.getElementById('reply-message-id').value.trim(),
|
|
470
|
-
sender: document.getElementById('reply-sender').value.trim(),
|
|
471
|
-
content: document.getElementById('reply-content').value.trim()
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
try {
|
|
476
|
-
showStatus('Sending message...', 'info');
|
|
477
|
-
|
|
478
|
-
const response = await fetch('/api/send/enhanced', {
|
|
479
|
-
method: 'POST',
|
|
480
|
-
headers: {
|
|
481
|
-
'Content-Type': 'application/json'
|
|
482
|
-
},
|
|
483
|
-
body: JSON.stringify(messageData)
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
const result = await response.json();
|
|
487
|
-
|
|
488
|
-
// Clear the "sending" message
|
|
489
|
-
document.getElementById('status-content').innerHTML = '';
|
|
490
|
-
|
|
491
|
-
// Show detailed error if available
|
|
492
|
-
if (!result.success) {
|
|
493
|
-
let errorMsg = result.error || 'Unknown error occurred';
|
|
494
|
-
|
|
495
|
-
// Provide user-friendly messages for common errors
|
|
496
|
-
if (errorMsg.includes('websocket not connected')) {
|
|
497
|
-
errorMsg = 'WhatsApp connection lost. Please check the dashboard and reconnect.';
|
|
498
|
-
} else if (errorMsg.includes('not connected')) {
|
|
499
|
-
errorMsg = 'WhatsApp is not connected. Please connect first.';
|
|
500
|
-
} else if (errorMsg.includes('invalid')) {
|
|
501
|
-
errorMsg = 'Invalid data provided: ' + errorMsg;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
showMessageStatus({ success: false, error: errorMsg });
|
|
505
|
-
} else {
|
|
506
|
-
showMessageStatus(result);
|
|
507
|
-
// Clear form on success
|
|
508
|
-
clearForm();
|
|
509
|
-
}
|
|
510
|
-
} catch (error) {
|
|
511
|
-
console.error('Error sending message:', error);
|
|
512
|
-
showMessageStatus({
|
|
513
|
-
success: false,
|
|
514
|
-
error: 'Network error: Unable to reach the API server. Please check if services are running.'
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function clearForm() {
|
|
520
|
-
document.getElementById('enhanced-message-form').reset();
|
|
521
|
-
document.getElementById('enable-reply').checked = false;
|
|
522
|
-
toggleReplySection();
|
|
523
|
-
handleMessageTypeChange();
|
|
524
|
-
handleRecipientTypeChange();
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function showMessageStatus(result) {
|
|
528
|
-
const statusDiv = document.getElementById('message-status');
|
|
529
|
-
const content = document.getElementById('status-content');
|
|
530
|
-
|
|
531
|
-
const timestamp = new Date().toLocaleString();
|
|
532
|
-
const statusClass = result.success ? 'bg-green-50 text-green-800 border-green-200' : 'bg-red-50 text-red-800 border-red-200';
|
|
533
|
-
|
|
534
|
-
const statusHtml = `
|
|
535
|
-
<div class="border rounded-lg p-4 ${statusClass}">
|
|
536
|
-
<div class="flex justify-between items-start">
|
|
537
|
-
<div class="flex-1">
|
|
538
|
-
<h3 class="font-semibold">${result.success ? 'Success' : 'Error'}</h3>
|
|
539
|
-
<p class="text-sm mt-1">${result.success ? (result.message || 'Message sent successfully') : result.error}</p>
|
|
540
|
-
<p class="text-xs mt-2 opacity-75">${timestamp}</p>
|
|
541
|
-
</div>
|
|
542
|
-
<button onclick="this.parentElement.parentElement.remove()" class="text-gray-400 hover:text-gray-600">
|
|
543
|
-
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
544
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
545
|
-
</svg>
|
|
546
|
-
</button>
|
|
547
|
-
</div>
|
|
548
|
-
</div>
|
|
549
|
-
`;
|
|
550
|
-
|
|
551
|
-
content.insertAdjacentHTML('afterbegin', statusHtml);
|
|
552
|
-
statusDiv.classList.remove('hidden');
|
|
553
|
-
|
|
554
|
-
// Auto-remove after 10 seconds for success messages
|
|
555
|
-
if (result.success) {
|
|
556
|
-
setTimeout(() => {
|
|
557
|
-
const firstStatus = content.firstElementChild;
|
|
558
|
-
if (firstStatus) {
|
|
559
|
-
firstStatus.remove();
|
|
560
|
-
if (content.children.length === 0) {
|
|
561
|
-
statusDiv.classList.add('hidden');
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}, 10000);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
function fillVCardTemplate() {
|
|
569
|
-
const template = `BEGIN:VCARD
|
|
570
|
-
VERSION:3.0
|
|
571
|
-
FN:John Doe
|
|
572
|
-
TEL:+1234567890
|
|
573
|
-
EMAIL:john@example.com
|
|
574
|
-
ORG:Example Company
|
|
575
|
-
TITLE:Software Engineer
|
|
576
|
-
END:VCARD`;
|
|
577
|
-
|
|
578
|
-
document.getElementById('contact-display-name').value = 'John Doe';
|
|
579
|
-
document.getElementById('contact-vcard').value = template;
|
|
580
|
-
showStatus('vCard template filled. Please customize the details.', 'success');
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
async function loadGroupsDropdown() {
|
|
584
|
-
const select = document.getElementById('group_id');
|
|
585
|
-
const status = document.getElementById('groups-status');
|
|
586
|
-
|
|
587
|
-
status.textContent = 'Loading groups...';
|
|
588
|
-
status.className = 'text-xs text-blue-500 mt-1';
|
|
589
|
-
|
|
590
|
-
try {
|
|
591
|
-
const response = await fetch('/api/groups');
|
|
592
|
-
const data = await response.json();
|
|
593
|
-
|
|
594
|
-
if (data.success && data.data) {
|
|
595
|
-
const groups = data.data;
|
|
596
|
-
select.innerHTML = '<option value="">-- Select a group --</option>';
|
|
597
|
-
|
|
598
|
-
groups.forEach(group => {
|
|
599
|
-
const option = document.createElement('option');
|
|
600
|
-
option.value = group.jid;
|
|
601
|
-
option.textContent = `${group.name} (${group.size} members)`;
|
|
602
|
-
select.appendChild(option);
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
status.textContent = `${groups.length} groups loaded`;
|
|
606
|
-
status.className = 'text-xs text-green-600 mt-1';
|
|
607
|
-
} else {
|
|
608
|
-
status.textContent = data.error || 'Failed to load groups';
|
|
609
|
-
status.className = 'text-xs text-red-500 mt-1';
|
|
610
|
-
}
|
|
611
|
-
} catch (error) {
|
|
612
|
-
status.textContent = 'Error: ' + error.message;
|
|
613
|
-
status.className = 'text-xs text-red-500 mt-1';
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function showStatus(message, type) {
|
|
618
|
-
if (type === 'info') {
|
|
619
|
-
// Show info message differently
|
|
620
|
-
const statusDiv = document.getElementById('message-status');
|
|
621
|
-
const content = document.getElementById('status-content');
|
|
622
|
-
|
|
623
|
-
const statusHtml = `
|
|
624
|
-
<div class="border rounded-lg p-4 bg-blue-50 text-blue-800 border-blue-200">
|
|
625
|
-
<div class="flex items-center">
|
|
626
|
-
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
627
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
|
628
|
-
</svg>
|
|
629
|
-
<span>${message}</span>
|
|
630
|
-
</div>
|
|
631
|
-
</div>
|
|
632
|
-
`;
|
|
633
|
-
|
|
634
|
-
content.innerHTML = statusHtml;
|
|
635
|
-
statusDiv.classList.remove('hidden');
|
|
636
|
-
} else {
|
|
637
|
-
showMessageStatus({
|
|
638
|
-
success: type === 'success',
|
|
639
|
-
message: type === 'success' ? message : null,
|
|
640
|
-
error: type === 'error' ? message : null
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
function sendTypingIndicator() {
|
|
646
|
-
const recipientType = document.querySelector('input[name="recipient_type"]:checked').value;
|
|
647
|
-
let jid;
|
|
648
|
-
|
|
649
|
-
if (recipientType === 'phone') {
|
|
650
|
-
const phone = document.getElementById('phone').value.trim();
|
|
651
|
-
if (!phone) {
|
|
652
|
-
showStatus('Please enter a phone number first', 'error');
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
jid = phone + '@s.whatsapp.net';
|
|
656
|
-
} else {
|
|
657
|
-
jid = document.getElementById('group_id').value.trim();
|
|
658
|
-
if (!jid) {
|
|
659
|
-
showStatus('Please select a group first', 'error');
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// Send typing indicator via WebSocket
|
|
665
|
-
socket.emit('typing', {
|
|
666
|
-
jid: jid,
|
|
667
|
-
state: 'composing'
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
showStatus('Sending typing indicator...', 'info');
|
|
671
|
-
|
|
672
|
-
// Auto-stop typing after 5 seconds
|
|
673
|
-
setTimeout(() => {
|
|
674
|
-
socket.emit('typing', {
|
|
675
|
-
jid: jid,
|
|
676
|
-
state: 'paused'
|
|
677
|
-
});
|
|
678
|
-
}, 5000);
|
|
679
|
-
}
|
|
680
|
-
</script>
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
{% block title %}Enhanced Messaging - WhatsApp Controller{% endblock %}
|
|
3
|
+
|
|
4
|
+
{% block content %}
|
|
5
|
+
<div class="space-y-8">
|
|
6
|
+
<!-- Page Header -->
|
|
7
|
+
<div>
|
|
8
|
+
<h1 class="text-3xl font-bold text-gray-900">Enhanced Messaging</h1>
|
|
9
|
+
<p class="text-gray-600 mt-1">Send messages with advanced features: media, location, stickers, contact cards, replies, and more</p>
|
|
10
|
+
|
|
11
|
+
<!-- Connection Status Badge -->
|
|
12
|
+
<div id="connection-status" class="mt-3 inline-flex items-center px-4 py-2 rounded-lg">
|
|
13
|
+
<span class="mr-2">Checking connection...</span>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- Enhanced Message Composer -->
|
|
18
|
+
<div class="bg-white rounded-lg shadow-md p-6">
|
|
19
|
+
<h2 class="text-xl font-semibold mb-6">Compose Enhanced Message</h2>
|
|
20
|
+
|
|
21
|
+
<form id="enhanced-message-form" class="space-y-6">
|
|
22
|
+
<!-- Recipient -->
|
|
23
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
24
|
+
<div>
|
|
25
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Send To</label>
|
|
26
|
+
<div class="flex space-x-4">
|
|
27
|
+
<label class="flex items-center">
|
|
28
|
+
<input type="radio" name="recipient_type" value="phone" checked class="mr-2">
|
|
29
|
+
<span>Phone Number</span>
|
|
30
|
+
</label>
|
|
31
|
+
<label class="flex items-center">
|
|
32
|
+
<input type="radio" name="recipient_type" value="group" class="mr-2">
|
|
33
|
+
<span>Group</span>
|
|
34
|
+
</label>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
40
|
+
<div id="phone-input">
|
|
41
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Phone Number</label>
|
|
42
|
+
<input type="text" id="phone" placeholder="919876543210"
|
|
43
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
44
|
+
</div>
|
|
45
|
+
<div id="group-input" class="hidden">
|
|
46
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Select Group</label>
|
|
47
|
+
<div class="flex space-x-2">
|
|
48
|
+
<select id="group_id" class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
49
|
+
<option value="">-- Select a group --</option>
|
|
50
|
+
</select>
|
|
51
|
+
<button type="button" onclick="loadGroupsDropdown()" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 whitespace-nowrap">
|
|
52
|
+
Load Groups
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
<p id="groups-status" class="text-xs text-gray-500 mt-1"></p>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- Message Type -->
|
|
60
|
+
<div>
|
|
61
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Message Type</label>
|
|
62
|
+
<select id="message-type" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
63
|
+
<option value="text">Text Message</option>
|
|
64
|
+
<option value="image">Image</option>
|
|
65
|
+
<option value="video">Video</option>
|
|
66
|
+
<option value="audio">Audio</option>
|
|
67
|
+
<option value="document">Document</option>
|
|
68
|
+
<option value="sticker">Sticker</option>
|
|
69
|
+
<option value="location">Location</option>
|
|
70
|
+
<option value="contact">Contact Card</option>
|
|
71
|
+
</select>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<!-- Text Message -->
|
|
75
|
+
<div id="text-section">
|
|
76
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Message</label>
|
|
77
|
+
<textarea id="message-text" placeholder="Enter your message..."
|
|
78
|
+
rows="4" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"></textarea>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<!-- Media Section -->
|
|
82
|
+
<div id="media-section" class="hidden space-y-4">
|
|
83
|
+
<div>
|
|
84
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Select Media File</label>
|
|
85
|
+
<input type="file" id="media-file-input" accept="image/*,video/*,audio/*,.pdf,.doc,.docx"
|
|
86
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
|
|
87
|
+
<p class="text-sm text-gray-500 mt-1">Choose a file from your computer</p>
|
|
88
|
+
<div id="messaging-file-preview" class="mt-2 hidden">
|
|
89
|
+
<div class="flex items-center gap-2 text-sm text-gray-600 bg-gray-50 p-2 rounded">
|
|
90
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
91
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
92
|
+
</svg>
|
|
93
|
+
<span id="messaging-file-name">No file selected</span>
|
|
94
|
+
<span id="messaging-file-size" class="text-xs text-gray-500"></span>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div>
|
|
100
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Caption (optional)</label>
|
|
101
|
+
<textarea id="media-caption" placeholder="Enter caption for media..."
|
|
102
|
+
rows="2" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"></textarea>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<!-- Location Section -->
|
|
107
|
+
<div id="location-section" class="hidden space-y-4">
|
|
108
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
109
|
+
<div>
|
|
110
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Latitude</label>
|
|
111
|
+
<input type="number" id="latitude" step="any" placeholder="19.0760"
|
|
112
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
113
|
+
</div>
|
|
114
|
+
<div>
|
|
115
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Longitude</label>
|
|
116
|
+
<input type="number" id="longitude" step="any" placeholder="72.8777"
|
|
117
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
121
|
+
<div>
|
|
122
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Location Name (optional)</label>
|
|
123
|
+
<input type="text" id="location-name" placeholder="Mumbai, India"
|
|
124
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
125
|
+
</div>
|
|
126
|
+
<div>
|
|
127
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Address (optional)</label>
|
|
128
|
+
<input type="text" id="location-address" placeholder="Full address"
|
|
129
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<!-- Contact Card Section -->
|
|
135
|
+
<div id="contact-section" class="hidden space-y-4">
|
|
136
|
+
<div>
|
|
137
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Display Name</label>
|
|
138
|
+
<input type="text" id="contact-display-name" placeholder="John Doe"
|
|
139
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
140
|
+
</div>
|
|
141
|
+
<div>
|
|
142
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">vCard Data</label>
|
|
143
|
+
<textarea id="contact-vcard" placeholder="BEGIN:VCARD VERSION:3.0 FN:John Doe TEL:+1234567890 EMAIL:john@example.com END:VCARD"
|
|
144
|
+
rows="6" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 font-mono text-sm"></textarea>
|
|
145
|
+
<p class="text-sm text-gray-500 mt-1">Enter vCard format contact information</p>
|
|
146
|
+
</div>
|
|
147
|
+
<div class="bg-blue-50 p-4 rounded-lg border border-blue-200">
|
|
148
|
+
<h4 class="font-semibold text-blue-900 mb-2">Quick vCard Template</h4>
|
|
149
|
+
<button type="button" onclick="fillVCardTemplate()" class="text-sm bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">
|
|
150
|
+
Use Template
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- Reply Section -->
|
|
156
|
+
<div class="border-t pt-6">
|
|
157
|
+
<div class="flex items-center justify-between mb-4">
|
|
158
|
+
<label class="flex items-center">
|
|
159
|
+
<input type="checkbox" id="enable-reply" class="mr-2">
|
|
160
|
+
<span class="font-medium text-gray-700">Reply to Message</span>
|
|
161
|
+
</label>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div id="reply-section" class="hidden space-y-4 bg-gray-50 p-4 rounded-lg">
|
|
165
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
166
|
+
<div>
|
|
167
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Original Message ID</label>
|
|
168
|
+
<input type="text" id="reply-message-id" placeholder="Message ID to reply to"
|
|
169
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
170
|
+
</div>
|
|
171
|
+
<div>
|
|
172
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Original Sender</label>
|
|
173
|
+
<input type="text" id="reply-sender" placeholder="Sender JID"
|
|
174
|
+
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div>
|
|
178
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">Original Message Content</label>
|
|
179
|
+
<textarea id="reply-content" placeholder="Content of the message being replied to"
|
|
180
|
+
rows="2" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"></textarea>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<!-- Actions -->
|
|
186
|
+
<div class="flex flex-wrap gap-4 pt-6 border-t">
|
|
187
|
+
<button type="submit" class="bg-green-600 text-white px-6 py-2 rounded-lg hover:bg-green-700 flex items-center">
|
|
188
|
+
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
189
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path>
|
|
190
|
+
</svg>
|
|
191
|
+
Send Enhanced Message
|
|
192
|
+
</button>
|
|
193
|
+
<button type="button" id="send-typing" class="bg-blue-500 text-white px-6 py-2 rounded-lg hover:bg-blue-600 flex items-center">
|
|
194
|
+
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
195
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
|
196
|
+
</svg>
|
|
197
|
+
Send Typing...
|
|
198
|
+
</button>
|
|
199
|
+
<button type="button" id="clear-form" class="bg-gray-600 text-white px-6 py-2 rounded-lg hover:bg-gray-700">
|
|
200
|
+
Clear Form
|
|
201
|
+
</button>
|
|
202
|
+
</div>
|
|
203
|
+
</form>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<!-- Message Status -->
|
|
207
|
+
<div id="message-status" class="hidden">
|
|
208
|
+
<div class="bg-white rounded-lg shadow-md p-6">
|
|
209
|
+
<h2 class="text-xl font-semibold mb-4">Message Status</h2>
|
|
210
|
+
<div id="status-content" class="space-y-2"></div>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
{% endblock %}
|
|
215
|
+
|
|
216
|
+
{% block scripts %}
|
|
217
|
+
<script>
|
|
218
|
+
const mimeTypes = {
|
|
219
|
+
image: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
|
|
220
|
+
video: ['video/mp4', 'video/3gpp', 'video/quicktime', 'video/avi'],
|
|
221
|
+
audio: ['audio/mpeg', 'audio/ogg', 'audio/wav', 'audio/aac'],
|
|
222
|
+
document: ['application/pdf', 'text/plain', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
let messagingFileBase64 = null;
|
|
226
|
+
let messagingFileName = '';
|
|
227
|
+
let messagingFileMimeType = '';
|
|
228
|
+
|
|
229
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
230
|
+
// Event listeners
|
|
231
|
+
document.getElementById('enhanced-message-form').addEventListener('submit', sendEnhancedMessage);
|
|
232
|
+
document.getElementById('message-type').addEventListener('change', handleMessageTypeChange);
|
|
233
|
+
document.getElementById('enable-reply').addEventListener('change', toggleReplySection);
|
|
234
|
+
document.getElementById('clear-form').addEventListener('click', clearForm);
|
|
235
|
+
document.getElementById('send-typing').addEventListener('click', sendTypingIndicator);
|
|
236
|
+
|
|
237
|
+
// File selection handler
|
|
238
|
+
document.getElementById('media-file-input').addEventListener('change', handleFileSelection);
|
|
239
|
+
|
|
240
|
+
// Recipient type change
|
|
241
|
+
document.querySelectorAll('input[name="recipient_type"]').forEach(radio => {
|
|
242
|
+
radio.addEventListener('change', handleRecipientTypeChange);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Initialize
|
|
246
|
+
handleMessageTypeChange();
|
|
247
|
+
|
|
248
|
+
// Check connection status on load and periodically
|
|
249
|
+
updateConnectionStatus();
|
|
250
|
+
setInterval(updateConnectionStatus, 5000); // Update every 5 seconds
|
|
251
|
+
|
|
252
|
+
// Listen for typing result
|
|
253
|
+
socket.on('typing_result', function(data) {
|
|
254
|
+
if (data.success) {
|
|
255
|
+
showStatus('Typing indicator sent', 'success');
|
|
256
|
+
} else {
|
|
257
|
+
showStatus('Failed: ' + data.error, 'error');
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
async function updateConnectionStatus() {
|
|
263
|
+
try {
|
|
264
|
+
const response = await fetch('/api/status');
|
|
265
|
+
const status = await response.json();
|
|
266
|
+
|
|
267
|
+
const statusBadge = document.getElementById('connection-status');
|
|
268
|
+
|
|
269
|
+
if (status.data && status.data.connected) {
|
|
270
|
+
statusBadge.className = 'mt-3 inline-flex items-center px-4 py-2 rounded-lg bg-green-100 border border-green-300';
|
|
271
|
+
statusBadge.innerHTML = `
|
|
272
|
+
<svg class="w-4 h-4 mr-2 text-green-600" fill="currentColor" viewBox="0 0 20 20">
|
|
273
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
|
274
|
+
</svg>
|
|
275
|
+
<span class="text-green-800 font-medium">WhatsApp Connected</span>
|
|
276
|
+
`;
|
|
277
|
+
} else {
|
|
278
|
+
statusBadge.className = 'mt-3 inline-flex items-center px-4 py-2 rounded-lg bg-red-100 border border-red-300';
|
|
279
|
+
statusBadge.innerHTML = `
|
|
280
|
+
<svg class="w-4 h-4 mr-2 text-red-600" fill="currentColor" viewBox="0 0 20 20">
|
|
281
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
|
|
282
|
+
</svg>
|
|
283
|
+
<span class="text-red-800 font-medium">WhatsApp Not Connected - <a href="/" class="underline">Go to Dashboard</a></span>
|
|
284
|
+
`;
|
|
285
|
+
}
|
|
286
|
+
} catch (error) {
|
|
287
|
+
const statusBadge = document.getElementById('connection-status');
|
|
288
|
+
statusBadge.className = 'mt-3 inline-flex items-center px-4 py-2 rounded-lg bg-yellow-100 border border-yellow-300';
|
|
289
|
+
statusBadge.innerHTML = `
|
|
290
|
+
<svg class="w-4 h-4 mr-2 text-yellow-600" fill="currentColor" viewBox="0 0 20 20">
|
|
291
|
+
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
|
|
292
|
+
</svg>
|
|
293
|
+
<span class="text-yellow-800 font-medium">Unable to check connection status</span>
|
|
294
|
+
`;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function handleFileSelection(e) {
|
|
299
|
+
const file = e.target.files[0];
|
|
300
|
+
if (!file) {
|
|
301
|
+
messagingFileBase64 = null;
|
|
302
|
+
messagingFileName = '';
|
|
303
|
+
messagingFileMimeType = '';
|
|
304
|
+
document.getElementById('messaging-file-preview').classList.add('hidden');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
messagingFileName = file.name;
|
|
309
|
+
messagingFileMimeType = file.type;
|
|
310
|
+
|
|
311
|
+
// Update preview
|
|
312
|
+
document.getElementById('messaging-file-name').textContent = file.name;
|
|
313
|
+
document.getElementById('messaging-file-size').textContent = formatFileSize(file.size);
|
|
314
|
+
document.getElementById('messaging-file-preview').classList.remove('hidden');
|
|
315
|
+
|
|
316
|
+
// Read file as base64
|
|
317
|
+
const reader = new FileReader();
|
|
318
|
+
reader.onload = function(event) {
|
|
319
|
+
messagingFileBase64 = event.target.result.split(',')[1];
|
|
320
|
+
showStatus('File loaded: ' + file.name, 'success');
|
|
321
|
+
};
|
|
322
|
+
reader.onerror = function() {
|
|
323
|
+
showStatus('Failed to read file', 'error');
|
|
324
|
+
messagingFileBase64 = null;
|
|
325
|
+
};
|
|
326
|
+
reader.readAsDataURL(file);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function formatFileSize(bytes) {
|
|
330
|
+
if (bytes === 0) return '0 Bytes';
|
|
331
|
+
const k = 1024;
|
|
332
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
333
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
334
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function handleRecipientTypeChange() {
|
|
338
|
+
const recipientType = document.querySelector('input[name="recipient_type"]:checked').value;
|
|
339
|
+
|
|
340
|
+
if (recipientType === 'phone') {
|
|
341
|
+
document.getElementById('phone-input').classList.remove('hidden');
|
|
342
|
+
document.getElementById('group-input').classList.add('hidden');
|
|
343
|
+
} else {
|
|
344
|
+
document.getElementById('phone-input').classList.add('hidden');
|
|
345
|
+
document.getElementById('group-input').classList.remove('hidden');
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function handleMessageTypeChange() {
|
|
350
|
+
const messageType = document.getElementById('message-type').value;
|
|
351
|
+
|
|
352
|
+
// Hide all sections
|
|
353
|
+
document.getElementById('text-section').classList.add('hidden');
|
|
354
|
+
document.getElementById('media-section').classList.add('hidden');
|
|
355
|
+
document.getElementById('location-section').classList.add('hidden');
|
|
356
|
+
document.getElementById('contact-section').classList.add('hidden');
|
|
357
|
+
|
|
358
|
+
// Show relevant section
|
|
359
|
+
if (messageType === 'text') {
|
|
360
|
+
document.getElementById('text-section').classList.remove('hidden');
|
|
361
|
+
} else if (['image', 'video', 'audio', 'document', 'sticker'].includes(messageType)) {
|
|
362
|
+
document.getElementById('media-section').classList.remove('hidden');
|
|
363
|
+
|
|
364
|
+
// Update file accept attribute based on type
|
|
365
|
+
const fileInput = document.getElementById('media-file-input');
|
|
366
|
+
if (messageType === 'sticker') {
|
|
367
|
+
fileInput.accept = 'image/webp,image/png';
|
|
368
|
+
} else if (messageType === 'image') {
|
|
369
|
+
fileInput.accept = 'image/*';
|
|
370
|
+
} else if (messageType === 'video') {
|
|
371
|
+
fileInput.accept = 'video/*';
|
|
372
|
+
} else if (messageType === 'audio') {
|
|
373
|
+
fileInput.accept = 'audio/*';
|
|
374
|
+
} else if (messageType === 'document') {
|
|
375
|
+
fileInput.accept = '.pdf,.doc,.docx,.txt,.zip';
|
|
376
|
+
}
|
|
377
|
+
} else if (messageType === 'location') {
|
|
378
|
+
document.getElementById('location-section').classList.remove('hidden');
|
|
379
|
+
} else if (messageType === 'contact') {
|
|
380
|
+
document.getElementById('contact-section').classList.remove('hidden');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function toggleReplySection() {
|
|
385
|
+
const enabled = document.getElementById('enable-reply').checked;
|
|
386
|
+
const section = document.getElementById('reply-section');
|
|
387
|
+
|
|
388
|
+
if (enabled) {
|
|
389
|
+
section.classList.remove('hidden');
|
|
390
|
+
} else {
|
|
391
|
+
section.classList.add('hidden');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function sendEnhancedMessage(e) {
|
|
396
|
+
e.preventDefault();
|
|
397
|
+
|
|
398
|
+
// Check WhatsApp connection status first
|
|
399
|
+
try {
|
|
400
|
+
const statusResponse = await fetch('/api/status');
|
|
401
|
+
const status = await statusResponse.json();
|
|
402
|
+
|
|
403
|
+
if (!status.data || !status.data.connected) {
|
|
404
|
+
showStatus('WhatsApp is not connected. Please connect first from the dashboard.', 'error');
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
} catch (error) {
|
|
408
|
+
showStatus('Failed to check connection status. Please try again.', 'error');
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const messageType = document.getElementById('message-type').value;
|
|
413
|
+
const recipientType = document.querySelector('input[name="recipient_type"]:checked').value;
|
|
414
|
+
|
|
415
|
+
// Build message data
|
|
416
|
+
const messageData = {
|
|
417
|
+
type: messageType
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// Set recipient
|
|
421
|
+
if (recipientType === 'phone') {
|
|
422
|
+
messageData.phone = document.getElementById('phone').value.trim();
|
|
423
|
+
} else {
|
|
424
|
+
messageData.group_id = document.getElementById('group_id').value.trim();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Add content based on type
|
|
428
|
+
if (messageType === 'text') {
|
|
429
|
+
messageData.message = document.getElementById('message-text').value.trim();
|
|
430
|
+
} else if (['image', 'video', 'audio', 'document', 'sticker'].includes(messageType)) {
|
|
431
|
+
if (!messagingFileBase64) {
|
|
432
|
+
showStatus('Please select a file first', 'error');
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const caption = document.getElementById('media-caption').value.trim();
|
|
437
|
+
|
|
438
|
+
messageData.media_data = {
|
|
439
|
+
data: messagingFileBase64,
|
|
440
|
+
mime_type: messagingFileMimeType,
|
|
441
|
+
filename: messagingFileName,
|
|
442
|
+
caption: caption
|
|
443
|
+
};
|
|
444
|
+
} else if (messageType === 'location') {
|
|
445
|
+
messageData.location = {
|
|
446
|
+
latitude: parseFloat(document.getElementById('latitude').value),
|
|
447
|
+
longitude: parseFloat(document.getElementById('longitude').value),
|
|
448
|
+
name: document.getElementById('location-name').value.trim(),
|
|
449
|
+
address: document.getElementById('location-address').value.trim()
|
|
450
|
+
};
|
|
451
|
+
} else if (messageType === 'contact') {
|
|
452
|
+
const displayName = document.getElementById('contact-display-name').value.trim();
|
|
453
|
+
const vcard = document.getElementById('contact-vcard').value.trim();
|
|
454
|
+
|
|
455
|
+
if (!displayName || !vcard) {
|
|
456
|
+
showStatus('Please enter display name and vCard data', 'error');
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
messageData.contact = {
|
|
461
|
+
display_name: displayName,
|
|
462
|
+
vcard: vcard
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Add reply data if enabled
|
|
467
|
+
if (document.getElementById('enable-reply').checked) {
|
|
468
|
+
messageData.reply = {
|
|
469
|
+
message_id: document.getElementById('reply-message-id').value.trim(),
|
|
470
|
+
sender: document.getElementById('reply-sender').value.trim(),
|
|
471
|
+
content: document.getElementById('reply-content').value.trim()
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
showStatus('Sending message...', 'info');
|
|
477
|
+
|
|
478
|
+
const response = await fetch('/api/send/enhanced', {
|
|
479
|
+
method: 'POST',
|
|
480
|
+
headers: {
|
|
481
|
+
'Content-Type': 'application/json'
|
|
482
|
+
},
|
|
483
|
+
body: JSON.stringify(messageData)
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
const result = await response.json();
|
|
487
|
+
|
|
488
|
+
// Clear the "sending" message
|
|
489
|
+
document.getElementById('status-content').innerHTML = '';
|
|
490
|
+
|
|
491
|
+
// Show detailed error if available
|
|
492
|
+
if (!result.success) {
|
|
493
|
+
let errorMsg = result.error || 'Unknown error occurred';
|
|
494
|
+
|
|
495
|
+
// Provide user-friendly messages for common errors
|
|
496
|
+
if (errorMsg.includes('websocket not connected')) {
|
|
497
|
+
errorMsg = 'WhatsApp connection lost. Please check the dashboard and reconnect.';
|
|
498
|
+
} else if (errorMsg.includes('not connected')) {
|
|
499
|
+
errorMsg = 'WhatsApp is not connected. Please connect first.';
|
|
500
|
+
} else if (errorMsg.includes('invalid')) {
|
|
501
|
+
errorMsg = 'Invalid data provided: ' + errorMsg;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
showMessageStatus({ success: false, error: errorMsg });
|
|
505
|
+
} else {
|
|
506
|
+
showMessageStatus(result);
|
|
507
|
+
// Clear form on success
|
|
508
|
+
clearForm();
|
|
509
|
+
}
|
|
510
|
+
} catch (error) {
|
|
511
|
+
console.error('Error sending message:', error);
|
|
512
|
+
showMessageStatus({
|
|
513
|
+
success: false,
|
|
514
|
+
error: 'Network error: Unable to reach the API server. Please check if services are running.'
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function clearForm() {
|
|
520
|
+
document.getElementById('enhanced-message-form').reset();
|
|
521
|
+
document.getElementById('enable-reply').checked = false;
|
|
522
|
+
toggleReplySection();
|
|
523
|
+
handleMessageTypeChange();
|
|
524
|
+
handleRecipientTypeChange();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function showMessageStatus(result) {
|
|
528
|
+
const statusDiv = document.getElementById('message-status');
|
|
529
|
+
const content = document.getElementById('status-content');
|
|
530
|
+
|
|
531
|
+
const timestamp = new Date().toLocaleString();
|
|
532
|
+
const statusClass = result.success ? 'bg-green-50 text-green-800 border-green-200' : 'bg-red-50 text-red-800 border-red-200';
|
|
533
|
+
|
|
534
|
+
const statusHtml = `
|
|
535
|
+
<div class="border rounded-lg p-4 ${statusClass}">
|
|
536
|
+
<div class="flex justify-between items-start">
|
|
537
|
+
<div class="flex-1">
|
|
538
|
+
<h3 class="font-semibold">${result.success ? 'Success' : 'Error'}</h3>
|
|
539
|
+
<p class="text-sm mt-1">${result.success ? (result.message || 'Message sent successfully') : result.error}</p>
|
|
540
|
+
<p class="text-xs mt-2 opacity-75">${timestamp}</p>
|
|
541
|
+
</div>
|
|
542
|
+
<button onclick="this.parentElement.parentElement.remove()" class="text-gray-400 hover:text-gray-600">
|
|
543
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
544
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
545
|
+
</svg>
|
|
546
|
+
</button>
|
|
547
|
+
</div>
|
|
548
|
+
</div>
|
|
549
|
+
`;
|
|
550
|
+
|
|
551
|
+
content.insertAdjacentHTML('afterbegin', statusHtml);
|
|
552
|
+
statusDiv.classList.remove('hidden');
|
|
553
|
+
|
|
554
|
+
// Auto-remove after 10 seconds for success messages
|
|
555
|
+
if (result.success) {
|
|
556
|
+
setTimeout(() => {
|
|
557
|
+
const firstStatus = content.firstElementChild;
|
|
558
|
+
if (firstStatus) {
|
|
559
|
+
firstStatus.remove();
|
|
560
|
+
if (content.children.length === 0) {
|
|
561
|
+
statusDiv.classList.add('hidden');
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}, 10000);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function fillVCardTemplate() {
|
|
569
|
+
const template = `BEGIN:VCARD
|
|
570
|
+
VERSION:3.0
|
|
571
|
+
FN:John Doe
|
|
572
|
+
TEL:+1234567890
|
|
573
|
+
EMAIL:john@example.com
|
|
574
|
+
ORG:Example Company
|
|
575
|
+
TITLE:Software Engineer
|
|
576
|
+
END:VCARD`;
|
|
577
|
+
|
|
578
|
+
document.getElementById('contact-display-name').value = 'John Doe';
|
|
579
|
+
document.getElementById('contact-vcard').value = template;
|
|
580
|
+
showStatus('vCard template filled. Please customize the details.', 'success');
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async function loadGroupsDropdown() {
|
|
584
|
+
const select = document.getElementById('group_id');
|
|
585
|
+
const status = document.getElementById('groups-status');
|
|
586
|
+
|
|
587
|
+
status.textContent = 'Loading groups...';
|
|
588
|
+
status.className = 'text-xs text-blue-500 mt-1';
|
|
589
|
+
|
|
590
|
+
try {
|
|
591
|
+
const response = await fetch('/api/groups');
|
|
592
|
+
const data = await response.json();
|
|
593
|
+
|
|
594
|
+
if (data.success && data.data) {
|
|
595
|
+
const groups = data.data;
|
|
596
|
+
select.innerHTML = '<option value="">-- Select a group --</option>';
|
|
597
|
+
|
|
598
|
+
groups.forEach(group => {
|
|
599
|
+
const option = document.createElement('option');
|
|
600
|
+
option.value = group.jid;
|
|
601
|
+
option.textContent = `${group.name} (${group.size} members)`;
|
|
602
|
+
select.appendChild(option);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
status.textContent = `${groups.length} groups loaded`;
|
|
606
|
+
status.className = 'text-xs text-green-600 mt-1';
|
|
607
|
+
} else {
|
|
608
|
+
status.textContent = data.error || 'Failed to load groups';
|
|
609
|
+
status.className = 'text-xs text-red-500 mt-1';
|
|
610
|
+
}
|
|
611
|
+
} catch (error) {
|
|
612
|
+
status.textContent = 'Error: ' + error.message;
|
|
613
|
+
status.className = 'text-xs text-red-500 mt-1';
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function showStatus(message, type) {
|
|
618
|
+
if (type === 'info') {
|
|
619
|
+
// Show info message differently
|
|
620
|
+
const statusDiv = document.getElementById('message-status');
|
|
621
|
+
const content = document.getElementById('status-content');
|
|
622
|
+
|
|
623
|
+
const statusHtml = `
|
|
624
|
+
<div class="border rounded-lg p-4 bg-blue-50 text-blue-800 border-blue-200">
|
|
625
|
+
<div class="flex items-center">
|
|
626
|
+
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
627
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
|
628
|
+
</svg>
|
|
629
|
+
<span>${message}</span>
|
|
630
|
+
</div>
|
|
631
|
+
</div>
|
|
632
|
+
`;
|
|
633
|
+
|
|
634
|
+
content.innerHTML = statusHtml;
|
|
635
|
+
statusDiv.classList.remove('hidden');
|
|
636
|
+
} else {
|
|
637
|
+
showMessageStatus({
|
|
638
|
+
success: type === 'success',
|
|
639
|
+
message: type === 'success' ? message : null,
|
|
640
|
+
error: type === 'error' ? message : null
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function sendTypingIndicator() {
|
|
646
|
+
const recipientType = document.querySelector('input[name="recipient_type"]:checked').value;
|
|
647
|
+
let jid;
|
|
648
|
+
|
|
649
|
+
if (recipientType === 'phone') {
|
|
650
|
+
const phone = document.getElementById('phone').value.trim();
|
|
651
|
+
if (!phone) {
|
|
652
|
+
showStatus('Please enter a phone number first', 'error');
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
jid = phone + '@s.whatsapp.net';
|
|
656
|
+
} else {
|
|
657
|
+
jid = document.getElementById('group_id').value.trim();
|
|
658
|
+
if (!jid) {
|
|
659
|
+
showStatus('Please select a group first', 'error');
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Send typing indicator via WebSocket
|
|
665
|
+
socket.emit('typing', {
|
|
666
|
+
jid: jid,
|
|
667
|
+
state: 'composing'
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
showStatus('Sending typing indicator...', 'info');
|
|
671
|
+
|
|
672
|
+
// Auto-stop typing after 5 seconds
|
|
673
|
+
setTimeout(() => {
|
|
674
|
+
socket.emit('typing', {
|
|
675
|
+
jid: jid,
|
|
676
|
+
state: 'paused'
|
|
677
|
+
});
|
|
678
|
+
}, 5000);
|
|
679
|
+
}
|
|
680
|
+
</script>
|
|
681
681
|
{% endblock %}
|