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.
Files changed (420) hide show
  1. package/.env.template +71 -71
  2. package/LICENSE +21 -21
  3. package/README.md +145 -87
  4. package/bin/cli.js +62 -106
  5. package/client/.dockerignore +45 -45
  6. package/client/Dockerfile +68 -68
  7. package/client/dist/assets/index-DFSC53FP.css +1 -0
  8. package/client/dist/assets/index-fJ-1gTf5.js +613 -0
  9. package/client/dist/index.html +14 -0
  10. package/client/eslint.config.js +34 -16
  11. package/client/nginx.conf +66 -66
  12. package/client/package.json +61 -48
  13. package/client/src/App.tsx +27 -27
  14. package/client/src/Dashboard.tsx +1200 -1172
  15. package/client/src/ParameterPanel.tsx +302 -300
  16. package/client/src/components/AIAgentNode.tsx +315 -321
  17. package/client/src/components/APIKeyValidator.tsx +117 -117
  18. package/client/src/components/ClaudeChatModelNode.tsx +17 -17
  19. package/client/src/components/CredentialsModal.tsx +1200 -306
  20. package/client/src/components/GeminiChatModelNode.tsx +17 -17
  21. package/client/src/components/GenericNode.tsx +356 -356
  22. package/client/src/components/LocationParameterPanel.tsx +153 -153
  23. package/client/src/components/ModelNode.tsx +285 -285
  24. package/client/src/components/OpenAIChatModelNode.tsx +17 -17
  25. package/client/src/components/OutputPanel.tsx +470 -470
  26. package/client/src/components/ParameterRenderer.tsx +1873 -1873
  27. package/client/src/components/SkillEditorModal.tsx +3 -3
  28. package/client/src/components/SquareNode.tsx +812 -796
  29. package/client/src/components/ToolkitNode.tsx +365 -365
  30. package/client/src/components/auth/LoginPage.tsx +247 -247
  31. package/client/src/components/auth/ProtectedRoute.tsx +59 -59
  32. package/client/src/components/base/BaseChatModelNode.tsx +270 -270
  33. package/client/src/components/icons/AIProviderIcons.tsx +50 -50
  34. package/client/src/components/maps/GoogleMapsPicker.tsx +136 -136
  35. package/client/src/components/maps/MapsPreviewPanel.tsx +109 -109
  36. package/client/src/components/maps/index.ts +25 -25
  37. package/client/src/components/parameterPanel/InputSection.tsx +1094 -1094
  38. package/client/src/components/parameterPanel/LocationPanelLayout.tsx +64 -64
  39. package/client/src/components/parameterPanel/MapsSection.tsx +91 -91
  40. package/client/src/components/parameterPanel/MiddleSection.tsx +867 -571
  41. package/client/src/components/parameterPanel/OutputSection.tsx +80 -80
  42. package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +81 -81
  43. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -436
  44. package/client/src/components/parameterPanel/index.ts +41 -41
  45. package/client/src/components/shared/DataPanel.tsx +142 -142
  46. package/client/src/components/shared/JSONTreeRenderer.tsx +105 -105
  47. package/client/src/components/ui/AIResultModal.tsx +203 -203
  48. package/client/src/components/ui/ApiKeyInput.tsx +93 -0
  49. package/client/src/components/ui/CodeEditor.tsx +81 -81
  50. package/client/src/components/ui/CollapsibleSection.tsx +87 -87
  51. package/client/src/components/ui/ComponentItem.tsx +153 -153
  52. package/client/src/components/ui/ComponentPalette.tsx +320 -320
  53. package/client/src/components/ui/ConsolePanel.tsx +151 -43
  54. package/client/src/components/ui/ErrorBoundary.tsx +195 -195
  55. package/client/src/components/ui/InputNodesPanel.tsx +203 -203
  56. package/client/src/components/ui/MapSelector.tsx +313 -313
  57. package/client/src/components/ui/Modal.tsx +151 -148
  58. package/client/src/components/ui/NodeOutputPanel.tsx +1150 -1150
  59. package/client/src/components/ui/OutputDisplayPanel.tsx +381 -381
  60. package/client/src/components/ui/QRCodeDisplay.tsx +182 -0
  61. package/client/src/components/ui/TopToolbar.tsx +736 -736
  62. package/client/src/components/ui/WorkflowSidebar.tsx +293 -293
  63. package/client/src/config/antdTheme.ts +186 -186
  64. package/client/src/contexts/AuthContext.tsx +221 -221
  65. package/client/src/contexts/ThemeContext.tsx +42 -42
  66. package/client/src/contexts/WebSocketContext.tsx +2144 -1971
  67. package/client/src/factories/baseChatModelFactory.ts +255 -255
  68. package/client/src/hooks/useAndroidOperations.ts +118 -164
  69. package/client/src/hooks/useApiKeyValidation.ts +106 -106
  70. package/client/src/hooks/useApiKeys.ts +238 -238
  71. package/client/src/hooks/useAppTheme.ts +17 -17
  72. package/client/src/hooks/useComponentPalette.ts +50 -50
  73. package/client/src/hooks/useDragAndDrop.ts +123 -123
  74. package/client/src/hooks/useDragVariable.ts +88 -88
  75. package/client/src/hooks/useExecution.ts +319 -313
  76. package/client/src/hooks/useParameterPanel.ts +176 -176
  77. package/client/src/hooks/useReactFlowNodes.ts +188 -188
  78. package/client/src/hooks/useToolSchema.ts +209 -209
  79. package/client/src/hooks/useWhatsApp.ts +196 -196
  80. package/client/src/hooks/useWorkflowManagement.ts +45 -45
  81. package/client/src/index.css +314 -314
  82. package/client/src/nodeDefinitions/aiAgentNodes.ts +335 -335
  83. package/client/src/nodeDefinitions/aiModelNodes.ts +340 -340
  84. package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -383
  85. package/client/src/nodeDefinitions/chatNodes.ts +135 -135
  86. package/client/src/nodeDefinitions/codeNodes.ts +54 -54
  87. package/client/src/nodeDefinitions/index.ts +14 -14
  88. package/client/src/nodeDefinitions/locationNodes.ts +462 -462
  89. package/client/src/nodeDefinitions/schedulerNodes.ts +220 -220
  90. package/client/src/nodeDefinitions/skillNodes.ts +17 -5
  91. package/client/src/nodeDefinitions/utilityNodes.ts +284 -284
  92. package/client/src/nodeDefinitions/whatsappNodes.ts +821 -865
  93. package/client/src/nodeDefinitions.ts +101 -103
  94. package/client/src/services/dynamicParameterService.ts +95 -95
  95. package/client/src/services/execution/aiAgentExecutionService.ts +34 -34
  96. package/client/src/services/executionService.ts +227 -231
  97. package/client/src/services/workflowApi.ts +91 -91
  98. package/client/src/store/useAppStore.ts +578 -581
  99. package/client/src/styles/theme.ts +513 -508
  100. package/client/src/styles/zIndex.ts +16 -16
  101. package/client/src/types/ComponentTypes.ts +38 -38
  102. package/client/src/types/INodeProperties.ts +287 -287
  103. package/client/src/types/NodeTypes.ts +27 -27
  104. package/client/src/utils/formatters.ts +32 -32
  105. package/client/src/utils/googleMapsLoader.ts +139 -139
  106. package/client/src/utils/locationUtils.ts +84 -84
  107. package/client/src/utils/nodeUtils.ts +30 -30
  108. package/client/src/utils/workflow.ts +29 -29
  109. package/client/src/vite-env.d.ts +12 -12
  110. package/client/tailwind.config.js +59 -59
  111. package/client/tsconfig.json +25 -25
  112. package/client/vite.config.js +35 -35
  113. package/package.json +78 -70
  114. package/scripts/build.js +153 -45
  115. package/scripts/clean.js +40 -40
  116. package/scripts/start.js +234 -210
  117. package/scripts/stop.js +301 -325
  118. package/server/.dockerignore +44 -44
  119. package/server/Dockerfile +45 -45
  120. package/server/constants.py +244 -249
  121. package/server/core/cache.py +460 -460
  122. package/server/core/config.py +127 -127
  123. package/server/core/container.py +98 -98
  124. package/server/core/database.py +1296 -1210
  125. package/server/core/logging.py +313 -313
  126. package/server/main.py +288 -288
  127. package/server/middleware/__init__.py +5 -5
  128. package/server/middleware/auth.py +89 -89
  129. package/server/models/auth.py +52 -52
  130. package/server/models/cache.py +24 -24
  131. package/server/models/database.py +235 -210
  132. package/server/models/nodes.py +435 -455
  133. package/server/pyproject.toml +75 -72
  134. package/server/requirements.txt +83 -83
  135. package/server/routers/android.py +294 -294
  136. package/server/routers/auth.py +203 -203
  137. package/server/routers/database.py +150 -150
  138. package/server/routers/maps.py +141 -141
  139. package/server/routers/nodejs_compat.py +288 -288
  140. package/server/routers/webhook.py +90 -90
  141. package/server/routers/websocket.py +2239 -2127
  142. package/server/routers/whatsapp.py +761 -761
  143. package/server/routers/workflow.py +199 -199
  144. package/server/services/ai.py +2444 -2414
  145. package/server/services/android_service.py +588 -588
  146. package/server/services/auth.py +130 -130
  147. package/server/services/chat_client.py +160 -160
  148. package/server/services/deployment/manager.py +706 -706
  149. package/server/services/event_waiter.py +675 -785
  150. package/server/services/execution/executor.py +1351 -1351
  151. package/server/services/execution/models.py +1 -1
  152. package/server/services/handlers/__init__.py +122 -126
  153. package/server/services/handlers/ai.py +390 -355
  154. package/server/services/handlers/android.py +69 -260
  155. package/server/services/handlers/code.py +278 -278
  156. package/server/services/handlers/http.py +193 -193
  157. package/server/services/handlers/tools.py +146 -32
  158. package/server/services/handlers/triggers.py +107 -107
  159. package/server/services/handlers/utility.py +822 -822
  160. package/server/services/handlers/whatsapp.py +423 -476
  161. package/server/services/maps.py +288 -288
  162. package/server/services/memory_store.py +103 -103
  163. package/server/services/node_executor.py +372 -375
  164. package/server/services/scheduler.py +155 -155
  165. package/server/services/skill_loader.py +1 -1
  166. package/server/services/status_broadcaster.py +834 -826
  167. package/server/services/temporal/__init__.py +23 -23
  168. package/server/services/temporal/activities.py +344 -344
  169. package/server/services/temporal/client.py +76 -76
  170. package/server/services/temporal/executor.py +147 -147
  171. package/server/services/temporal/worker.py +251 -251
  172. package/server/services/temporal/workflow.py +355 -355
  173. package/server/services/temporal/ws_client.py +236 -236
  174. package/server/services/text.py +110 -110
  175. package/server/services/user_auth.py +172 -172
  176. package/server/services/websocket_client.py +29 -29
  177. package/server/services/workflow.py +597 -597
  178. package/server/skills/android-skill/SKILL.md +4 -4
  179. package/server/skills/code-skill/SKILL.md +123 -89
  180. package/server/skills/maps-skill/SKILL.md +3 -3
  181. package/server/skills/memory-skill/SKILL.md +1 -1
  182. package/server/skills/web-search-skill/SKILL.md +154 -0
  183. package/server/skills/whatsapp-skill/SKILL.md +3 -3
  184. package/server/uv.lock +461 -100
  185. package/server/whatsapp-rpc/.dockerignore +30 -30
  186. package/server/whatsapp-rpc/Dockerfile +44 -44
  187. package/server/whatsapp-rpc/Dockerfile.web +17 -17
  188. package/server/whatsapp-rpc/README.md +139 -139
  189. package/server/whatsapp-rpc/bin/whatsapp-rpc-server +0 -0
  190. package/server/whatsapp-rpc/cli.js +95 -95
  191. package/server/whatsapp-rpc/configs/config.yaml +6 -6
  192. package/server/whatsapp-rpc/docker-compose.yml +35 -35
  193. package/server/whatsapp-rpc/docs/API.md +410 -410
  194. package/server/whatsapp-rpc/node_modules/.package-lock.json +259 -0
  195. package/server/whatsapp-rpc/node_modules/chalk/license +9 -0
  196. package/server/whatsapp-rpc/node_modules/chalk/package.json +83 -0
  197. package/server/whatsapp-rpc/node_modules/chalk/readme.md +297 -0
  198. package/server/whatsapp-rpc/node_modules/chalk/source/index.d.ts +325 -0
  199. package/server/whatsapp-rpc/node_modules/chalk/source/index.js +225 -0
  200. package/server/whatsapp-rpc/node_modules/chalk/source/utilities.js +33 -0
  201. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  202. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  203. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  204. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  205. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  206. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  207. package/server/whatsapp-rpc/node_modules/commander/LICENSE +22 -0
  208. package/server/whatsapp-rpc/node_modules/commander/Readme.md +1148 -0
  209. package/server/whatsapp-rpc/node_modules/commander/esm.mjs +16 -0
  210. package/server/whatsapp-rpc/node_modules/commander/index.js +26 -0
  211. package/server/whatsapp-rpc/node_modules/commander/lib/argument.js +145 -0
  212. package/server/whatsapp-rpc/node_modules/commander/lib/command.js +2179 -0
  213. package/server/whatsapp-rpc/node_modules/commander/lib/error.js +43 -0
  214. package/server/whatsapp-rpc/node_modules/commander/lib/help.js +462 -0
  215. package/server/whatsapp-rpc/node_modules/commander/lib/option.js +329 -0
  216. package/server/whatsapp-rpc/node_modules/commander/lib/suggestSimilar.js +100 -0
  217. package/server/whatsapp-rpc/node_modules/commander/package-support.json +16 -0
  218. package/server/whatsapp-rpc/node_modules/commander/package.json +80 -0
  219. package/server/whatsapp-rpc/node_modules/commander/typings/esm.d.mts +3 -0
  220. package/server/whatsapp-rpc/node_modules/commander/typings/index.d.ts +884 -0
  221. package/server/whatsapp-rpc/node_modules/cross-spawn/LICENSE +21 -0
  222. package/server/whatsapp-rpc/node_modules/cross-spawn/README.md +89 -0
  223. package/server/whatsapp-rpc/node_modules/cross-spawn/index.js +39 -0
  224. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/enoent.js +59 -0
  225. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/parse.js +91 -0
  226. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/escape.js +47 -0
  227. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/readShebang.js +23 -0
  228. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/resolveCommand.js +52 -0
  229. package/server/whatsapp-rpc/node_modules/cross-spawn/package.json +73 -0
  230. package/server/whatsapp-rpc/node_modules/execa/index.d.ts +955 -0
  231. package/server/whatsapp-rpc/node_modules/execa/index.js +309 -0
  232. package/server/whatsapp-rpc/node_modules/execa/lib/command.js +119 -0
  233. package/server/whatsapp-rpc/node_modules/execa/lib/error.js +87 -0
  234. package/server/whatsapp-rpc/node_modules/execa/lib/kill.js +102 -0
  235. package/server/whatsapp-rpc/node_modules/execa/lib/pipe.js +42 -0
  236. package/server/whatsapp-rpc/node_modules/execa/lib/promise.js +36 -0
  237. package/server/whatsapp-rpc/node_modules/execa/lib/stdio.js +49 -0
  238. package/server/whatsapp-rpc/node_modules/execa/lib/stream.js +133 -0
  239. package/server/whatsapp-rpc/node_modules/execa/lib/verbose.js +19 -0
  240. package/server/whatsapp-rpc/node_modules/execa/license +9 -0
  241. package/server/whatsapp-rpc/node_modules/execa/package.json +90 -0
  242. package/server/whatsapp-rpc/node_modules/execa/readme.md +822 -0
  243. package/server/whatsapp-rpc/node_modules/get-stream/license +9 -0
  244. package/server/whatsapp-rpc/node_modules/get-stream/package.json +53 -0
  245. package/server/whatsapp-rpc/node_modules/get-stream/readme.md +291 -0
  246. package/server/whatsapp-rpc/node_modules/get-stream/source/array-buffer.js +84 -0
  247. package/server/whatsapp-rpc/node_modules/get-stream/source/array.js +32 -0
  248. package/server/whatsapp-rpc/node_modules/get-stream/source/buffer.js +20 -0
  249. package/server/whatsapp-rpc/node_modules/get-stream/source/contents.js +101 -0
  250. package/server/whatsapp-rpc/node_modules/get-stream/source/index.d.ts +119 -0
  251. package/server/whatsapp-rpc/node_modules/get-stream/source/index.js +5 -0
  252. package/server/whatsapp-rpc/node_modules/get-stream/source/string.js +36 -0
  253. package/server/whatsapp-rpc/node_modules/get-stream/source/utils.js +11 -0
  254. package/server/whatsapp-rpc/node_modules/get-them-args/LICENSE +21 -0
  255. package/server/whatsapp-rpc/node_modules/get-them-args/README.md +95 -0
  256. package/server/whatsapp-rpc/node_modules/get-them-args/index.js +97 -0
  257. package/server/whatsapp-rpc/node_modules/get-them-args/package.json +36 -0
  258. package/server/whatsapp-rpc/node_modules/human-signals/LICENSE +201 -0
  259. package/server/whatsapp-rpc/node_modules/human-signals/README.md +168 -0
  260. package/server/whatsapp-rpc/node_modules/human-signals/build/src/core.js +273 -0
  261. package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.d.ts +73 -0
  262. package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.js +70 -0
  263. package/server/whatsapp-rpc/node_modules/human-signals/build/src/realtime.js +16 -0
  264. package/server/whatsapp-rpc/node_modules/human-signals/build/src/signals.js +34 -0
  265. package/server/whatsapp-rpc/node_modules/human-signals/package.json +61 -0
  266. package/server/whatsapp-rpc/node_modules/is-stream/index.d.ts +81 -0
  267. package/server/whatsapp-rpc/node_modules/is-stream/index.js +29 -0
  268. package/server/whatsapp-rpc/node_modules/is-stream/license +9 -0
  269. package/server/whatsapp-rpc/node_modules/is-stream/package.json +44 -0
  270. package/server/whatsapp-rpc/node_modules/is-stream/readme.md +60 -0
  271. package/server/whatsapp-rpc/node_modules/isexe/LICENSE +15 -0
  272. package/server/whatsapp-rpc/node_modules/isexe/README.md +51 -0
  273. package/server/whatsapp-rpc/node_modules/isexe/index.js +57 -0
  274. package/server/whatsapp-rpc/node_modules/isexe/mode.js +41 -0
  275. package/server/whatsapp-rpc/node_modules/isexe/package.json +31 -0
  276. package/server/whatsapp-rpc/node_modules/isexe/test/basic.js +221 -0
  277. package/server/whatsapp-rpc/node_modules/isexe/windows.js +42 -0
  278. package/server/whatsapp-rpc/node_modules/kill-port/.editorconfig +12 -0
  279. package/server/whatsapp-rpc/node_modules/kill-port/.gitattributes +1 -0
  280. package/server/whatsapp-rpc/node_modules/kill-port/LICENSE +21 -0
  281. package/server/whatsapp-rpc/node_modules/kill-port/README.md +140 -0
  282. package/server/whatsapp-rpc/node_modules/kill-port/cli.js +25 -0
  283. package/server/whatsapp-rpc/node_modules/kill-port/example.js +21 -0
  284. package/server/whatsapp-rpc/node_modules/kill-port/index.js +46 -0
  285. package/server/whatsapp-rpc/node_modules/kill-port/logo.png +0 -0
  286. package/server/whatsapp-rpc/node_modules/kill-port/package.json +41 -0
  287. package/server/whatsapp-rpc/node_modules/kill-port/pnpm-lock.yaml +4606 -0
  288. package/server/whatsapp-rpc/node_modules/kill-port/test.js +16 -0
  289. package/server/whatsapp-rpc/node_modules/merge-stream/LICENSE +21 -0
  290. package/server/whatsapp-rpc/node_modules/merge-stream/README.md +78 -0
  291. package/server/whatsapp-rpc/node_modules/merge-stream/index.js +41 -0
  292. package/server/whatsapp-rpc/node_modules/merge-stream/package.json +19 -0
  293. package/server/whatsapp-rpc/node_modules/mimic-fn/index.d.ts +52 -0
  294. package/server/whatsapp-rpc/node_modules/mimic-fn/index.js +71 -0
  295. package/server/whatsapp-rpc/node_modules/mimic-fn/license +9 -0
  296. package/server/whatsapp-rpc/node_modules/mimic-fn/package.json +45 -0
  297. package/server/whatsapp-rpc/node_modules/mimic-fn/readme.md +90 -0
  298. package/server/whatsapp-rpc/node_modules/npm-run-path/index.d.ts +90 -0
  299. package/server/whatsapp-rpc/node_modules/npm-run-path/index.js +52 -0
  300. package/server/whatsapp-rpc/node_modules/npm-run-path/license +9 -0
  301. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.d.ts +31 -0
  302. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.js +12 -0
  303. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/license +9 -0
  304. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/package.json +41 -0
  305. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/readme.md +57 -0
  306. package/server/whatsapp-rpc/node_modules/npm-run-path/package.json +49 -0
  307. package/server/whatsapp-rpc/node_modules/npm-run-path/readme.md +104 -0
  308. package/server/whatsapp-rpc/node_modules/onetime/index.d.ts +59 -0
  309. package/server/whatsapp-rpc/node_modules/onetime/index.js +41 -0
  310. package/server/whatsapp-rpc/node_modules/onetime/license +9 -0
  311. package/server/whatsapp-rpc/node_modules/onetime/package.json +45 -0
  312. package/server/whatsapp-rpc/node_modules/onetime/readme.md +94 -0
  313. package/server/whatsapp-rpc/node_modules/path-key/index.d.ts +40 -0
  314. package/server/whatsapp-rpc/node_modules/path-key/index.js +16 -0
  315. package/server/whatsapp-rpc/node_modules/path-key/license +9 -0
  316. package/server/whatsapp-rpc/node_modules/path-key/package.json +39 -0
  317. package/server/whatsapp-rpc/node_modules/path-key/readme.md +61 -0
  318. package/server/whatsapp-rpc/node_modules/shebang-command/index.js +19 -0
  319. package/server/whatsapp-rpc/node_modules/shebang-command/license +9 -0
  320. package/server/whatsapp-rpc/node_modules/shebang-command/package.json +34 -0
  321. package/server/whatsapp-rpc/node_modules/shebang-command/readme.md +34 -0
  322. package/server/whatsapp-rpc/node_modules/shebang-regex/index.d.ts +22 -0
  323. package/server/whatsapp-rpc/node_modules/shebang-regex/index.js +2 -0
  324. package/server/whatsapp-rpc/node_modules/shebang-regex/license +9 -0
  325. package/server/whatsapp-rpc/node_modules/shebang-regex/package.json +35 -0
  326. package/server/whatsapp-rpc/node_modules/shebang-regex/readme.md +33 -0
  327. package/server/whatsapp-rpc/node_modules/shell-exec/LICENSE +21 -0
  328. package/server/whatsapp-rpc/node_modules/shell-exec/README.md +60 -0
  329. package/server/whatsapp-rpc/node_modules/shell-exec/index.js +47 -0
  330. package/server/whatsapp-rpc/node_modules/shell-exec/package.json +29 -0
  331. package/server/whatsapp-rpc/node_modules/signal-exit/LICENSE.txt +16 -0
  332. package/server/whatsapp-rpc/node_modules/signal-exit/README.md +74 -0
  333. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts +12 -0
  334. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts.map +1 -0
  335. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js +10 -0
  336. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js.map +1 -0
  337. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts +48 -0
  338. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts.map +1 -0
  339. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js +279 -0
  340. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js.map +1 -0
  341. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/package.json +3 -0
  342. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts +29 -0
  343. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts.map +1 -0
  344. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js +42 -0
  345. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js.map +1 -0
  346. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts +12 -0
  347. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts.map +1 -0
  348. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js +4 -0
  349. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js.map +1 -0
  350. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts +48 -0
  351. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts.map +1 -0
  352. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js +275 -0
  353. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js.map +1 -0
  354. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/package.json +3 -0
  355. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts +29 -0
  356. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts.map +1 -0
  357. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js +39 -0
  358. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js.map +1 -0
  359. package/server/whatsapp-rpc/node_modules/signal-exit/package.json +106 -0
  360. package/server/whatsapp-rpc/node_modules/strip-final-newline/index.js +14 -0
  361. package/server/whatsapp-rpc/node_modules/strip-final-newline/license +9 -0
  362. package/server/whatsapp-rpc/node_modules/strip-final-newline/package.json +43 -0
  363. package/server/whatsapp-rpc/node_modules/strip-final-newline/readme.md +35 -0
  364. package/server/whatsapp-rpc/node_modules/which/CHANGELOG.md +166 -0
  365. package/server/whatsapp-rpc/node_modules/which/LICENSE +15 -0
  366. package/server/whatsapp-rpc/node_modules/which/README.md +54 -0
  367. package/server/whatsapp-rpc/node_modules/which/bin/node-which +52 -0
  368. package/server/whatsapp-rpc/node_modules/which/package.json +43 -0
  369. package/server/whatsapp-rpc/node_modules/which/which.js +125 -0
  370. package/server/whatsapp-rpc/package-lock.json +272 -0
  371. package/server/whatsapp-rpc/package.json +30 -30
  372. package/server/whatsapp-rpc/schema.json +1294 -1294
  373. package/server/whatsapp-rpc/scripts/clean.cjs +66 -66
  374. package/server/whatsapp-rpc/scripts/cli.js +162 -162
  375. package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -166
  376. package/server/whatsapp-rpc/src/python/pyproject.toml +15 -15
  377. package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -4
  378. package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -427
  379. package/server/whatsapp-rpc/web/app.py +609 -609
  380. package/server/whatsapp-rpc/web/requirements.txt +6 -6
  381. package/server/whatsapp-rpc/web/rpc_client.py +427 -427
  382. package/server/whatsapp-rpc/web/static/openapi.yaml +59 -59
  383. package/server/whatsapp-rpc/web/templates/base.html +149 -149
  384. package/server/whatsapp-rpc/web/templates/contacts.html +240 -240
  385. package/server/whatsapp-rpc/web/templates/dashboard.html +319 -319
  386. package/server/whatsapp-rpc/web/templates/groups.html +328 -328
  387. package/server/whatsapp-rpc/web/templates/messages.html +465 -465
  388. package/server/whatsapp-rpc/web/templates/messaging.html +680 -680
  389. package/server/whatsapp-rpc/web/templates/send.html +258 -258
  390. package/server/whatsapp-rpc/web/templates/settings.html +459 -459
  391. package/client/src/components/ui/AndroidSettingsPanel.tsx +0 -401
  392. package/client/src/components/ui/WhatsAppSettingsPanel.tsx +0 -345
  393. package/client/src/nodeDefinitions/androidDeviceNodes.ts +0 -140
  394. package/docker-compose.prod.yml +0 -107
  395. package/docker-compose.yml +0 -104
  396. package/docs-MachinaOs/README.md +0 -85
  397. package/docs-MachinaOs/deployment/docker.mdx +0 -228
  398. package/docs-MachinaOs/deployment/production.mdx +0 -345
  399. package/docs-MachinaOs/docs.json +0 -75
  400. package/docs-MachinaOs/faq.mdx +0 -309
  401. package/docs-MachinaOs/favicon.svg +0 -5
  402. package/docs-MachinaOs/installation.mdx +0 -160
  403. package/docs-MachinaOs/introduction.mdx +0 -114
  404. package/docs-MachinaOs/logo/dark.svg +0 -6
  405. package/docs-MachinaOs/logo/light.svg +0 -6
  406. package/docs-MachinaOs/nodes/ai-agent.mdx +0 -216
  407. package/docs-MachinaOs/nodes/ai-models.mdx +0 -240
  408. package/docs-MachinaOs/nodes/android.mdx +0 -411
  409. package/docs-MachinaOs/nodes/overview.mdx +0 -181
  410. package/docs-MachinaOs/nodes/schedulers.mdx +0 -316
  411. package/docs-MachinaOs/nodes/webhooks.mdx +0 -330
  412. package/docs-MachinaOs/nodes/whatsapp.mdx +0 -305
  413. package/docs-MachinaOs/quickstart.mdx +0 -119
  414. package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +0 -177
  415. package/docs-MachinaOs/tutorials/android-automation.mdx +0 -242
  416. package/docs-MachinaOs/tutorials/first-workflow.mdx +0 -134
  417. package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +0 -185
  418. package/nul +0 -0
  419. package/scripts/check-ports.ps1 +0 -33
  420. 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&#10;VERSION:3.0&#10;FN:John Doe&#10;TEL:+1234567890&#10;EMAIL:john@example.com&#10;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&#10;VERSION:3.0&#10;FN:John Doe&#10;TEL:+1234567890&#10;EMAIL:john@example.com&#10;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 %}