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,465 +1,465 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}Received Messages - WhatsApp Controller{% endblock %}
4
-
5
- {% block content %}
6
- <div class="max-w-6xl mx-auto">
7
- <div class="bg-white rounded-lg shadow-md p-6">
8
- <div class="flex justify-between items-center mb-6">
9
- <h2 class="text-2xl font-bold text-gray-800">Received Messages</h2>
10
- <div class="flex items-center space-x-4">
11
- <button onclick="loadMessages()" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition">
12
- Refresh
13
- </button>
14
- <button onclick="clearMessages()" class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition">
15
- Clear Display
16
- </button>
17
- </div>
18
- </div>
19
-
20
- <!-- Messages count -->
21
- <div class="mb-4 p-3 bg-gray-100 rounded">
22
- <p class="text-sm text-gray-600">
23
- <span class="font-semibold">Total Messages:</span>
24
- <span id="message-count">0</span>
25
- </p>
26
- </div>
27
-
28
- <!-- Messages container -->
29
- <div id="messages-container" class="space-y-4 max-h-[600px] overflow-y-auto p-4 bg-gray-50 rounded">
30
- <p class="text-gray-500 text-center py-8">No messages yet. Waiting for incoming messages...</p>
31
- </div>
32
- </div>
33
- </div>
34
-
35
- <!-- Media Viewer Modal -->
36
- <div id="media-modal" class="fixed inset-0 bg-black bg-opacity-75 hidden z-50 flex items-center justify-center">
37
- <div class="bg-white rounded-lg max-w-4xl max-h-[90vh] w-full m-4 overflow-hidden">
38
- <div class="flex justify-between items-center p-4 border-b">
39
- <h3 class="text-lg font-semibold" id="media-modal-title">Media Viewer</h3>
40
- <button onclick="closeMediaModal()" class="text-gray-500 hover:text-gray-700">
41
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
42
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
43
- </svg>
44
- </button>
45
- </div>
46
- <div id="media-modal-content" class="p-4 overflow-auto max-h-[calc(90vh-80px)] flex items-center justify-center bg-gray-100">
47
- <p class="text-gray-500">Loading...</p>
48
- </div>
49
- </div>
50
- </div>
51
-
52
- {% endblock %}
53
-
54
- {% block scripts %}
55
- <script>
56
- let messages = [];
57
- let groupsCache = {}; // Cache group names by JID
58
- const messagesContainer = document.getElementById('messages-container');
59
- const messageCount = document.getElementById('message-count');
60
-
61
- // Load groups cache on page load
62
- loadGroupsCache();
63
-
64
- // Subscribe to messages via WebSocket
65
- socket.emit('subscribe_messages');
66
-
67
- // Load groups into cache for name lookup
68
- async function loadGroupsCache() {
69
- try {
70
- const response = await fetch('/api/groups');
71
- const data = await response.json();
72
- if (data.success && data.data) {
73
- data.data.forEach(group => {
74
- groupsCache[group.jid] = group.name;
75
- });
76
- console.log(`Cached ${Object.keys(groupsCache).length} group names`);
77
- }
78
- } catch (error) {
79
- console.error('Failed to load groups cache:', error);
80
- }
81
- }
82
-
83
- // Get group name from cache
84
- function getGroupName(jid) {
85
- return groupsCache[jid] || null;
86
- }
87
-
88
- // Listen for new messages via WebSocket
89
- socket.on('whatsapp_event', function(data) {
90
- console.log('Received WhatsApp event:', data);
91
-
92
- if (data.type === 'message_received') {
93
- // Add new message to the beginning
94
- messages.unshift(data);
95
- renderMessages();
96
-
97
- // Show notification
98
- showNotification('New message received from ' + formatSender(data.data.sender));
99
- }
100
- });
101
-
102
- // Refresh just re-renders (messages come via WebSocket)
103
- function loadMessages() {
104
- renderMessages();
105
- console.log('Messages display refreshed');
106
- }
107
-
108
- // Render all messages
109
- function renderMessages() {
110
- if (messages.length === 0) {
111
- messagesContainer.innerHTML = `
112
- <div class="text-center py-8">
113
- <p class="text-gray-500 mb-2">No messages yet.</p>
114
- <p class="text-sm text-gray-400">Messages will appear here in real-time as they are received.</p>
115
- <p class="text-xs text-gray-400 mt-2">Make sure WhatsApp is connected.</p>
116
- </div>
117
- `;
118
- messageCount.textContent = '0';
119
- return;
120
- }
121
-
122
- messageCount.textContent = messages.length;
123
-
124
- messagesContainer.innerHTML = messages.map(msg => {
125
- const data = msg.data || {};
126
- return createMessageCard(data);
127
- }).join('');
128
- }
129
-
130
- // Create message card HTML
131
- function createMessageCard(data) {
132
- const messageType = data.message_type || 'unknown';
133
- const timestamp = formatTimestamp(data.timestamp);
134
- const sender = formatSender(data.sender);
135
- const isFromMe = data.is_from_me;
136
- const isGroup = data.is_group || false;
137
- const isForwarded = data.is_forwarded || false;
138
- const forwardingScore = data.forwarding_score || 0;
139
- const chatId = data.chat_id || '';
140
-
141
- // Get group name if it's a group message
142
- let groupName = null;
143
- if (isGroup && chatId) {
144
- groupName = getGroupName(chatId);
145
- }
146
-
147
- let contentHtml = '';
148
- let typeIcon = '';
149
- let typeColor = 'bg-blue-100 text-blue-800';
150
-
151
- // Render based on message type
152
- switch(messageType) {
153
- case 'text':
154
- typeIcon = '&#128172;'; // Speech bubble
155
- contentHtml = `<p class="text-gray-800">${escapeHtml(data.text || '')}</p>`;
156
- break;
157
-
158
- case 'image':
159
- typeIcon = '&#128247;'; // Camera
160
- typeColor = 'bg-purple-100 text-purple-800';
161
- const imageInfo = data.image || {};
162
- contentHtml = `
163
- <div class="flex items-center space-x-3 mb-2">
164
- <svg class="w-16 h-16 text-purple-400" fill="currentColor" viewBox="0 0 20 20">
165
- <path fill-rule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clip-rule="evenodd"></path>
166
- </svg>
167
- <div class="flex-1">
168
- <p class="text-sm text-gray-600">Image message</p>
169
- <p class="text-xs text-gray-500">${imageInfo.mime_type || 'image'} - ${formatFileSize(imageInfo.file_length || 0)}</p>
170
- ${data.caption ? `<p class="text-gray-800 italic mt-1">${escapeHtml(data.caption)}</p>` : ''}
171
- </div>
172
- </div>
173
- ${imageInfo.url ? `<button onclick="viewMedia('${escapeHtml(data.message_id)}', 'image', '${escapeHtml(imageInfo.url)}')" class="mt-2 px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600 text-sm">View Image</button>` : ''}
174
- `;
175
- break;
176
-
177
- case 'video':
178
- typeIcon = '&#127909;'; // Video camera
179
- typeColor = 'bg-pink-100 text-pink-800';
180
- const videoInfo = data.video || {};
181
- contentHtml = `
182
- <div class="flex items-center space-x-3 mb-2">
183
- <svg class="w-16 h-16 text-pink-400" fill="currentColor" viewBox="0 0 20 20">
184
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd"></path>
185
- </svg>
186
- <div class="flex-1">
187
- <p class="text-sm text-gray-600">Video message</p>
188
- <p class="text-xs text-gray-500">${videoInfo.mime_type || 'video'} - ${formatFileSize(videoInfo.file_length || 0)} - ${videoInfo.seconds || 0}s</p>
189
- ${data.caption ? `<p class="text-gray-800 italic mt-1">${escapeHtml(data.caption)}</p>` : ''}
190
- </div>
191
- </div>
192
- ${videoInfo.url ? `<button onclick="viewMedia('${escapeHtml(data.message_id)}', 'video', '${escapeHtml(videoInfo.url)}')" class="mt-2 px-4 py-2 bg-pink-500 text-white rounded hover:bg-pink-600 text-sm">View Video</button>` : ''}
193
- `;
194
- break;
195
-
196
- case 'audio':
197
- typeIcon = '&#127925;'; // Musical note
198
- typeColor = 'bg-green-100 text-green-800';
199
- contentHtml = `<p class="text-sm text-gray-600">Audio message</p>`;
200
- break;
201
-
202
- case 'document':
203
- typeIcon = '&#128196;'; // Document
204
- typeColor = 'bg-yellow-100 text-yellow-800';
205
- contentHtml = `
206
- <p class="text-sm text-gray-600 mb-2">Document</p>
207
- ${data.file_name ? `<p class="text-gray-700 font-mono text-xs">${escapeHtml(data.file_name)}</p>` : ''}
208
- `;
209
- break;
210
-
211
- case 'sticker':
212
- typeIcon = '&#128526;'; // Sticker emoji
213
- typeColor = 'bg-orange-100 text-orange-800';
214
- contentHtml = `<p class="text-sm text-gray-600">Sticker</p>`;
215
- break;
216
-
217
- case 'location':
218
- typeIcon = '&#128205;'; // Pin
219
- typeColor = 'bg-red-100 text-red-800';
220
- contentHtml = `
221
- <p class="text-sm text-gray-600 mb-2">Location</p>
222
- ${data.location ? `
223
- <p class="text-gray-800">
224
- <span class="font-semibold">${escapeHtml(data.location.name || 'Unknown')}</span><br>
225
- <span class="text-sm text-gray-600">${escapeHtml(data.location.address || '')}</span><br>
226
- <span class="text-xs text-gray-500">Lat: ${data.location.latitude}, Lon: ${data.location.longitude}</span>
227
- </p>
228
- ` : ''}
229
- `;
230
- break;
231
-
232
- case 'contact':
233
- typeIcon = '&#128100;'; // Contact
234
- typeColor = 'bg-indigo-100 text-indigo-800';
235
- contentHtml = `
236
- <p class="text-sm text-gray-600 mb-2">Contact Card</p>
237
- ${data.contact ? `
238
- <p class="text-gray-800 font-semibold">${escapeHtml(data.contact.display_name || 'Unknown')}</p>
239
- ` : ''}
240
- `;
241
- break;
242
-
243
- case 'contacts':
244
- typeIcon = '&#128101;'; // Contacts
245
- typeColor = 'bg-teal-100 text-teal-800';
246
- contentHtml = `<p class="text-sm text-gray-600">Multiple contacts (${data.contacts_count || 0})</p>`;
247
- break;
248
-
249
- default:
250
- typeIcon = '&#10067;'; // Question mark
251
- typeColor = 'bg-gray-100 text-gray-800';
252
- contentHtml = `<p class="text-sm text-gray-600">Unknown message type: ${messageType}</p>`;
253
- }
254
-
255
- // Add quoted message if exists
256
- let quotedHtml = '';
257
- if (data.quoted) {
258
- quotedHtml = `
259
- <div class="mt-2 p-2 bg-gray-100 border-l-4 border-gray-400 rounded">
260
- <p class="text-xs text-gray-600">Replying to:</p>
261
- <p class="text-sm text-gray-700 italic">${escapeHtml(data.quoted.content || '')}</p>
262
- </div>
263
- `;
264
- }
265
-
266
- // Build group info line
267
- let groupInfoHtml = '';
268
- if (isGroup) {
269
- const displayGroupName = groupName || chatId.replace('@g.us', '');
270
- groupInfoHtml = `<p class="text-xs text-purple-600">in <span class="font-medium">${escapeHtml(displayGroupName)}</span></p>`;
271
- }
272
-
273
- // Build forwarded indicator
274
- let forwardedHtml = '';
275
- if (isForwarded) {
276
- const forwardedLabel = forwardingScore > 4 ? 'Forwarded many times' : 'Forwarded';
277
- forwardedHtml = `<span class="inline-block px-2 py-1 rounded text-xs font-medium bg-gray-200 text-gray-700 ml-1" title="Forwarding score: ${forwardingScore}">&#8631; ${forwardedLabel}</span>`;
278
- }
279
-
280
- return `
281
- <div class="bg-white border rounded-lg p-4 shadow-sm hover:shadow-md transition ${isFromMe ? 'border-l-4 border-green-500' : isGroup ? 'border-l-4 border-purple-500' : 'border-l-4 border-blue-500'}">
282
- <div class="flex justify-between items-start mb-2">
283
- <div class="flex items-center space-x-2">
284
- <span class="text-2xl">${typeIcon}</span>
285
- <div>
286
- <p class="font-semibold text-gray-800">${sender}</p>
287
- ${groupInfoHtml}
288
- <span class="inline-block px-2 py-1 rounded text-xs font-medium ${typeColor}">${messageType}</span>${forwardedHtml}
289
- </div>
290
- </div>
291
- <div class="text-right">
292
- <p class="text-xs text-gray-500">${timestamp}</p>
293
- ${isFromMe ? '<span class="text-xs text-green-600 font-medium">Sent by me</span>' : ''}
294
- ${isGroup && !isFromMe ? '<span class="text-xs text-purple-600 font-medium">Group message</span>' : ''}
295
- </div>
296
- </div>
297
- <div class="mt-2">
298
- ${contentHtml}
299
- ${quotedHtml}
300
- </div>
301
- <div class="mt-2 pt-2 border-t text-xs text-gray-400 flex justify-between items-center">
302
- <span class="font-mono">ID: ${escapeHtml(data.message_id || 'N/A')}</span>
303
- ${!isFromMe ? `<button onclick="markAsRead('${escapeHtml(data.message_id)}', '${escapeHtml(chatId)}', '${escapeHtml(data.sender || '')}')" class="px-2 py-1 bg-blue-500 text-white rounded text-xs hover:bg-blue-600">Mark Read</button>` : ''}
304
- </div>
305
- </div>
306
- `;
307
- }
308
-
309
- // Format timestamp
310
- function formatTimestamp(timestamp) {
311
- if (!timestamp) return 'Unknown time';
312
- // Handle both ISO 8601 strings and Unix timestamps
313
- const date = typeof timestamp === 'string' ? new Date(timestamp) : new Date(timestamp * 1000);
314
- if (isNaN(date.getTime())) return 'Invalid Date';
315
- return date.toLocaleString();
316
- }
317
-
318
- // Format sender
319
- function formatSender(sender) {
320
- if (!sender) return 'Unknown';
321
- // Remove @s.whatsapp.net suffix
322
- return sender.replace('@s.whatsapp.net', '').replace('@c.us', '');
323
- }
324
-
325
- // Escape HTML
326
- function escapeHtml(text) {
327
- const div = document.createElement('div');
328
- div.textContent = text;
329
- return div.innerHTML;
330
- }
331
-
332
- // Clear messages display
333
- function clearMessages() {
334
- if (confirm('Clear all displayed messages? (This will not delete them from storage)')) {
335
- messages = [];
336
- renderMessages();
337
- }
338
- }
339
-
340
- // Format file size
341
- function formatFileSize(bytes) {
342
- if (!bytes || bytes === 0) return '0 B';
343
- const k = 1024;
344
- const sizes = ['B', 'KB', 'MB', 'GB'];
345
- const i = Math.floor(Math.log(bytes) / Math.log(k));
346
- return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
347
- }
348
-
349
- // View media function
350
- function viewMedia(messageId, mediaType, url) {
351
- console.log('View media:', messageId, mediaType, url);
352
-
353
- const modal = document.getElementById('media-modal');
354
- const modalTitle = document.getElementById('media-modal-title');
355
- const modalContent = document.getElementById('media-modal-content');
356
-
357
- // Show modal
358
- modal.classList.remove('hidden');
359
-
360
- // Set title
361
- modalTitle.textContent = `Viewing ${mediaType}`;
362
-
363
- // Show loading
364
- modalContent.innerHTML = '<p class="text-gray-500">Downloading and decrypting media...</p>';
365
-
366
- // Fetch media via Flask RPC endpoint (returns JSON with base64 data)
367
- fetch(`/api/media/${messageId}`)
368
- .then(response => response.json())
369
- .then(result => {
370
- if (!result.success) {
371
- throw new Error(result.error || 'Failed to download media');
372
- }
373
-
374
- const mediaData = result.data;
375
- const base64Data = mediaData.data;
376
- const mimeType = mediaData.mime_type || 'application/octet-stream';
377
-
378
- // Convert base64 to blob
379
- const byteCharacters = atob(base64Data);
380
- const byteNumbers = new Array(byteCharacters.length);
381
- for (let i = 0; i < byteCharacters.length; i++) {
382
- byteNumbers[i] = byteCharacters.charCodeAt(i);
383
- }
384
- const byteArray = new Uint8Array(byteNumbers);
385
- const blob = new Blob([byteArray], { type: mimeType });
386
- const blobUrl = URL.createObjectURL(blob);
387
-
388
- if (mediaType === 'image') {
389
- modalContent.innerHTML = `
390
- <img src="${blobUrl}" class="max-w-full max-h-full object-contain" alt="WhatsApp Image">
391
- `;
392
- } else if (mediaType === 'video') {
393
- modalContent.innerHTML = `
394
- <video controls class="max-w-full max-h-full" autoplay>
395
- <source src="${blobUrl}" type="${mimeType}">
396
- Your browser does not support the video tag.
397
- </video>
398
- `;
399
- } else if (mediaType === 'audio') {
400
- modalContent.innerHTML = `
401
- <audio controls autoplay class="w-full">
402
- <source src="${blobUrl}" type="${mimeType}">
403
- Your browser does not support the audio tag.
404
- </audio>
405
- `;
406
- } else {
407
- modalContent.innerHTML = `
408
- <div class="text-center">
409
- <p class="text-gray-600 mb-4">Media loaded successfully</p>
410
- <a href="${blobUrl}" download class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
411
- Download ${mediaType}
412
- </a>
413
- </div>
414
- `;
415
- }
416
- })
417
- .catch(error => {
418
- console.error('Error loading media:', error);
419
- modalContent.innerHTML = `
420
- <div class="text-center text-red-600">
421
- <p>Failed to load media: ${error.message}</p>
422
- <p class="text-sm text-gray-500 mt-2">The message may have expired or been deleted.</p>
423
- </div>
424
- `;
425
- });
426
- }
427
-
428
- // Close media modal
429
- function closeMediaModal() {
430
- const modal = document.getElementById('media-modal');
431
- modal.classList.add('hidden');
432
-
433
- // Clear content to stop videos/audio
434
- const modalContent = document.getElementById('media-modal-content');
435
- modalContent.innerHTML = '<p class="text-gray-500">Loading...</p>';
436
- }
437
-
438
- // Show notification
439
- function showNotification(message, type = 'info') {
440
- // Simple console notification for now
441
- console.log(`[${type.toUpperCase()}] ${message}`);
442
-
443
- // Could add a toast notification here in the future
444
- }
445
-
446
- // Mark message as read via WebSocket
447
- function markAsRead(messageId, chatJid, senderJid) {
448
- console.log('Marking as read:', messageId, chatJid, senderJid);
449
- socket.emit('mark_read', {
450
- message_ids: [messageId],
451
- chat_jid: chatJid,
452
- sender_jid: senderJid || null
453
- });
454
- }
455
-
456
- // Listen for mark_read result
457
- socket.on('mark_read_result', function(data) {
458
- if (data.success) {
459
- showNotification('Message marked as read');
460
- } else {
461
- showNotification('Failed to mark as read: ' + data.error, 'error');
462
- }
463
- });
464
- </script>
465
- {% endblock %}
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Received Messages - WhatsApp Controller{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="max-w-6xl mx-auto">
7
+ <div class="bg-white rounded-lg shadow-md p-6">
8
+ <div class="flex justify-between items-center mb-6">
9
+ <h2 class="text-2xl font-bold text-gray-800">Received Messages</h2>
10
+ <div class="flex items-center space-x-4">
11
+ <button onclick="loadMessages()" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition">
12
+ Refresh
13
+ </button>
14
+ <button onclick="clearMessages()" class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition">
15
+ Clear Display
16
+ </button>
17
+ </div>
18
+ </div>
19
+
20
+ <!-- Messages count -->
21
+ <div class="mb-4 p-3 bg-gray-100 rounded">
22
+ <p class="text-sm text-gray-600">
23
+ <span class="font-semibold">Total Messages:</span>
24
+ <span id="message-count">0</span>
25
+ </p>
26
+ </div>
27
+
28
+ <!-- Messages container -->
29
+ <div id="messages-container" class="space-y-4 max-h-[600px] overflow-y-auto p-4 bg-gray-50 rounded">
30
+ <p class="text-gray-500 text-center py-8">No messages yet. Waiting for incoming messages...</p>
31
+ </div>
32
+ </div>
33
+ </div>
34
+
35
+ <!-- Media Viewer Modal -->
36
+ <div id="media-modal" class="fixed inset-0 bg-black bg-opacity-75 hidden z-50 flex items-center justify-center">
37
+ <div class="bg-white rounded-lg max-w-4xl max-h-[90vh] w-full m-4 overflow-hidden">
38
+ <div class="flex justify-between items-center p-4 border-b">
39
+ <h3 class="text-lg font-semibold" id="media-modal-title">Media Viewer</h3>
40
+ <button onclick="closeMediaModal()" class="text-gray-500 hover:text-gray-700">
41
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
42
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
43
+ </svg>
44
+ </button>
45
+ </div>
46
+ <div id="media-modal-content" class="p-4 overflow-auto max-h-[calc(90vh-80px)] flex items-center justify-center bg-gray-100">
47
+ <p class="text-gray-500">Loading...</p>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ {% endblock %}
53
+
54
+ {% block scripts %}
55
+ <script>
56
+ let messages = [];
57
+ let groupsCache = {}; // Cache group names by JID
58
+ const messagesContainer = document.getElementById('messages-container');
59
+ const messageCount = document.getElementById('message-count');
60
+
61
+ // Load groups cache on page load
62
+ loadGroupsCache();
63
+
64
+ // Subscribe to messages via WebSocket
65
+ socket.emit('subscribe_messages');
66
+
67
+ // Load groups into cache for name lookup
68
+ async function loadGroupsCache() {
69
+ try {
70
+ const response = await fetch('/api/groups');
71
+ const data = await response.json();
72
+ if (data.success && data.data) {
73
+ data.data.forEach(group => {
74
+ groupsCache[group.jid] = group.name;
75
+ });
76
+ console.log(`Cached ${Object.keys(groupsCache).length} group names`);
77
+ }
78
+ } catch (error) {
79
+ console.error('Failed to load groups cache:', error);
80
+ }
81
+ }
82
+
83
+ // Get group name from cache
84
+ function getGroupName(jid) {
85
+ return groupsCache[jid] || null;
86
+ }
87
+
88
+ // Listen for new messages via WebSocket
89
+ socket.on('whatsapp_event', function(data) {
90
+ console.log('Received WhatsApp event:', data);
91
+
92
+ if (data.type === 'message_received') {
93
+ // Add new message to the beginning
94
+ messages.unshift(data);
95
+ renderMessages();
96
+
97
+ // Show notification
98
+ showNotification('New message received from ' + formatSender(data.data.sender));
99
+ }
100
+ });
101
+
102
+ // Refresh just re-renders (messages come via WebSocket)
103
+ function loadMessages() {
104
+ renderMessages();
105
+ console.log('Messages display refreshed');
106
+ }
107
+
108
+ // Render all messages
109
+ function renderMessages() {
110
+ if (messages.length === 0) {
111
+ messagesContainer.innerHTML = `
112
+ <div class="text-center py-8">
113
+ <p class="text-gray-500 mb-2">No messages yet.</p>
114
+ <p class="text-sm text-gray-400">Messages will appear here in real-time as they are received.</p>
115
+ <p class="text-xs text-gray-400 mt-2">Make sure WhatsApp is connected.</p>
116
+ </div>
117
+ `;
118
+ messageCount.textContent = '0';
119
+ return;
120
+ }
121
+
122
+ messageCount.textContent = messages.length;
123
+
124
+ messagesContainer.innerHTML = messages.map(msg => {
125
+ const data = msg.data || {};
126
+ return createMessageCard(data);
127
+ }).join('');
128
+ }
129
+
130
+ // Create message card HTML
131
+ function createMessageCard(data) {
132
+ const messageType = data.message_type || 'unknown';
133
+ const timestamp = formatTimestamp(data.timestamp);
134
+ const sender = formatSender(data.sender);
135
+ const isFromMe = data.is_from_me;
136
+ const isGroup = data.is_group || false;
137
+ const isForwarded = data.is_forwarded || false;
138
+ const forwardingScore = data.forwarding_score || 0;
139
+ const chatId = data.chat_id || '';
140
+
141
+ // Get group name if it's a group message
142
+ let groupName = null;
143
+ if (isGroup && chatId) {
144
+ groupName = getGroupName(chatId);
145
+ }
146
+
147
+ let contentHtml = '';
148
+ let typeIcon = '';
149
+ let typeColor = 'bg-blue-100 text-blue-800';
150
+
151
+ // Render based on message type
152
+ switch(messageType) {
153
+ case 'text':
154
+ typeIcon = '&#128172;'; // Speech bubble
155
+ contentHtml = `<p class="text-gray-800">${escapeHtml(data.text || '')}</p>`;
156
+ break;
157
+
158
+ case 'image':
159
+ typeIcon = '&#128247;'; // Camera
160
+ typeColor = 'bg-purple-100 text-purple-800';
161
+ const imageInfo = data.image || {};
162
+ contentHtml = `
163
+ <div class="flex items-center space-x-3 mb-2">
164
+ <svg class="w-16 h-16 text-purple-400" fill="currentColor" viewBox="0 0 20 20">
165
+ <path fill-rule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clip-rule="evenodd"></path>
166
+ </svg>
167
+ <div class="flex-1">
168
+ <p class="text-sm text-gray-600">Image message</p>
169
+ <p class="text-xs text-gray-500">${imageInfo.mime_type || 'image'} - ${formatFileSize(imageInfo.file_length || 0)}</p>
170
+ ${data.caption ? `<p class="text-gray-800 italic mt-1">${escapeHtml(data.caption)}</p>` : ''}
171
+ </div>
172
+ </div>
173
+ ${imageInfo.url ? `<button onclick="viewMedia('${escapeHtml(data.message_id)}', 'image', '${escapeHtml(imageInfo.url)}')" class="mt-2 px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600 text-sm">View Image</button>` : ''}
174
+ `;
175
+ break;
176
+
177
+ case 'video':
178
+ typeIcon = '&#127909;'; // Video camera
179
+ typeColor = 'bg-pink-100 text-pink-800';
180
+ const videoInfo = data.video || {};
181
+ contentHtml = `
182
+ <div class="flex items-center space-x-3 mb-2">
183
+ <svg class="w-16 h-16 text-pink-400" fill="currentColor" viewBox="0 0 20 20">
184
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd"></path>
185
+ </svg>
186
+ <div class="flex-1">
187
+ <p class="text-sm text-gray-600">Video message</p>
188
+ <p class="text-xs text-gray-500">${videoInfo.mime_type || 'video'} - ${formatFileSize(videoInfo.file_length || 0)} - ${videoInfo.seconds || 0}s</p>
189
+ ${data.caption ? `<p class="text-gray-800 italic mt-1">${escapeHtml(data.caption)}</p>` : ''}
190
+ </div>
191
+ </div>
192
+ ${videoInfo.url ? `<button onclick="viewMedia('${escapeHtml(data.message_id)}', 'video', '${escapeHtml(videoInfo.url)}')" class="mt-2 px-4 py-2 bg-pink-500 text-white rounded hover:bg-pink-600 text-sm">View Video</button>` : ''}
193
+ `;
194
+ break;
195
+
196
+ case 'audio':
197
+ typeIcon = '&#127925;'; // Musical note
198
+ typeColor = 'bg-green-100 text-green-800';
199
+ contentHtml = `<p class="text-sm text-gray-600">Audio message</p>`;
200
+ break;
201
+
202
+ case 'document':
203
+ typeIcon = '&#128196;'; // Document
204
+ typeColor = 'bg-yellow-100 text-yellow-800';
205
+ contentHtml = `
206
+ <p class="text-sm text-gray-600 mb-2">Document</p>
207
+ ${data.file_name ? `<p class="text-gray-700 font-mono text-xs">${escapeHtml(data.file_name)}</p>` : ''}
208
+ `;
209
+ break;
210
+
211
+ case 'sticker':
212
+ typeIcon = '&#128526;'; // Sticker emoji
213
+ typeColor = 'bg-orange-100 text-orange-800';
214
+ contentHtml = `<p class="text-sm text-gray-600">Sticker</p>`;
215
+ break;
216
+
217
+ case 'location':
218
+ typeIcon = '&#128205;'; // Pin
219
+ typeColor = 'bg-red-100 text-red-800';
220
+ contentHtml = `
221
+ <p class="text-sm text-gray-600 mb-2">Location</p>
222
+ ${data.location ? `
223
+ <p class="text-gray-800">
224
+ <span class="font-semibold">${escapeHtml(data.location.name || 'Unknown')}</span><br>
225
+ <span class="text-sm text-gray-600">${escapeHtml(data.location.address || '')}</span><br>
226
+ <span class="text-xs text-gray-500">Lat: ${data.location.latitude}, Lon: ${data.location.longitude}</span>
227
+ </p>
228
+ ` : ''}
229
+ `;
230
+ break;
231
+
232
+ case 'contact':
233
+ typeIcon = '&#128100;'; // Contact
234
+ typeColor = 'bg-indigo-100 text-indigo-800';
235
+ contentHtml = `
236
+ <p class="text-sm text-gray-600 mb-2">Contact Card</p>
237
+ ${data.contact ? `
238
+ <p class="text-gray-800 font-semibold">${escapeHtml(data.contact.display_name || 'Unknown')}</p>
239
+ ` : ''}
240
+ `;
241
+ break;
242
+
243
+ case 'contacts':
244
+ typeIcon = '&#128101;'; // Contacts
245
+ typeColor = 'bg-teal-100 text-teal-800';
246
+ contentHtml = `<p class="text-sm text-gray-600">Multiple contacts (${data.contacts_count || 0})</p>`;
247
+ break;
248
+
249
+ default:
250
+ typeIcon = '&#10067;'; // Question mark
251
+ typeColor = 'bg-gray-100 text-gray-800';
252
+ contentHtml = `<p class="text-sm text-gray-600">Unknown message type: ${messageType}</p>`;
253
+ }
254
+
255
+ // Add quoted message if exists
256
+ let quotedHtml = '';
257
+ if (data.quoted) {
258
+ quotedHtml = `
259
+ <div class="mt-2 p-2 bg-gray-100 border-l-4 border-gray-400 rounded">
260
+ <p class="text-xs text-gray-600">Replying to:</p>
261
+ <p class="text-sm text-gray-700 italic">${escapeHtml(data.quoted.content || '')}</p>
262
+ </div>
263
+ `;
264
+ }
265
+
266
+ // Build group info line
267
+ let groupInfoHtml = '';
268
+ if (isGroup) {
269
+ const displayGroupName = groupName || chatId.replace('@g.us', '');
270
+ groupInfoHtml = `<p class="text-xs text-purple-600">in <span class="font-medium">${escapeHtml(displayGroupName)}</span></p>`;
271
+ }
272
+
273
+ // Build forwarded indicator
274
+ let forwardedHtml = '';
275
+ if (isForwarded) {
276
+ const forwardedLabel = forwardingScore > 4 ? 'Forwarded many times' : 'Forwarded';
277
+ forwardedHtml = `<span class="inline-block px-2 py-1 rounded text-xs font-medium bg-gray-200 text-gray-700 ml-1" title="Forwarding score: ${forwardingScore}">&#8631; ${forwardedLabel}</span>`;
278
+ }
279
+
280
+ return `
281
+ <div class="bg-white border rounded-lg p-4 shadow-sm hover:shadow-md transition ${isFromMe ? 'border-l-4 border-green-500' : isGroup ? 'border-l-4 border-purple-500' : 'border-l-4 border-blue-500'}">
282
+ <div class="flex justify-between items-start mb-2">
283
+ <div class="flex items-center space-x-2">
284
+ <span class="text-2xl">${typeIcon}</span>
285
+ <div>
286
+ <p class="font-semibold text-gray-800">${sender}</p>
287
+ ${groupInfoHtml}
288
+ <span class="inline-block px-2 py-1 rounded text-xs font-medium ${typeColor}">${messageType}</span>${forwardedHtml}
289
+ </div>
290
+ </div>
291
+ <div class="text-right">
292
+ <p class="text-xs text-gray-500">${timestamp}</p>
293
+ ${isFromMe ? '<span class="text-xs text-green-600 font-medium">Sent by me</span>' : ''}
294
+ ${isGroup && !isFromMe ? '<span class="text-xs text-purple-600 font-medium">Group message</span>' : ''}
295
+ </div>
296
+ </div>
297
+ <div class="mt-2">
298
+ ${contentHtml}
299
+ ${quotedHtml}
300
+ </div>
301
+ <div class="mt-2 pt-2 border-t text-xs text-gray-400 flex justify-between items-center">
302
+ <span class="font-mono">ID: ${escapeHtml(data.message_id || 'N/A')}</span>
303
+ ${!isFromMe ? `<button onclick="markAsRead('${escapeHtml(data.message_id)}', '${escapeHtml(chatId)}', '${escapeHtml(data.sender || '')}')" class="px-2 py-1 bg-blue-500 text-white rounded text-xs hover:bg-blue-600">Mark Read</button>` : ''}
304
+ </div>
305
+ </div>
306
+ `;
307
+ }
308
+
309
+ // Format timestamp
310
+ function formatTimestamp(timestamp) {
311
+ if (!timestamp) return 'Unknown time';
312
+ // Handle both ISO 8601 strings and Unix timestamps
313
+ const date = typeof timestamp === 'string' ? new Date(timestamp) : new Date(timestamp * 1000);
314
+ if (isNaN(date.getTime())) return 'Invalid Date';
315
+ return date.toLocaleString();
316
+ }
317
+
318
+ // Format sender
319
+ function formatSender(sender) {
320
+ if (!sender) return 'Unknown';
321
+ // Remove @s.whatsapp.net suffix
322
+ return sender.replace('@s.whatsapp.net', '').replace('@c.us', '');
323
+ }
324
+
325
+ // Escape HTML
326
+ function escapeHtml(text) {
327
+ const div = document.createElement('div');
328
+ div.textContent = text;
329
+ return div.innerHTML;
330
+ }
331
+
332
+ // Clear messages display
333
+ function clearMessages() {
334
+ if (confirm('Clear all displayed messages? (This will not delete them from storage)')) {
335
+ messages = [];
336
+ renderMessages();
337
+ }
338
+ }
339
+
340
+ // Format file size
341
+ function formatFileSize(bytes) {
342
+ if (!bytes || bytes === 0) return '0 B';
343
+ const k = 1024;
344
+ const sizes = ['B', 'KB', 'MB', 'GB'];
345
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
346
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
347
+ }
348
+
349
+ // View media function
350
+ function viewMedia(messageId, mediaType, url) {
351
+ console.log('View media:', messageId, mediaType, url);
352
+
353
+ const modal = document.getElementById('media-modal');
354
+ const modalTitle = document.getElementById('media-modal-title');
355
+ const modalContent = document.getElementById('media-modal-content');
356
+
357
+ // Show modal
358
+ modal.classList.remove('hidden');
359
+
360
+ // Set title
361
+ modalTitle.textContent = `Viewing ${mediaType}`;
362
+
363
+ // Show loading
364
+ modalContent.innerHTML = '<p class="text-gray-500">Downloading and decrypting media...</p>';
365
+
366
+ // Fetch media via Flask RPC endpoint (returns JSON with base64 data)
367
+ fetch(`/api/media/${messageId}`)
368
+ .then(response => response.json())
369
+ .then(result => {
370
+ if (!result.success) {
371
+ throw new Error(result.error || 'Failed to download media');
372
+ }
373
+
374
+ const mediaData = result.data;
375
+ const base64Data = mediaData.data;
376
+ const mimeType = mediaData.mime_type || 'application/octet-stream';
377
+
378
+ // Convert base64 to blob
379
+ const byteCharacters = atob(base64Data);
380
+ const byteNumbers = new Array(byteCharacters.length);
381
+ for (let i = 0; i < byteCharacters.length; i++) {
382
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
383
+ }
384
+ const byteArray = new Uint8Array(byteNumbers);
385
+ const blob = new Blob([byteArray], { type: mimeType });
386
+ const blobUrl = URL.createObjectURL(blob);
387
+
388
+ if (mediaType === 'image') {
389
+ modalContent.innerHTML = `
390
+ <img src="${blobUrl}" class="max-w-full max-h-full object-contain" alt="WhatsApp Image">
391
+ `;
392
+ } else if (mediaType === 'video') {
393
+ modalContent.innerHTML = `
394
+ <video controls class="max-w-full max-h-full" autoplay>
395
+ <source src="${blobUrl}" type="${mimeType}">
396
+ Your browser does not support the video tag.
397
+ </video>
398
+ `;
399
+ } else if (mediaType === 'audio') {
400
+ modalContent.innerHTML = `
401
+ <audio controls autoplay class="w-full">
402
+ <source src="${blobUrl}" type="${mimeType}">
403
+ Your browser does not support the audio tag.
404
+ </audio>
405
+ `;
406
+ } else {
407
+ modalContent.innerHTML = `
408
+ <div class="text-center">
409
+ <p class="text-gray-600 mb-4">Media loaded successfully</p>
410
+ <a href="${blobUrl}" download class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
411
+ Download ${mediaType}
412
+ </a>
413
+ </div>
414
+ `;
415
+ }
416
+ })
417
+ .catch(error => {
418
+ console.error('Error loading media:', error);
419
+ modalContent.innerHTML = `
420
+ <div class="text-center text-red-600">
421
+ <p>Failed to load media: ${error.message}</p>
422
+ <p class="text-sm text-gray-500 mt-2">The message may have expired or been deleted.</p>
423
+ </div>
424
+ `;
425
+ });
426
+ }
427
+
428
+ // Close media modal
429
+ function closeMediaModal() {
430
+ const modal = document.getElementById('media-modal');
431
+ modal.classList.add('hidden');
432
+
433
+ // Clear content to stop videos/audio
434
+ const modalContent = document.getElementById('media-modal-content');
435
+ modalContent.innerHTML = '<p class="text-gray-500">Loading...</p>';
436
+ }
437
+
438
+ // Show notification
439
+ function showNotification(message, type = 'info') {
440
+ // Simple console notification for now
441
+ console.log(`[${type.toUpperCase()}] ${message}`);
442
+
443
+ // Could add a toast notification here in the future
444
+ }
445
+
446
+ // Mark message as read via WebSocket
447
+ function markAsRead(messageId, chatJid, senderJid) {
448
+ console.log('Marking as read:', messageId, chatJid, senderJid);
449
+ socket.emit('mark_read', {
450
+ message_ids: [messageId],
451
+ chat_jid: chatJid,
452
+ sender_jid: senderJid || null
453
+ });
454
+ }
455
+
456
+ // Listen for mark_read result
457
+ socket.on('mark_read_result', function(data) {
458
+ if (data.success) {
459
+ showNotification('Message marked as read');
460
+ } else {
461
+ showNotification('Failed to mark as read: ' + data.error, 'error');
462
+ }
463
+ });
464
+ </script>
465
+ {% endblock %}