machinaos 0.0.1 → 0.0.7

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 (422) hide show
  1. package/.env.template +71 -71
  2. package/LICENSE +21 -21
  3. package/README.md +163 -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/install.ps1 +308 -0
  114. package/install.sh +343 -0
  115. package/package.json +81 -70
  116. package/scripts/build.js +174 -51
  117. package/scripts/clean.js +40 -40
  118. package/scripts/start.js +234 -210
  119. package/scripts/stop.js +301 -325
  120. package/server/.dockerignore +44 -44
  121. package/server/Dockerfile +45 -45
  122. package/server/constants.py +244 -249
  123. package/server/core/cache.py +460 -460
  124. package/server/core/config.py +127 -127
  125. package/server/core/container.py +98 -98
  126. package/server/core/database.py +1296 -1210
  127. package/server/core/logging.py +313 -313
  128. package/server/main.py +288 -288
  129. package/server/middleware/__init__.py +5 -5
  130. package/server/middleware/auth.py +89 -89
  131. package/server/models/auth.py +52 -52
  132. package/server/models/cache.py +24 -24
  133. package/server/models/database.py +235 -210
  134. package/server/models/nodes.py +435 -455
  135. package/server/pyproject.toml +75 -72
  136. package/server/requirements.txt +83 -83
  137. package/server/routers/android.py +294 -294
  138. package/server/routers/auth.py +203 -203
  139. package/server/routers/database.py +150 -150
  140. package/server/routers/maps.py +141 -141
  141. package/server/routers/nodejs_compat.py +288 -288
  142. package/server/routers/webhook.py +90 -90
  143. package/server/routers/websocket.py +2239 -2127
  144. package/server/routers/whatsapp.py +761 -761
  145. package/server/routers/workflow.py +199 -199
  146. package/server/services/ai.py +2444 -2414
  147. package/server/services/android_service.py +588 -588
  148. package/server/services/auth.py +130 -130
  149. package/server/services/chat_client.py +160 -160
  150. package/server/services/deployment/manager.py +706 -706
  151. package/server/services/event_waiter.py +675 -785
  152. package/server/services/execution/executor.py +1351 -1351
  153. package/server/services/execution/models.py +1 -1
  154. package/server/services/handlers/__init__.py +122 -126
  155. package/server/services/handlers/ai.py +390 -355
  156. package/server/services/handlers/android.py +69 -260
  157. package/server/services/handlers/code.py +278 -278
  158. package/server/services/handlers/http.py +193 -193
  159. package/server/services/handlers/tools.py +146 -32
  160. package/server/services/handlers/triggers.py +107 -107
  161. package/server/services/handlers/utility.py +822 -822
  162. package/server/services/handlers/whatsapp.py +423 -476
  163. package/server/services/maps.py +288 -288
  164. package/server/services/memory_store.py +103 -103
  165. package/server/services/node_executor.py +372 -375
  166. package/server/services/scheduler.py +155 -155
  167. package/server/services/skill_loader.py +1 -1
  168. package/server/services/status_broadcaster.py +834 -826
  169. package/server/services/temporal/__init__.py +23 -23
  170. package/server/services/temporal/activities.py +344 -344
  171. package/server/services/temporal/client.py +76 -76
  172. package/server/services/temporal/executor.py +147 -147
  173. package/server/services/temporal/worker.py +251 -251
  174. package/server/services/temporal/workflow.py +355 -355
  175. package/server/services/temporal/ws_client.py +236 -236
  176. package/server/services/text.py +110 -110
  177. package/server/services/user_auth.py +172 -172
  178. package/server/services/websocket_client.py +29 -29
  179. package/server/services/workflow.py +597 -597
  180. package/server/skills/android-skill/SKILL.md +4 -4
  181. package/server/skills/code-skill/SKILL.md +123 -89
  182. package/server/skills/maps-skill/SKILL.md +3 -3
  183. package/server/skills/memory-skill/SKILL.md +1 -1
  184. package/server/skills/web-search-skill/SKILL.md +154 -0
  185. package/server/skills/whatsapp-skill/SKILL.md +3 -3
  186. package/server/uv.lock +461 -100
  187. package/server/whatsapp-rpc/.dockerignore +30 -30
  188. package/server/whatsapp-rpc/Dockerfile +44 -44
  189. package/server/whatsapp-rpc/Dockerfile.web +17 -17
  190. package/server/whatsapp-rpc/README.md +139 -139
  191. package/server/whatsapp-rpc/bin/whatsapp-rpc-server +0 -0
  192. package/server/whatsapp-rpc/cli.js +95 -95
  193. package/server/whatsapp-rpc/configs/config.yaml +6 -6
  194. package/server/whatsapp-rpc/docker-compose.yml +35 -35
  195. package/server/whatsapp-rpc/docs/API.md +410 -410
  196. package/server/whatsapp-rpc/node_modules/.package-lock.json +259 -0
  197. package/server/whatsapp-rpc/node_modules/chalk/license +9 -0
  198. package/server/whatsapp-rpc/node_modules/chalk/package.json +83 -0
  199. package/server/whatsapp-rpc/node_modules/chalk/readme.md +297 -0
  200. package/server/whatsapp-rpc/node_modules/chalk/source/index.d.ts +325 -0
  201. package/server/whatsapp-rpc/node_modules/chalk/source/index.js +225 -0
  202. package/server/whatsapp-rpc/node_modules/chalk/source/utilities.js +33 -0
  203. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  204. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  205. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  206. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  207. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  208. package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  209. package/server/whatsapp-rpc/node_modules/commander/LICENSE +22 -0
  210. package/server/whatsapp-rpc/node_modules/commander/Readme.md +1148 -0
  211. package/server/whatsapp-rpc/node_modules/commander/esm.mjs +16 -0
  212. package/server/whatsapp-rpc/node_modules/commander/index.js +26 -0
  213. package/server/whatsapp-rpc/node_modules/commander/lib/argument.js +145 -0
  214. package/server/whatsapp-rpc/node_modules/commander/lib/command.js +2179 -0
  215. package/server/whatsapp-rpc/node_modules/commander/lib/error.js +43 -0
  216. package/server/whatsapp-rpc/node_modules/commander/lib/help.js +462 -0
  217. package/server/whatsapp-rpc/node_modules/commander/lib/option.js +329 -0
  218. package/server/whatsapp-rpc/node_modules/commander/lib/suggestSimilar.js +100 -0
  219. package/server/whatsapp-rpc/node_modules/commander/package-support.json +16 -0
  220. package/server/whatsapp-rpc/node_modules/commander/package.json +80 -0
  221. package/server/whatsapp-rpc/node_modules/commander/typings/esm.d.mts +3 -0
  222. package/server/whatsapp-rpc/node_modules/commander/typings/index.d.ts +884 -0
  223. package/server/whatsapp-rpc/node_modules/cross-spawn/LICENSE +21 -0
  224. package/server/whatsapp-rpc/node_modules/cross-spawn/README.md +89 -0
  225. package/server/whatsapp-rpc/node_modules/cross-spawn/index.js +39 -0
  226. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/enoent.js +59 -0
  227. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/parse.js +91 -0
  228. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/escape.js +47 -0
  229. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/readShebang.js +23 -0
  230. package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/resolveCommand.js +52 -0
  231. package/server/whatsapp-rpc/node_modules/cross-spawn/package.json +73 -0
  232. package/server/whatsapp-rpc/node_modules/execa/index.d.ts +955 -0
  233. package/server/whatsapp-rpc/node_modules/execa/index.js +309 -0
  234. package/server/whatsapp-rpc/node_modules/execa/lib/command.js +119 -0
  235. package/server/whatsapp-rpc/node_modules/execa/lib/error.js +87 -0
  236. package/server/whatsapp-rpc/node_modules/execa/lib/kill.js +102 -0
  237. package/server/whatsapp-rpc/node_modules/execa/lib/pipe.js +42 -0
  238. package/server/whatsapp-rpc/node_modules/execa/lib/promise.js +36 -0
  239. package/server/whatsapp-rpc/node_modules/execa/lib/stdio.js +49 -0
  240. package/server/whatsapp-rpc/node_modules/execa/lib/stream.js +133 -0
  241. package/server/whatsapp-rpc/node_modules/execa/lib/verbose.js +19 -0
  242. package/server/whatsapp-rpc/node_modules/execa/license +9 -0
  243. package/server/whatsapp-rpc/node_modules/execa/package.json +90 -0
  244. package/server/whatsapp-rpc/node_modules/execa/readme.md +822 -0
  245. package/server/whatsapp-rpc/node_modules/get-stream/license +9 -0
  246. package/server/whatsapp-rpc/node_modules/get-stream/package.json +53 -0
  247. package/server/whatsapp-rpc/node_modules/get-stream/readme.md +291 -0
  248. package/server/whatsapp-rpc/node_modules/get-stream/source/array-buffer.js +84 -0
  249. package/server/whatsapp-rpc/node_modules/get-stream/source/array.js +32 -0
  250. package/server/whatsapp-rpc/node_modules/get-stream/source/buffer.js +20 -0
  251. package/server/whatsapp-rpc/node_modules/get-stream/source/contents.js +101 -0
  252. package/server/whatsapp-rpc/node_modules/get-stream/source/index.d.ts +119 -0
  253. package/server/whatsapp-rpc/node_modules/get-stream/source/index.js +5 -0
  254. package/server/whatsapp-rpc/node_modules/get-stream/source/string.js +36 -0
  255. package/server/whatsapp-rpc/node_modules/get-stream/source/utils.js +11 -0
  256. package/server/whatsapp-rpc/node_modules/get-them-args/LICENSE +21 -0
  257. package/server/whatsapp-rpc/node_modules/get-them-args/README.md +95 -0
  258. package/server/whatsapp-rpc/node_modules/get-them-args/index.js +97 -0
  259. package/server/whatsapp-rpc/node_modules/get-them-args/package.json +36 -0
  260. package/server/whatsapp-rpc/node_modules/human-signals/LICENSE +201 -0
  261. package/server/whatsapp-rpc/node_modules/human-signals/README.md +168 -0
  262. package/server/whatsapp-rpc/node_modules/human-signals/build/src/core.js +273 -0
  263. package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.d.ts +73 -0
  264. package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.js +70 -0
  265. package/server/whatsapp-rpc/node_modules/human-signals/build/src/realtime.js +16 -0
  266. package/server/whatsapp-rpc/node_modules/human-signals/build/src/signals.js +34 -0
  267. package/server/whatsapp-rpc/node_modules/human-signals/package.json +61 -0
  268. package/server/whatsapp-rpc/node_modules/is-stream/index.d.ts +81 -0
  269. package/server/whatsapp-rpc/node_modules/is-stream/index.js +29 -0
  270. package/server/whatsapp-rpc/node_modules/is-stream/license +9 -0
  271. package/server/whatsapp-rpc/node_modules/is-stream/package.json +44 -0
  272. package/server/whatsapp-rpc/node_modules/is-stream/readme.md +60 -0
  273. package/server/whatsapp-rpc/node_modules/isexe/LICENSE +15 -0
  274. package/server/whatsapp-rpc/node_modules/isexe/README.md +51 -0
  275. package/server/whatsapp-rpc/node_modules/isexe/index.js +57 -0
  276. package/server/whatsapp-rpc/node_modules/isexe/mode.js +41 -0
  277. package/server/whatsapp-rpc/node_modules/isexe/package.json +31 -0
  278. package/server/whatsapp-rpc/node_modules/isexe/test/basic.js +221 -0
  279. package/server/whatsapp-rpc/node_modules/isexe/windows.js +42 -0
  280. package/server/whatsapp-rpc/node_modules/kill-port/.editorconfig +12 -0
  281. package/server/whatsapp-rpc/node_modules/kill-port/.gitattributes +1 -0
  282. package/server/whatsapp-rpc/node_modules/kill-port/LICENSE +21 -0
  283. package/server/whatsapp-rpc/node_modules/kill-port/README.md +140 -0
  284. package/server/whatsapp-rpc/node_modules/kill-port/cli.js +25 -0
  285. package/server/whatsapp-rpc/node_modules/kill-port/example.js +21 -0
  286. package/server/whatsapp-rpc/node_modules/kill-port/index.js +46 -0
  287. package/server/whatsapp-rpc/node_modules/kill-port/logo.png +0 -0
  288. package/server/whatsapp-rpc/node_modules/kill-port/package.json +41 -0
  289. package/server/whatsapp-rpc/node_modules/kill-port/pnpm-lock.yaml +4606 -0
  290. package/server/whatsapp-rpc/node_modules/kill-port/test.js +16 -0
  291. package/server/whatsapp-rpc/node_modules/merge-stream/LICENSE +21 -0
  292. package/server/whatsapp-rpc/node_modules/merge-stream/README.md +78 -0
  293. package/server/whatsapp-rpc/node_modules/merge-stream/index.js +41 -0
  294. package/server/whatsapp-rpc/node_modules/merge-stream/package.json +19 -0
  295. package/server/whatsapp-rpc/node_modules/mimic-fn/index.d.ts +52 -0
  296. package/server/whatsapp-rpc/node_modules/mimic-fn/index.js +71 -0
  297. package/server/whatsapp-rpc/node_modules/mimic-fn/license +9 -0
  298. package/server/whatsapp-rpc/node_modules/mimic-fn/package.json +45 -0
  299. package/server/whatsapp-rpc/node_modules/mimic-fn/readme.md +90 -0
  300. package/server/whatsapp-rpc/node_modules/npm-run-path/index.d.ts +90 -0
  301. package/server/whatsapp-rpc/node_modules/npm-run-path/index.js +52 -0
  302. package/server/whatsapp-rpc/node_modules/npm-run-path/license +9 -0
  303. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.d.ts +31 -0
  304. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.js +12 -0
  305. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/license +9 -0
  306. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/package.json +41 -0
  307. package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/readme.md +57 -0
  308. package/server/whatsapp-rpc/node_modules/npm-run-path/package.json +49 -0
  309. package/server/whatsapp-rpc/node_modules/npm-run-path/readme.md +104 -0
  310. package/server/whatsapp-rpc/node_modules/onetime/index.d.ts +59 -0
  311. package/server/whatsapp-rpc/node_modules/onetime/index.js +41 -0
  312. package/server/whatsapp-rpc/node_modules/onetime/license +9 -0
  313. package/server/whatsapp-rpc/node_modules/onetime/package.json +45 -0
  314. package/server/whatsapp-rpc/node_modules/onetime/readme.md +94 -0
  315. package/server/whatsapp-rpc/node_modules/path-key/index.d.ts +40 -0
  316. package/server/whatsapp-rpc/node_modules/path-key/index.js +16 -0
  317. package/server/whatsapp-rpc/node_modules/path-key/license +9 -0
  318. package/server/whatsapp-rpc/node_modules/path-key/package.json +39 -0
  319. package/server/whatsapp-rpc/node_modules/path-key/readme.md +61 -0
  320. package/server/whatsapp-rpc/node_modules/shebang-command/index.js +19 -0
  321. package/server/whatsapp-rpc/node_modules/shebang-command/license +9 -0
  322. package/server/whatsapp-rpc/node_modules/shebang-command/package.json +34 -0
  323. package/server/whatsapp-rpc/node_modules/shebang-command/readme.md +34 -0
  324. package/server/whatsapp-rpc/node_modules/shebang-regex/index.d.ts +22 -0
  325. package/server/whatsapp-rpc/node_modules/shebang-regex/index.js +2 -0
  326. package/server/whatsapp-rpc/node_modules/shebang-regex/license +9 -0
  327. package/server/whatsapp-rpc/node_modules/shebang-regex/package.json +35 -0
  328. package/server/whatsapp-rpc/node_modules/shebang-regex/readme.md +33 -0
  329. package/server/whatsapp-rpc/node_modules/shell-exec/LICENSE +21 -0
  330. package/server/whatsapp-rpc/node_modules/shell-exec/README.md +60 -0
  331. package/server/whatsapp-rpc/node_modules/shell-exec/index.js +47 -0
  332. package/server/whatsapp-rpc/node_modules/shell-exec/package.json +29 -0
  333. package/server/whatsapp-rpc/node_modules/signal-exit/LICENSE.txt +16 -0
  334. package/server/whatsapp-rpc/node_modules/signal-exit/README.md +74 -0
  335. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts +12 -0
  336. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts.map +1 -0
  337. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js +10 -0
  338. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js.map +1 -0
  339. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts +48 -0
  340. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts.map +1 -0
  341. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js +279 -0
  342. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js.map +1 -0
  343. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/package.json +3 -0
  344. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts +29 -0
  345. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts.map +1 -0
  346. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js +42 -0
  347. package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js.map +1 -0
  348. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts +12 -0
  349. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts.map +1 -0
  350. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js +4 -0
  351. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js.map +1 -0
  352. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts +48 -0
  353. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts.map +1 -0
  354. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js +275 -0
  355. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js.map +1 -0
  356. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/package.json +3 -0
  357. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts +29 -0
  358. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts.map +1 -0
  359. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js +39 -0
  360. package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js.map +1 -0
  361. package/server/whatsapp-rpc/node_modules/signal-exit/package.json +106 -0
  362. package/server/whatsapp-rpc/node_modules/strip-final-newline/index.js +14 -0
  363. package/server/whatsapp-rpc/node_modules/strip-final-newline/license +9 -0
  364. package/server/whatsapp-rpc/node_modules/strip-final-newline/package.json +43 -0
  365. package/server/whatsapp-rpc/node_modules/strip-final-newline/readme.md +35 -0
  366. package/server/whatsapp-rpc/node_modules/which/CHANGELOG.md +166 -0
  367. package/server/whatsapp-rpc/node_modules/which/LICENSE +15 -0
  368. package/server/whatsapp-rpc/node_modules/which/README.md +54 -0
  369. package/server/whatsapp-rpc/node_modules/which/bin/node-which +52 -0
  370. package/server/whatsapp-rpc/node_modules/which/package.json +43 -0
  371. package/server/whatsapp-rpc/node_modules/which/which.js +125 -0
  372. package/server/whatsapp-rpc/package-lock.json +272 -0
  373. package/server/whatsapp-rpc/package.json +30 -30
  374. package/server/whatsapp-rpc/schema.json +1294 -1294
  375. package/server/whatsapp-rpc/scripts/clean.cjs +66 -66
  376. package/server/whatsapp-rpc/scripts/cli.js +162 -162
  377. package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -166
  378. package/server/whatsapp-rpc/src/python/pyproject.toml +15 -15
  379. package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -4
  380. package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -427
  381. package/server/whatsapp-rpc/web/app.py +609 -609
  382. package/server/whatsapp-rpc/web/requirements.txt +6 -6
  383. package/server/whatsapp-rpc/web/rpc_client.py +427 -427
  384. package/server/whatsapp-rpc/web/static/openapi.yaml +59 -59
  385. package/server/whatsapp-rpc/web/templates/base.html +149 -149
  386. package/server/whatsapp-rpc/web/templates/contacts.html +240 -240
  387. package/server/whatsapp-rpc/web/templates/dashboard.html +319 -319
  388. package/server/whatsapp-rpc/web/templates/groups.html +328 -328
  389. package/server/whatsapp-rpc/web/templates/messages.html +465 -465
  390. package/server/whatsapp-rpc/web/templates/messaging.html +680 -680
  391. package/server/whatsapp-rpc/web/templates/send.html +258 -258
  392. package/server/whatsapp-rpc/web/templates/settings.html +459 -459
  393. package/client/src/components/ui/AndroidSettingsPanel.tsx +0 -401
  394. package/client/src/components/ui/WhatsAppSettingsPanel.tsx +0 -345
  395. package/client/src/nodeDefinitions/androidDeviceNodes.ts +0 -140
  396. package/docker-compose.prod.yml +0 -107
  397. package/docker-compose.yml +0 -104
  398. package/docs-MachinaOs/README.md +0 -85
  399. package/docs-MachinaOs/deployment/docker.mdx +0 -228
  400. package/docs-MachinaOs/deployment/production.mdx +0 -345
  401. package/docs-MachinaOs/docs.json +0 -75
  402. package/docs-MachinaOs/faq.mdx +0 -309
  403. package/docs-MachinaOs/favicon.svg +0 -5
  404. package/docs-MachinaOs/installation.mdx +0 -160
  405. package/docs-MachinaOs/introduction.mdx +0 -114
  406. package/docs-MachinaOs/logo/dark.svg +0 -6
  407. package/docs-MachinaOs/logo/light.svg +0 -6
  408. package/docs-MachinaOs/nodes/ai-agent.mdx +0 -216
  409. package/docs-MachinaOs/nodes/ai-models.mdx +0 -240
  410. package/docs-MachinaOs/nodes/android.mdx +0 -411
  411. package/docs-MachinaOs/nodes/overview.mdx +0 -181
  412. package/docs-MachinaOs/nodes/schedulers.mdx +0 -316
  413. package/docs-MachinaOs/nodes/webhooks.mdx +0 -330
  414. package/docs-MachinaOs/nodes/whatsapp.mdx +0 -305
  415. package/docs-MachinaOs/quickstart.mdx +0 -119
  416. package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +0 -177
  417. package/docs-MachinaOs/tutorials/android-automation.mdx +0 -242
  418. package/docs-MachinaOs/tutorials/first-workflow.mdx +0 -134
  419. package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +0 -185
  420. package/nul +0 -0
  421. package/scripts/check-ports.ps1 +0 -33
  422. 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 %}