elevenlabs-webhook-nodejs 1.0.0

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 (969) hide show
  1. package/.claude/settings.local.json +27 -0
  2. package/.dockerignore +6 -0
  3. package/.env.example +43 -0
  4. package/BEFORE-PRODUCTION.md +32 -0
  5. package/Dockerfile +23 -0
  6. package/SYSTEM_OVERVIEW.md +942 -0
  7. package/SYSTEM_STATUS.md +332 -0
  8. package/backup_script/main.py +146 -0
  9. package/baileys/.dockerignore +7 -0
  10. package/baileys/Dockerfile +13 -0
  11. package/baileys/README.md +412 -0
  12. package/baileys/index.js +499 -0
  13. package/baileys/package-lock.json +2532 -0
  14. package/baileys/package.json +25 -0
  15. package/baileys/server.js +96 -0
  16. package/baileys/src/config.js +55 -0
  17. package/baileys/src/middleware/api-key.js +16 -0
  18. package/baileys/src/routes/accounts.js +51 -0
  19. package/baileys/src/routes/chats.js +103 -0
  20. package/baileys/src/routes/webhooks.js +34 -0
  21. package/baileys/src/services/account-store.js +259 -0
  22. package/baileys/src/services/webhook-dispatcher.js +161 -0
  23. package/baileys/src/services/worker-manager.js +597 -0
  24. package/baileys/src/utils/jid.js +79 -0
  25. package/baileys/src/utils/logger.js +16 -0
  26. package/baileys/src/utils/use-mongodb-auth-state.js +122 -0
  27. package/baileys/worker.js +721 -0
  28. package/dist/app.d.ts +1 -0
  29. package/dist/app.js +84 -0
  30. package/dist/app.js.map +1 -0
  31. package/dist/config/agentPrompts.json +19 -0
  32. package/dist/config/database.d.ts +1 -0
  33. package/dist/config/database.js +26 -0
  34. package/dist/config/database.js.map +1 -0
  35. package/dist/config/env.d.ts +41 -0
  36. package/dist/config/env.js +81 -0
  37. package/dist/config/env.js.map +1 -0
  38. package/dist/config/index.d.ts +3 -0
  39. package/dist/config/index.js +73 -0
  40. package/dist/config/index.js.map +1 -0
  41. package/dist/controllers/webhook.controller.d.ts +10 -0
  42. package/dist/controllers/webhook.controller.js +128 -0
  43. package/dist/controllers/webhook.controller.js.map +1 -0
  44. package/dist/errors/index.d.ts +4 -0
  45. package/dist/errors/index.js +13 -0
  46. package/dist/errors/index.js.map +1 -0
  47. package/dist/hooks/webhookValidator.d.ts +2 -0
  48. package/dist/hooks/webhookValidator.js +47 -0
  49. package/dist/hooks/webhookValidator.js.map +1 -0
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.js +55 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/migrations/001_session-architecture.d.ts +3 -0
  54. package/dist/migrations/001_session-architecture.js +119 -0
  55. package/dist/migrations/001_session-architecture.js.map +1 -0
  56. package/dist/migrations/002_agent-ref.d.ts +3 -0
  57. package/dist/migrations/002_agent-ref.js +55 -0
  58. package/dist/migrations/002_agent-ref.js.map +1 -0
  59. package/dist/migrations/003_shift-schedule.d.ts +3 -0
  60. package/dist/migrations/003_shift-schedule.js +48 -0
  61. package/dist/migrations/003_shift-schedule.js.map +1 -0
  62. package/dist/migrations/004_invert-shift-to-clinic-hours.d.ts +3 -0
  63. package/dist/migrations/004_invert-shift-to-clinic-hours.js +27 -0
  64. package/dist/migrations/004_invert-shift-to-clinic-hours.js.map +1 -0
  65. package/dist/migrations/005_composite-baileys-chat-ids.d.ts +3 -0
  66. package/dist/migrations/005_composite-baileys-chat-ids.js +47 -0
  67. package/dist/migrations/005_composite-baileys-chat-ids.js.map +1 -0
  68. package/dist/migrations/migration.model.d.ts +18 -0
  69. package/dist/migrations/migration.model.js +45 -0
  70. package/dist/migrations/migration.model.js.map +1 -0
  71. package/dist/migrations/migration.routes.d.ts +2 -0
  72. package/dist/migrations/migration.routes.js +37 -0
  73. package/dist/migrations/migration.routes.js.map +1 -0
  74. package/dist/migrations/migration.runner.d.ts +19 -0
  75. package/dist/migrations/migration.runner.js +74 -0
  76. package/dist/migrations/migration.runner.js.map +1 -0
  77. package/dist/models/ConversationClaim.d.ts +13 -0
  78. package/dist/models/ConversationClaim.js +44 -0
  79. package/dist/models/ConversationClaim.js.map +1 -0
  80. package/dist/models/ConversationEvaluation.d.ts +23 -0
  81. package/dist/models/ConversationEvaluation.js +92 -0
  82. package/dist/models/ConversationEvaluation.js.map +1 -0
  83. package/dist/models/Summary.d.ts +37 -0
  84. package/dist/models/Summary.js +78 -0
  85. package/dist/models/Summary.js.map +1 -0
  86. package/dist/models/Transcription.d.ts +34 -0
  87. package/dist/models/Transcription.js +71 -0
  88. package/dist/models/Transcription.js.map +1 -0
  89. package/dist/models/WhatsAppRecipient.d.ts +23 -0
  90. package/dist/models/WhatsAppRecipient.js +53 -0
  91. package/dist/models/WhatsAppRecipient.js.map +1 -0
  92. package/dist/modules/admin/admin.routes.d.ts +2 -0
  93. package/dist/modules/admin/admin.routes.js +1966 -0
  94. package/dist/modules/admin/admin.routes.js.map +1 -0
  95. package/dist/modules/admin/elevenlabs-test-chat.routes.d.ts +2 -0
  96. package/dist/modules/admin/elevenlabs-test-chat.routes.js +158 -0
  97. package/dist/modules/admin/elevenlabs-test-chat.routes.js.map +1 -0
  98. package/dist/modules/admin/whatsapp-test-chat.routes.d.ts +2 -0
  99. package/dist/modules/admin/whatsapp-test-chat.routes.js +204 -0
  100. package/dist/modules/admin/whatsapp-test-chat.routes.js.map +1 -0
  101. package/dist/modules/agents/agent.model.d.ts +38 -0
  102. package/dist/modules/agents/agent.model.js +92 -0
  103. package/dist/modules/agents/agent.model.js.map +1 -0
  104. package/dist/modules/agents/agent.routes.d.ts +2 -0
  105. package/dist/modules/agents/agent.routes.js +61 -0
  106. package/dist/modules/agents/agent.routes.js.map +1 -0
  107. package/dist/modules/appointment-validation/appointment-validation.model.d.ts +76 -0
  108. package/dist/modules/appointment-validation/appointment-validation.model.js +118 -0
  109. package/dist/modules/appointment-validation/appointment-validation.model.js.map +1 -0
  110. package/dist/modules/appointment-validation/appointment-validation.routes.d.ts +2 -0
  111. package/dist/modules/appointment-validation/appointment-validation.routes.js +202 -0
  112. package/dist/modules/appointment-validation/appointment-validation.routes.js.map +1 -0
  113. package/dist/modules/appointment-validation/appointment-validation.service.d.ts +53 -0
  114. package/dist/modules/appointment-validation/appointment-validation.service.js +827 -0
  115. package/dist/modules/appointment-validation/appointment-validation.service.js.map +1 -0
  116. package/dist/modules/auth/auth.model.d.ts +17 -0
  117. package/dist/modules/auth/auth.model.js +64 -0
  118. package/dist/modules/auth/auth.model.js.map +1 -0
  119. package/dist/modules/auth/auth.routes.d.ts +2 -0
  120. package/dist/modules/auth/auth.routes.js +202 -0
  121. package/dist/modules/auth/auth.routes.js.map +1 -0
  122. package/dist/modules/auth/auth.service.d.ts +28 -0
  123. package/dist/modules/auth/auth.service.js +183 -0
  124. package/dist/modules/auth/auth.service.js.map +1 -0
  125. package/dist/modules/auth/refresh-token.model.d.ts +13 -0
  126. package/dist/modules/auth/refresh-token.model.js +52 -0
  127. package/dist/modules/auth/refresh-token.model.js.map +1 -0
  128. package/dist/modules/billing/billing-alert.model.d.ts +16 -0
  129. package/dist/modules/billing/billing-alert.model.js +47 -0
  130. package/dist/modules/billing/billing-alert.model.js.map +1 -0
  131. package/dist/modules/billing/billing-period-snapshot.model.d.ts +35 -0
  132. package/dist/modules/billing/billing-period-snapshot.model.js +68 -0
  133. package/dist/modules/billing/billing-period-snapshot.model.js.map +1 -0
  134. package/dist/modules/billing/billing.model.d.ts +18 -0
  135. package/dist/modules/billing/billing.model.js +62 -0
  136. package/dist/modules/billing/billing.model.js.map +1 -0
  137. package/dist/modules/billing/billing.routes.d.ts +2 -0
  138. package/dist/modules/billing/billing.routes.js +63 -0
  139. package/dist/modules/billing/billing.routes.js.map +1 -0
  140. package/dist/modules/billing/billing.service.d.ts +69 -0
  141. package/dist/modules/billing/billing.service.js +498 -0
  142. package/dist/modules/billing/billing.service.js.map +1 -0
  143. package/dist/modules/billing/payment.model.d.ts +24 -0
  144. package/dist/modules/billing/payment.model.js +57 -0
  145. package/dist/modules/billing/payment.model.js.map +1 -0
  146. package/dist/modules/calls/call.model.d.ts +41 -0
  147. package/dist/modules/calls/call.model.js +97 -0
  148. package/dist/modules/calls/call.model.js.map +1 -0
  149. package/dist/modules/calls/call.routes.d.ts +2 -0
  150. package/dist/modules/calls/call.routes.js +103 -0
  151. package/dist/modules/calls/call.routes.js.map +1 -0
  152. package/dist/modules/campaigns/campaign.model.d.ts +45 -0
  153. package/dist/modules/campaigns/campaign.model.js +98 -0
  154. package/dist/modules/campaigns/campaign.model.js.map +1 -0
  155. package/dist/modules/campaigns/campaign.routes.d.ts +2 -0
  156. package/dist/modules/campaigns/campaign.routes.js +323 -0
  157. package/dist/modules/campaigns/campaign.routes.js.map +1 -0
  158. package/dist/modules/campaigns/campaign.service.d.ts +11 -0
  159. package/dist/modules/campaigns/campaign.service.js +86 -0
  160. package/dist/modules/campaigns/campaign.service.js.map +1 -0
  161. package/dist/modules/google-calendar/google-calendar.routes.d.ts +2 -0
  162. package/dist/modules/google-calendar/google-calendar.routes.js +32 -0
  163. package/dist/modules/google-calendar/google-calendar.routes.js.map +1 -0
  164. package/dist/modules/inbound-call/inbound-call-config.model.d.ts +20 -0
  165. package/dist/modules/inbound-call/inbound-call-config.model.js +68 -0
  166. package/dist/modules/inbound-call/inbound-call-config.model.js.map +1 -0
  167. package/dist/modules/inbound-call/inbound-call.routes.d.ts +2 -0
  168. package/dist/modules/inbound-call/inbound-call.routes.js +243 -0
  169. package/dist/modules/inbound-call/inbound-call.routes.js.map +1 -0
  170. package/dist/modules/leads/lead.model.d.ts +24 -0
  171. package/dist/modules/leads/lead.model.js +54 -0
  172. package/dist/modules/leads/lead.model.js.map +1 -0
  173. package/dist/modules/leads/lead.routes.d.ts +2 -0
  174. package/dist/modules/leads/lead.routes.js +201 -0
  175. package/dist/modules/leads/lead.routes.js.map +1 -0
  176. package/dist/modules/plans/plan.model.d.ts +25 -0
  177. package/dist/modules/plans/plan.model.js +59 -0
  178. package/dist/modules/plans/plan.model.js.map +1 -0
  179. package/dist/modules/surveys/survey.model.d.ts +30 -0
  180. package/dist/modules/surveys/survey.model.js +75 -0
  181. package/dist/modules/surveys/survey.model.js.map +1 -0
  182. package/dist/modules/surveys/survey.routes.d.ts +2 -0
  183. package/dist/modules/surveys/survey.routes.js +153 -0
  184. package/dist/modules/surveys/survey.routes.js.map +1 -0
  185. package/dist/modules/tenants/tenant.model.d.ts +86 -0
  186. package/dist/modules/tenants/tenant.model.js +127 -0
  187. package/dist/modules/tenants/tenant.model.js.map +1 -0
  188. package/dist/modules/tenants/tenant.routes.d.ts +2 -0
  189. package/dist/modules/tenants/tenant.routes.js +65 -0
  190. package/dist/modules/tenants/tenant.routes.js.map +1 -0
  191. package/dist/modules/users/user.routes.d.ts +2 -0
  192. package/dist/modules/users/user.routes.js +106 -0
  193. package/dist/modules/users/user.routes.js.map +1 -0
  194. package/dist/modules/webhooks/elevenlabs-tool.routes.d.ts +20 -0
  195. package/dist/modules/webhooks/elevenlabs-tool.routes.js +85 -0
  196. package/dist/modules/webhooks/elevenlabs-tool.routes.js.map +1 -0
  197. package/dist/modules/webhooks/elevenlabs-tool.service.d.ts +11 -0
  198. package/dist/modules/webhooks/elevenlabs-tool.service.js +360 -0
  199. package/dist/modules/webhooks/elevenlabs-tool.service.js.map +1 -0
  200. package/dist/modules/webhooks/elevenlabs.routes.d.ts +2 -0
  201. package/dist/modules/webhooks/elevenlabs.routes.js +34 -0
  202. package/dist/modules/webhooks/elevenlabs.routes.js.map +1 -0
  203. package/dist/modules/webhooks/elevenlabs.service.d.ts +6 -0
  204. package/dist/modules/webhooks/elevenlabs.service.js +512 -0
  205. package/dist/modules/webhooks/elevenlabs.service.js.map +1 -0
  206. package/dist/modules/webhooks/unipile.routes.d.ts +2 -0
  207. package/dist/modules/webhooks/unipile.routes.js +780 -0
  208. package/dist/modules/webhooks/unipile.routes.js.map +1 -0
  209. package/dist/modules/whatsapp/appointment.model.d.ts +27 -0
  210. package/dist/modules/whatsapp/appointment.model.js +58 -0
  211. package/dist/modules/whatsapp/appointment.model.js.map +1 -0
  212. package/dist/modules/whatsapp/operator-request.model.d.ts +29 -0
  213. package/dist/modules/whatsapp/operator-request.model.js +65 -0
  214. package/dist/modules/whatsapp/operator-request.model.js.map +1 -0
  215. package/dist/modules/whatsapp/stt-usage.model.d.ts +16 -0
  216. package/dist/modules/whatsapp/stt-usage.model.js +53 -0
  217. package/dist/modules/whatsapp/stt-usage.model.js.map +1 -0
  218. package/dist/modules/whatsapp/whatsapp-chat.model.d.ts +23 -0
  219. package/dist/modules/whatsapp/whatsapp-chat.model.js +55 -0
  220. package/dist/modules/whatsapp/whatsapp-chat.model.js.map +1 -0
  221. package/dist/modules/whatsapp/whatsapp-contact-profile.model.d.ts +23 -0
  222. package/dist/modules/whatsapp/whatsapp-contact-profile.model.js +54 -0
  223. package/dist/modules/whatsapp/whatsapp-contact-profile.model.js.map +1 -0
  224. package/dist/modules/whatsapp/whatsapp-message.model.d.ts +30 -0
  225. package/dist/modules/whatsapp/whatsapp-message.model.js +52 -0
  226. package/dist/modules/whatsapp/whatsapp-message.model.js.map +1 -0
  227. package/dist/modules/whatsapp/whatsapp-session.model.d.ts +33 -0
  228. package/dist/modules/whatsapp/whatsapp-session.model.js +65 -0
  229. package/dist/modules/whatsapp/whatsapp-session.model.js.map +1 -0
  230. package/dist/modules/whatsapp/whatsapp.routes.d.ts +2 -0
  231. package/dist/modules/whatsapp/whatsapp.routes.js +1237 -0
  232. package/dist/modules/whatsapp/whatsapp.routes.js.map +1 -0
  233. package/dist/plugins/cors.d.ts +3 -0
  234. package/dist/plugins/cors.js +11 -0
  235. package/dist/plugins/cors.js.map +1 -0
  236. package/dist/plugins/jwt.d.ts +3 -0
  237. package/dist/plugins/jwt.js +22 -0
  238. package/dist/plugins/jwt.js.map +1 -0
  239. package/dist/plugins/rawBody.d.ts +3 -0
  240. package/dist/plugins/rawBody.js +16 -0
  241. package/dist/plugins/rawBody.js.map +1 -0
  242. package/dist/plugins/rbac.d.ts +5 -0
  243. package/dist/plugins/rbac.js +29 -0
  244. package/dist/plugins/rbac.js.map +1 -0
  245. package/dist/routes/admin.routes.d.ts +2 -0
  246. package/dist/routes/admin.routes.js +169 -0
  247. package/dist/routes/admin.routes.js.map +1 -0
  248. package/dist/routes/health.routes.d.ts +2 -0
  249. package/dist/routes/health.routes.js +16 -0
  250. package/dist/routes/health.routes.js.map +1 -0
  251. package/dist/routes/webhook.routes.d.ts +2 -0
  252. package/dist/routes/webhook.routes.js +17 -0
  253. package/dist/routes/webhook.routes.js.map +1 -0
  254. package/dist/services/ai/base.ai.d.ts +19 -0
  255. package/dist/services/ai/base.ai.js +120 -0
  256. package/dist/services/ai/base.ai.js.map +1 -0
  257. package/dist/services/ai/gemini.service.d.ts +11 -0
  258. package/dist/services/ai/gemini.service.js +43 -0
  259. package/dist/services/ai/gemini.service.js.map +1 -0
  260. package/dist/services/ai/index.d.ts +2 -0
  261. package/dist/services/ai/index.js +26 -0
  262. package/dist/services/ai/index.js.map +1 -0
  263. package/dist/services/ai/openai.service.d.ts +11 -0
  264. package/dist/services/ai/openai.service.js +50 -0
  265. package/dist/services/ai/openai.service.js.map +1 -0
  266. package/dist/services/elevenlabs.service.d.ts +52 -0
  267. package/dist/services/elevenlabs.service.js +447 -0
  268. package/dist/services/elevenlabs.service.js.map +1 -0
  269. package/dist/services/google-calendar.service.d.ts +60 -0
  270. package/dist/services/google-calendar.service.js +494 -0
  271. package/dist/services/google-calendar.service.js.map +1 -0
  272. package/dist/services/inbound-call-schedule.service.d.ts +11 -0
  273. package/dist/services/inbound-call-schedule.service.js +162 -0
  274. package/dist/services/inbound-call-schedule.service.js.map +1 -0
  275. package/dist/services/netgsm.service.d.ts +41 -0
  276. package/dist/services/netgsm.service.js +89 -0
  277. package/dist/services/netgsm.service.js.map +1 -0
  278. package/dist/services/unipile.service.d.ts +41 -0
  279. package/dist/services/unipile.service.js +149 -0
  280. package/dist/services/unipile.service.js.map +1 -0
  281. package/dist/services/whatsapp-agent.service.d.ts +139 -0
  282. package/dist/services/whatsapp-agent.service.js +2055 -0
  283. package/dist/services/whatsapp-agent.service.js.map +1 -0
  284. package/dist/services/whatsapp.service.d.ts +26 -0
  285. package/dist/services/whatsapp.service.js +206 -0
  286. package/dist/services/whatsapp.service.js.map +1 -0
  287. package/dist/templates/index.d.ts +39 -0
  288. package/dist/templates/index.js +35 -0
  289. package/dist/templates/index.js.map +1 -0
  290. package/dist/templates/receptionist.d.ts +2 -0
  291. package/dist/templates/receptionist.js +39 -0
  292. package/dist/templates/receptionist.js.map +1 -0
  293. package/dist/templates/survey.d.ts +2 -0
  294. package/dist/templates/survey.js +41 -0
  295. package/dist/templates/survey.js.map +1 -0
  296. package/dist/types/index.d.ts +173 -0
  297. package/dist/types/index.js +3 -0
  298. package/dist/types/index.js.map +1 -0
  299. package/dist/utils/logger.d.ts +3 -0
  300. package/dist/utils/logger.js +105 -0
  301. package/dist/utils/logger.js.map +1 -0
  302. package/docker-compose.nestjs.yml +89 -0
  303. package/docker-compose.yml +78 -0
  304. package/docs/AI_AGENT_ENHANCEMENT_PLAN.md +164 -0
  305. package/docs/API.md +1193 -0
  306. package/docs/API_ENDPOINTS.md +344 -0
  307. package/docs/ARCHITECTURE.md +305 -0
  308. package/docs/AUTH_API.md +252 -0
  309. package/docs/BILLING_SMS_ALERTS.md +94 -0
  310. package/docs/CHAT_ASSIGNMENT_SYSTEM.md +118 -0
  311. package/docs/CLIENT_TOOLS_AND_FEATURES.md +337 -0
  312. package/docs/ELEVENLABS_WEBHOOK_TOOLS.md +644 -0
  313. package/docs/FRONTEND_CHECKLIST.md +227 -0
  314. package/docs/IMPLEMENTATION_STATUS.md +470 -0
  315. package/docs/MIGRATION_GUIDE.md +96 -0
  316. package/docs/MISSINGS_REPORT.md +507 -0
  317. package/docs/NESTJS_MIGRATION_REFERENCE.md +5136 -0
  318. package/docs/PROJECT_DESCRIPTION.md +1038 -0
  319. package/docs/SCALING.md +148 -0
  320. package/docs/SESSION_SUMMARY_2026_03_17.md +135 -0
  321. package/docs/WHATSAPP_AGENT.md +1086 -0
  322. package/docs/architecture/00-SYSTEM-OVERVIEW.md +318 -0
  323. package/docs/architecture/01-DATABASE-SCHEMA.md +2564 -0
  324. package/docs/architecture/02-AUTHENTICATION.md +1040 -0
  325. package/docs/architecture/03-MULTI-CLINIC.md +742 -0
  326. package/docs/architecture/04-WHATSAPP-AGENT.md +608 -0
  327. package/docs/architecture/05-OPERATOR-WORKFLOW.md +444 -0
  328. package/docs/architecture/06-BAILEYS-MICROSERVICE.md +616 -0
  329. package/docs/architecture/07-APPOINTMENTS.md +849 -0
  330. package/docs/architecture/08-VOICE-CALLS.md +470 -0
  331. package/docs/architecture/09-OUTBOUND-CAMPAIGNS.md +542 -0
  332. package/docs/architecture/10-CLIENT-TOOLS.md +665 -0
  333. package/docs/architecture/11-BILLING.md +458 -0
  334. package/docs/architecture/12-SECURITY.md +216 -0
  335. package/docs/architecture/13-LOGGING-AUDIT.md +549 -0
  336. package/docs/architecture/14-META-BUSINESS-API.md +454 -0
  337. package/docs/architecture/15-AI-MODULE.md +479 -0
  338. package/docs/architecture/16-BACKGROUND-JOBS.md +469 -0
  339. package/docs/architecture/17-REALTIME-LIVECHAT.md +447 -0
  340. package/docs/architecture/18-FILE-STORAGE.md +410 -0
  341. package/docs/architecture/19-PATIENTS.md +1034 -0
  342. package/docs/architecture/20-TREATMENTS-AND-PLANS.md +774 -0
  343. package/docs/architecture/21-BEFORE-AFTER-PHOTOS.md +519 -0
  344. package/docs/database.md +456 -0
  345. package/docs/ornek-randevu-onay.csv +3 -0
  346. package/ecosystem.config.js +16 -0
  347. package/elevenlabs-convai-api-reference.md +1171 -0
  348. package/frontend/.dockerignore +2 -0
  349. package/frontend/Dockerfile +24 -0
  350. package/frontend/README.md +75 -0
  351. package/frontend/components.json +25 -0
  352. package/frontend/eslint.config.js +23 -0
  353. package/frontend/index.html +13 -0
  354. package/frontend/nginx.conf +37 -0
  355. package/frontend/package-lock.json +8709 -0
  356. package/frontend/package.json +71 -0
  357. package/frontend/public/favicon.svg +1 -0
  358. package/frontend/public/icons.svg +24 -0
  359. package/frontend/src/App.tsx +125 -0
  360. package/frontend/src/components/error-boundary.tsx +50 -0
  361. package/frontend/src/components/shared/activity-timeline.tsx +66 -0
  362. package/frontend/src/components/shared/appointment-calendar.css +80 -0
  363. package/frontend/src/components/shared/appointment-calendar.tsx +245 -0
  364. package/frontend/src/components/shared/chat-bubble.tsx +72 -0
  365. package/frontend/src/components/shared/combobox.tsx +119 -0
  366. package/frontend/src/components/shared/confirm-dialog.tsx +57 -0
  367. package/frontend/src/components/shared/data-table-pagination.tsx +97 -0
  368. package/frontend/src/components/shared/data-table-toolbar.tsx +39 -0
  369. package/frontend/src/components/shared/empty-state.tsx +27 -0
  370. package/frontend/src/components/shared/file-upload.tsx +140 -0
  371. package/frontend/src/components/shared/index.ts +20 -0
  372. package/frontend/src/components/shared/language-switcher.tsx +29 -0
  373. package/frontend/src/components/shared/notification-dropdown.tsx +115 -0
  374. package/frontend/src/components/shared/page-header.tsx +23 -0
  375. package/frontend/src/components/shared/stat-card.tsx +37 -0
  376. package/frontend/src/components/shared/status-badge.tsx +70 -0
  377. package/frontend/src/components/shared/status-dot.tsx +43 -0
  378. package/frontend/src/components/ui/alert-dialog.tsx +187 -0
  379. package/frontend/src/components/ui/alert.tsx +76 -0
  380. package/frontend/src/components/ui/avatar.tsx +109 -0
  381. package/frontend/src/components/ui/badge.tsx +52 -0
  382. package/frontend/src/components/ui/breadcrumb.tsx +125 -0
  383. package/frontend/src/components/ui/button.tsx +60 -0
  384. package/frontend/src/components/ui/calendar.tsx +219 -0
  385. package/frontend/src/components/ui/card.tsx +103 -0
  386. package/frontend/src/components/ui/chart.tsx +371 -0
  387. package/frontend/src/components/ui/checkbox.tsx +29 -0
  388. package/frontend/src/components/ui/collapsible.tsx +19 -0
  389. package/frontend/src/components/ui/command.tsx +194 -0
  390. package/frontend/src/components/ui/dialog.tsx +158 -0
  391. package/frontend/src/components/ui/dropdown-menu.tsx +268 -0
  392. package/frontend/src/components/ui/input-group.tsx +156 -0
  393. package/frontend/src/components/ui/input.tsx +20 -0
  394. package/frontend/src/components/ui/label.tsx +20 -0
  395. package/frontend/src/components/ui/pagination.tsx +130 -0
  396. package/frontend/src/components/ui/popover.tsx +90 -0
  397. package/frontend/src/components/ui/progress.tsx +83 -0
  398. package/frontend/src/components/ui/radio-group.tsx +36 -0
  399. package/frontend/src/components/ui/scroll-area.tsx +54 -0
  400. package/frontend/src/components/ui/select.tsx +199 -0
  401. package/frontend/src/components/ui/separator.tsx +23 -0
  402. package/frontend/src/components/ui/sheet.tsx +138 -0
  403. package/frontend/src/components/ui/sidebar.tsx +723 -0
  404. package/frontend/src/components/ui/skeleton.tsx +13 -0
  405. package/frontend/src/components/ui/sonner.tsx +47 -0
  406. package/frontend/src/components/ui/switch.tsx +30 -0
  407. package/frontend/src/components/ui/table.tsx +114 -0
  408. package/frontend/src/components/ui/tabs.tsx +82 -0
  409. package/frontend/src/components/ui/textarea.tsx +18 -0
  410. package/frontend/src/components/ui/toggle-group.tsx +89 -0
  411. package/frontend/src/components/ui/toggle.tsx +43 -0
  412. package/frontend/src/components/ui/tooltip.tsx +64 -0
  413. package/frontend/src/hooks/use-mobile.ts +19 -0
  414. package/frontend/src/i18n/index.ts +20 -0
  415. package/frontend/src/i18n/locales/en.json +786 -0
  416. package/frontend/src/i18n/locales/tr.json +786 -0
  417. package/frontend/src/index.css +134 -0
  418. package/frontend/src/layouts/admin-layout.tsx +111 -0
  419. package/frontend/src/layouts/app-header.tsx +130 -0
  420. package/frontend/src/layouts/app-layout.tsx +19 -0
  421. package/frontend/src/layouts/app-sidebar.tsx +212 -0
  422. package/frontend/src/layouts/auth-guard.tsx +31 -0
  423. package/frontend/src/layouts/mobile-sidebar.tsx +120 -0
  424. package/frontend/src/lib/api.ts +68 -0
  425. package/frontend/src/lib/hooks/use-appointments.ts +224 -0
  426. package/frontend/src/lib/hooks/use-availability-blocks.ts +83 -0
  427. package/frontend/src/lib/hooks/use-chats.ts +236 -0
  428. package/frontend/src/lib/hooks/use-clinic-detail.ts +171 -0
  429. package/frontend/src/lib/hooks/use-clinics.ts +37 -0
  430. package/frontend/src/lib/hooks/use-doctor-calendar.ts +80 -0
  431. package/frontend/src/lib/hooks/use-examinations.ts +89 -0
  432. package/frontend/src/lib/hooks/use-medical-history.ts +52 -0
  433. package/frontend/src/lib/hooks/use-patients.ts +296 -0
  434. package/frontend/src/lib/hooks/use-photo-sets.ts +118 -0
  435. package/frontend/src/lib/hooks/use-treatment-plans.ts +152 -0
  436. package/frontend/src/lib/hooks/use-treatments.ts +125 -0
  437. package/frontend/src/lib/hooks/use-users.ts +172 -0
  438. package/frontend/src/lib/utils.ts +6 -0
  439. package/frontend/src/main.tsx +17 -0
  440. package/frontend/src/pages/admin/agents/detail.tsx +774 -0
  441. package/frontend/src/pages/admin/agents/import.tsx +280 -0
  442. package/frontend/src/pages/admin/agents/index.tsx +245 -0
  443. package/frontend/src/pages/admin/ai-playground.tsx +543 -0
  444. package/frontend/src/pages/appointments/create-appointment-dialog.tsx +390 -0
  445. package/frontend/src/pages/appointments/index.tsx +860 -0
  446. package/frontend/src/pages/audit/index.tsx +24 -0
  447. package/frontend/src/pages/auth/login.tsx +194 -0
  448. package/frontend/src/pages/billing/index.tsx +24 -0
  449. package/frontend/src/pages/calendar/index.tsx +704 -0
  450. package/frontend/src/pages/calendar-connections/index.tsx +295 -0
  451. package/frontend/src/pages/calls/index.tsx +24 -0
  452. package/frontend/src/pages/campaigns/index.tsx +24 -0
  453. package/frontend/src/pages/chats/index.tsx +981 -0
  454. package/frontend/src/pages/clinics/index.tsx +224 -0
  455. package/frontend/src/pages/clinics/settings.tsx +412 -0
  456. package/frontend/src/pages/components-showcase.tsx +773 -0
  457. package/frontend/src/pages/connections/index.tsx +328 -0
  458. package/frontend/src/pages/dashboard.tsx +50 -0
  459. package/frontend/src/pages/leads/index.tsx +24 -0
  460. package/frontend/src/pages/my-schedule/index.tsx +496 -0
  461. package/frontend/src/pages/patients/create-patient-dialog.tsx +358 -0
  462. package/frontend/src/pages/patients/detail.tsx +1195 -0
  463. package/frontend/src/pages/patients/edit-patient-dialog.tsx +387 -0
  464. package/frontend/src/pages/patients/examinations-tab.tsx +460 -0
  465. package/frontend/src/pages/patients/index.tsx +381 -0
  466. package/frontend/src/pages/patients/medical-history-dialog.tsx +207 -0
  467. package/frontend/src/pages/patients/photo-sets-tab.tsx +616 -0
  468. package/frontend/src/pages/patients/timeline-tab.tsx +164 -0
  469. package/frontend/src/pages/patients/treatment-plans-tab.tsx +598 -0
  470. package/frontend/src/pages/phone-numbers/detail.tsx +427 -0
  471. package/frontend/src/pages/phone-numbers/index.tsx +455 -0
  472. package/frontend/src/pages/platform/index.tsx +454 -0
  473. package/frontend/src/pages/settings/index.tsx +126 -0
  474. package/frontend/src/pages/treatments/index.tsx +487 -0
  475. package/frontend/src/pages/users/doctor-profile.tsx +672 -0
  476. package/frontend/src/pages/users/edit.tsx +329 -0
  477. package/frontend/src/pages/users/index.tsx +407 -0
  478. package/frontend/src/pages/validation/index.tsx +24 -0
  479. package/frontend/src/stores/auth.store.ts +108 -0
  480. package/frontend/src/stores/ui.store.ts +41 -0
  481. package/frontend/tsconfig.app.json +32 -0
  482. package/frontend/tsconfig.json +13 -0
  483. package/frontend/tsconfig.node.json +26 -0
  484. package/frontend/vite.config.ts +29 -0
  485. package/nestjs/.dockerignore +5 -0
  486. package/nestjs/.env.docker +64 -0
  487. package/nestjs/.prettierrc +4 -0
  488. package/nestjs/Dockerfile +36 -0
  489. package/nestjs/README.md +98 -0
  490. package/nestjs/eslint.config.mjs +35 -0
  491. package/nestjs/nest-cli.json +8 -0
  492. package/nestjs/package-lock.json +13390 -0
  493. package/nestjs/package.json +114 -0
  494. package/nestjs/prisma/migrations/20260409161536_add_message_metadata_fields/migration.sql +1746 -0
  495. package/nestjs/prisma/migrations/20260410140436_add_agent_ai_fields/migration.sql +36 -0
  496. package/nestjs/prisma/migrations/20260410175519_add_agent_clinic_assignments/migration.sql +21 -0
  497. package/nestjs/prisma/migrations/20260412094344_make_agent_tenant_optional/migration.sql +2 -0
  498. package/nestjs/prisma/migrations/20260412110008_add_admin_chat_sessions/migration.sql +47 -0
  499. package/nestjs/prisma/migrations/migration_lock.toml +3 -0
  500. package/nestjs/prisma/schema.prisma +1843 -0
  501. package/nestjs/prisma/seed.ts +375 -0
  502. package/nestjs/prisma.config.ts +14 -0
  503. package/nestjs/src/admin/admin.controller.ts +27 -0
  504. package/nestjs/src/admin/admin.module.ts +16 -0
  505. package/nestjs/src/admin/admin.service.ts +91 -0
  506. package/nestjs/src/admin/ai-chat-session.service.ts +454 -0
  507. package/nestjs/src/admin/ai-test.controller.ts +191 -0
  508. package/nestjs/src/admin/superadmin.controller.ts +106 -0
  509. package/nestjs/src/agents/agents.controller.ts +262 -0
  510. package/nestjs/src/agents/agents.module.ts +13 -0
  511. package/nestjs/src/agents/agents.service.ts +733 -0
  512. package/nestjs/src/agents/dto/create-agent.dto.ts +99 -0
  513. package/nestjs/src/agents/dto/index.ts +2 -0
  514. package/nestjs/src/agents/dto/update-agent.dto.ts +148 -0
  515. package/nestjs/src/app.module.ts +115 -0
  516. package/nestjs/src/appointment-validation/appointment-validation.controller.ts +194 -0
  517. package/nestjs/src/appointment-validation/appointment-validation.module.ts +16 -0
  518. package/nestjs/src/appointment-validation/appointment-validation.service.ts +450 -0
  519. package/nestjs/src/appointment-validation/dto/create-batch.dto.ts +105 -0
  520. package/nestjs/src/appointment-validation/dto/index.ts +1 -0
  521. package/nestjs/src/appointment-validation/processors/validation-dispatch.processor.ts +26 -0
  522. package/nestjs/src/appointment-validation/processors/validation-sync.processor.ts +23 -0
  523. package/nestjs/src/appointments/appointments.controller.ts +268 -0
  524. package/nestjs/src/appointments/appointments.module.ts +13 -0
  525. package/nestjs/src/appointments/appointments.service.ts +773 -0
  526. package/nestjs/src/appointments/dto/create-appointment.dto.ts +72 -0
  527. package/nestjs/src/appointments/dto/index.ts +4 -0
  528. package/nestjs/src/appointments/dto/query-appointments.dto.ts +60 -0
  529. package/nestjs/src/appointments/dto/update-appointment.dto.ts +43 -0
  530. package/nestjs/src/appointments/dto/update-status.dto.ts +18 -0
  531. package/nestjs/src/appointments/processors/reminder.processor.ts +243 -0
  532. package/nestjs/src/audit/audit.controller.ts +84 -0
  533. package/nestjs/src/audit/audit.decorator.ts +20 -0
  534. package/nestjs/src/audit/audit.interceptor.ts +67 -0
  535. package/nestjs/src/audit/audit.interfaces.ts +15 -0
  536. package/nestjs/src/audit/audit.module.ts +12 -0
  537. package/nestjs/src/audit/audit.service.ts +177 -0
  538. package/nestjs/src/auth/auth.controller.ts +116 -0
  539. package/nestjs/src/auth/auth.module.ts +25 -0
  540. package/nestjs/src/auth/auth.service.ts +612 -0
  541. package/nestjs/src/auth/dto/change-password.dto.ts +13 -0
  542. package/nestjs/src/auth/dto/forgot-password.dto.ts +8 -0
  543. package/nestjs/src/auth/dto/index.ts +6 -0
  544. package/nestjs/src/auth/dto/login.dto.ts +13 -0
  545. package/nestjs/src/auth/dto/refresh.dto.ts +8 -0
  546. package/nestjs/src/auth/dto/register.dto.ts +28 -0
  547. package/nestjs/src/auth/dto/reset-password.dto.ts +13 -0
  548. package/nestjs/src/auth/permissions.config.ts +85 -0
  549. package/nestjs/src/availability-blocks/availability-blocks.controller.ts +83 -0
  550. package/nestjs/src/availability-blocks/availability-blocks.module.ts +10 -0
  551. package/nestjs/src/availability-blocks/availability-blocks.service.ts +202 -0
  552. package/nestjs/src/billing/billing.controller.ts +104 -0
  553. package/nestjs/src/billing/billing.module.ts +12 -0
  554. package/nestjs/src/billing/billing.service.ts +398 -0
  555. package/nestjs/src/billing/dto/index.ts +2 -0
  556. package/nestjs/src/billing/dto/query-billing.dto.ts +29 -0
  557. package/nestjs/src/billing/dto/record-payment.dto.ts +60 -0
  558. package/nestjs/src/billing/processors/alerts.processor.ts +216 -0
  559. package/nestjs/src/billing/processors/snapshot.processor.ts +181 -0
  560. package/nestjs/src/calls/calls.controller.ts +92 -0
  561. package/nestjs/src/calls/calls.module.ts +10 -0
  562. package/nestjs/src/calls/calls.service.ts +359 -0
  563. package/nestjs/src/calls/dto/index.ts +1 -0
  564. package/nestjs/src/calls/dto/query-calls.dto.ts +46 -0
  565. package/nestjs/src/clinics/clinics.controller.ts +226 -0
  566. package/nestjs/src/clinics/clinics.module.ts +11 -0
  567. package/nestjs/src/clinics/clinics.service.ts +203 -0
  568. package/nestjs/src/clinics/dto/create-clinic.dto.ts +40 -0
  569. package/nestjs/src/clinics/dto/create-meta-connection.dto.ts +44 -0
  570. package/nestjs/src/clinics/dto/index.ts +4 -0
  571. package/nestjs/src/clinics/dto/update-clinic.dto.ts +133 -0
  572. package/nestjs/src/clinics/dto/update-meta-connection.dto.ts +22 -0
  573. package/nestjs/src/clinics/meta-connections.service.ts +210 -0
  574. package/nestjs/src/common/decorators/accessible-clinics.decorator.ts +9 -0
  575. package/nestjs/src/common/decorators/current-user.decorator.ts +10 -0
  576. package/nestjs/src/common/decorators/features.decorator.ts +7 -0
  577. package/nestjs/src/common/decorators/index.ts +4 -0
  578. package/nestjs/src/common/decorators/permissions.decorator.ts +13 -0
  579. package/nestjs/src/common/gateways/events.gateway.ts +157 -0
  580. package/nestjs/src/common/guards/clinic-access.guard.ts +40 -0
  581. package/nestjs/src/common/guards/features.guard.ts +38 -0
  582. package/nestjs/src/common/guards/index.ts +5 -0
  583. package/nestjs/src/common/guards/jwt-auth.guard.ts +54 -0
  584. package/nestjs/src/common/guards/permissions.guard.ts +50 -0
  585. package/nestjs/src/common/guards/superadmin.guard.ts +35 -0
  586. package/nestjs/src/common/interceptors/request-context.interceptor.ts +32 -0
  587. package/nestjs/src/common/interceptors/superadmin-tenant.interceptor.ts +42 -0
  588. package/nestjs/src/common/interfaces/jwt-payload.interface.ts +11 -0
  589. package/nestjs/src/config/config.module.ts +13 -0
  590. package/nestjs/src/database/database.module.ts +9 -0
  591. package/nestjs/src/database/prisma.service.ts +20 -0
  592. package/nestjs/src/database/tenant-context.ts +27 -0
  593. package/nestjs/src/google-calendar/google-calendar.controller.ts +259 -0
  594. package/nestjs/src/google-calendar/google-calendar.module.ts +10 -0
  595. package/nestjs/src/google-calendar/google-calendar.service.ts +811 -0
  596. package/nestjs/src/integrations/ai/ai.interface.ts +74 -0
  597. package/nestjs/src/integrations/ai/ai.module.ts +11 -0
  598. package/nestjs/src/integrations/ai/ai.service.ts +148 -0
  599. package/nestjs/src/integrations/ai/providers/anthropic.provider.ts +146 -0
  600. package/nestjs/src/integrations/ai/providers/openai.provider.ts +158 -0
  601. package/nestjs/src/integrations/elevenlabs/elevenlabs.module.ts +8 -0
  602. package/nestjs/src/integrations/elevenlabs/elevenlabs.service.ts +226 -0
  603. package/nestjs/src/integrations/encryption/encryption.module.ts +9 -0
  604. package/nestjs/src/integrations/encryption/encryption.service.ts +31 -0
  605. package/nestjs/src/integrations/image/image.module.ts +9 -0
  606. package/nestjs/src/integrations/image/image.service.ts +61 -0
  607. package/nestjs/src/integrations/meta-business/meta-business.module.ts +10 -0
  608. package/nestjs/src/integrations/meta-business/meta-instagram.service.ts +94 -0
  609. package/nestjs/src/integrations/meta-business/meta-webhook.service.ts +52 -0
  610. package/nestjs/src/integrations/meta-business/meta-whatsapp.service.ts +254 -0
  611. package/nestjs/src/integrations/minio/minio.module.ts +9 -0
  612. package/nestjs/src/integrations/minio/minio.service.ts +88 -0
  613. package/nestjs/src/integrations/netgsm/netgsm.module.ts +8 -0
  614. package/nestjs/src/integrations/netgsm/netgsm.service.ts +17 -0
  615. package/nestjs/src/integrations/tektippay/tektippay.module.ts +8 -0
  616. package/nestjs/src/integrations/tektippay/tektippay.service.ts +23 -0
  617. package/nestjs/src/leads/dto/index.ts +2 -0
  618. package/nestjs/src/leads/dto/query-leads.dto.ts +50 -0
  619. package/nestjs/src/leads/dto/update-lead.dto.ts +26 -0
  620. package/nestjs/src/leads/leads.controller.ts +184 -0
  621. package/nestjs/src/leads/leads.module.ts +10 -0
  622. package/nestjs/src/leads/leads.service.ts +375 -0
  623. package/nestjs/src/logging/logging.controller.ts +82 -0
  624. package/nestjs/src/logging/logging.module.ts +11 -0
  625. package/nestjs/src/logging/logging.service.ts +180 -0
  626. package/nestjs/src/main.ts +86 -0
  627. package/nestjs/src/outbound-campaigns/dto/create-campaign.dto.ts +47 -0
  628. package/nestjs/src/outbound-campaigns/dto/index.ts +3 -0
  629. package/nestjs/src/outbound-campaigns/dto/update-campaign.dto.ts +31 -0
  630. package/nestjs/src/outbound-campaigns/dto/upload-entries.dto.ts +35 -0
  631. package/nestjs/src/outbound-campaigns/outbound-campaigns.controller.ts +307 -0
  632. package/nestjs/src/outbound-campaigns/outbound-campaigns.module.ts +13 -0
  633. package/nestjs/src/outbound-campaigns/outbound-campaigns.service.ts +471 -0
  634. package/nestjs/src/outbound-campaigns/processors/campaign-dispatch.processor.ts +366 -0
  635. package/nestjs/src/patients/documents.service.ts +231 -0
  636. package/nestjs/src/patients/dto/create-examination.dto.ts +34 -0
  637. package/nestjs/src/patients/dto/create-note.dto.ts +14 -0
  638. package/nestjs/src/patients/dto/create-patient.dto.ts +86 -0
  639. package/nestjs/src/patients/dto/create-photo-set.dto.ts +32 -0
  640. package/nestjs/src/patients/dto/index.ts +10 -0
  641. package/nestjs/src/patients/dto/update-examination.dto.ts +29 -0
  642. package/nestjs/src/patients/dto/update-medical-history.dto.ts +47 -0
  643. package/nestjs/src/patients/dto/update-patient.dto.ts +87 -0
  644. package/nestjs/src/patients/dto/update-photo-set.dto.ts +28 -0
  645. package/nestjs/src/patients/dto/upload-document.dto.ts +31 -0
  646. package/nestjs/src/patients/dto/upload-photo.dto.ts +27 -0
  647. package/nestjs/src/patients/examinations.service.ts +271 -0
  648. package/nestjs/src/patients/medical-history.service.ts +149 -0
  649. package/nestjs/src/patients/notes.service.ts +172 -0
  650. package/nestjs/src/patients/patients.controller.ts +485 -0
  651. package/nestjs/src/patients/patients.module.ts +22 -0
  652. package/nestjs/src/patients/patients.service.ts +412 -0
  653. package/nestjs/src/patients/photo-sets.service.ts +389 -0
  654. package/nestjs/src/phone-numbers/dto/create-phone-number.dto.ts +57 -0
  655. package/nestjs/src/phone-numbers/dto/index.ts +3 -0
  656. package/nestjs/src/phone-numbers/dto/update-phone-number.dto.ts +38 -0
  657. package/nestjs/src/phone-numbers/dto/update-schedule.dto.ts +39 -0
  658. package/nestjs/src/phone-numbers/phone-numbers.controller.ts +125 -0
  659. package/nestjs/src/phone-numbers/phone-numbers.module.ts +10 -0
  660. package/nestjs/src/phone-numbers/phone-numbers.service.ts +209 -0
  661. package/nestjs/src/plans/dto/create-plan.dto.ts +70 -0
  662. package/nestjs/src/plans/dto/index.ts +2 -0
  663. package/nestjs/src/plans/dto/update-plan.dto.ts +80 -0
  664. package/nestjs/src/plans/plans.controller.ts +50 -0
  665. package/nestjs/src/plans/plans.module.ts +10 -0
  666. package/nestjs/src/plans/plans.service.ts +115 -0
  667. package/nestjs/src/platform/dto/create-tenant.dto.ts +36 -0
  668. package/nestjs/src/platform/dto/index.ts +2 -0
  669. package/nestjs/src/platform/dto/update-tenant-platform.dto.ts +44 -0
  670. package/nestjs/src/platform/platform.controller.ts +79 -0
  671. package/nestjs/src/platform/platform.module.ts +10 -0
  672. package/nestjs/src/platform/platform.service.ts +301 -0
  673. package/nestjs/src/queue/queue.module.ts +56 -0
  674. package/nestjs/src/redis/redis.module.ts +20 -0
  675. package/nestjs/src/tenants/dto/index.ts +1 -0
  676. package/nestjs/src/tenants/dto/update-tenant.dto.ts +15 -0
  677. package/nestjs/src/tenants/tenants.controller.ts +45 -0
  678. package/nestjs/src/tenants/tenants.module.ts +10 -0
  679. package/nestjs/src/tenants/tenants.service.ts +41 -0
  680. package/nestjs/src/tools/adapters/elevenlabs-http.adapter.ts +51 -0
  681. package/nestjs/src/tools/adapters/elevenlabs-ws.adapter.ts +59 -0
  682. package/nestjs/src/tools/handlers/calendar.tools.ts +441 -0
  683. package/nestjs/src/tools/handlers/notification.tools.ts +34 -0
  684. package/nestjs/src/tools/handlers/operator.tools.ts +303 -0
  685. package/nestjs/src/tools/handlers/patient.tools.ts +242 -0
  686. package/nestjs/src/tools/handlers/payment.tools.ts +43 -0
  687. package/nestjs/src/tools/handlers/validation.tools.ts +152 -0
  688. package/nestjs/src/tools/tool-registry.service.ts +221 -0
  689. package/nestjs/src/tools/tool.decorator.ts +16 -0
  690. package/nestjs/src/tools/tool.interfaces.ts +26 -0
  691. package/nestjs/src/tools/tools.module.ts +50 -0
  692. package/nestjs/src/treatments/dto/create-plan-item.dto.ts +27 -0
  693. package/nestjs/src/treatments/dto/create-treatment-plan.dto.ts +69 -0
  694. package/nestjs/src/treatments/dto/create-treatment.dto.ts +59 -0
  695. package/nestjs/src/treatments/dto/index.ts +6 -0
  696. package/nestjs/src/treatments/dto/update-plan-item.dto.ts +23 -0
  697. package/nestjs/src/treatments/dto/update-treatment-plan.dto.ts +22 -0
  698. package/nestjs/src/treatments/dto/update-treatment.dto.ts +70 -0
  699. package/nestjs/src/treatments/treatment-plans.service.ts +362 -0
  700. package/nestjs/src/treatments/treatments.controller.ts +265 -0
  701. package/nestjs/src/treatments/treatments.module.ts +14 -0
  702. package/nestjs/src/treatments/treatments.service.ts +165 -0
  703. package/nestjs/src/users/doctor-profiles.service.ts +202 -0
  704. package/nestjs/src/users/dto/index.ts +4 -0
  705. package/nestjs/src/users/dto/invite-user.dto.ts +52 -0
  706. package/nestjs/src/users/dto/update-clinic-assignments.dto.ts +9 -0
  707. package/nestjs/src/users/dto/update-doctor-profile.dto.ts +49 -0
  708. package/nestjs/src/users/dto/update-user.dto.ts +41 -0
  709. package/nestjs/src/users/users.controller.ts +142 -0
  710. package/nestjs/src/users/users.module.ts +11 -0
  711. package/nestjs/src/users/users.service.ts +250 -0
  712. package/nestjs/src/webhooks/elevenlabs-tool.controller.ts +66 -0
  713. package/nestjs/src/webhooks/elevenlabs-webhook.controller.ts +60 -0
  714. package/nestjs/src/webhooks/meta-webhook.controller.ts +178 -0
  715. package/nestjs/src/webhooks/processors/elevenlabs-webhook.processor.ts +28 -0
  716. package/nestjs/src/webhooks/webhooks.module.ts +17 -0
  717. package/nestjs/src/whatsapp/chat-context.service.ts +281 -0
  718. package/nestjs/src/whatsapp/dto/add-blacklist.dto.ts +13 -0
  719. package/nestjs/src/whatsapp/dto/index.ts +2 -0
  720. package/nestjs/src/whatsapp/dto/send-message.dto.ts +14 -0
  721. package/nestjs/src/whatsapp/listeners/meta-message.listener.ts +579 -0
  722. package/nestjs/src/whatsapp/meta-auth.controller.ts +268 -0
  723. package/nestjs/src/whatsapp/meta-instagram-auth.controller.ts +244 -0
  724. package/nestjs/src/whatsapp/operator.service.ts +692 -0
  725. package/nestjs/src/whatsapp/processors/cost-sweep.processor.ts +130 -0
  726. package/nestjs/src/whatsapp/processors/grace.processor.ts +191 -0
  727. package/nestjs/src/whatsapp/processors/message-buffer.processor.ts +138 -0
  728. package/nestjs/src/whatsapp/processors/message-cleanup.processor.ts +148 -0
  729. package/nestjs/src/whatsapp/processors/meta-token-refresh.processor.ts +114 -0
  730. package/nestjs/src/whatsapp/processors/operator-expiry.processor.ts +105 -0
  731. package/nestjs/src/whatsapp/processors/profile-update.processor.ts +234 -0
  732. package/nestjs/src/whatsapp/processors/session-cleanup.processor.ts +178 -0
  733. package/nestjs/src/whatsapp/processors/session-labels.processor.ts +248 -0
  734. package/nestjs/src/whatsapp/whatsapp-agent.service.ts +2506 -0
  735. package/nestjs/src/whatsapp/whatsapp-recovery.service.ts +117 -0
  736. package/nestjs/src/whatsapp/whatsapp.controller.ts +398 -0
  737. package/nestjs/src/whatsapp/whatsapp.module.ts +51 -0
  738. package/nestjs/src/whatsapp/whatsapp.service.ts +592 -0
  739. package/nestjs/test/app.e2e-spec.ts +25 -0
  740. package/nestjs/test/jest-e2e.json +9 -0
  741. package/nestjs/tsconfig.build.json +4 -0
  742. package/nestjs/tsconfig.json +25 -0
  743. package/nginx.example.conf +18 -0
  744. package/package.json +47 -0
  745. package/scripts/addRecipient.ts +48 -0
  746. package/scripts/listRecipients.ts +31 -0
  747. package/scripts/migrate-agent-ref.ts +86 -0
  748. package/scripts/migrate-sessions.ts +183 -0
  749. package/scripts/promote.ts +27 -0
  750. package/scripts/retrigger.ts +84 -0
  751. package/scripts/seed.ts +435 -0
  752. package/scripts/testSend.ts +63 -0
  753. package/src/app.ts +85 -0
  754. package/src/config/agentPrompts.json +19 -0
  755. package/src/config/database.ts +21 -0
  756. package/src/config/env.ts +94 -0
  757. package/src/config/index.ts +86 -0
  758. package/src/controllers/webhook.controller.ts +150 -0
  759. package/src/errors/index.ts +9 -0
  760. package/src/hooks/webhookValidator.ts +55 -0
  761. package/src/index.ts +68 -0
  762. package/src/migrations/001_session-architecture.ts +138 -0
  763. package/src/migrations/002_agent-ref.ts +65 -0
  764. package/src/migrations/003_shift-schedule.ts +55 -0
  765. package/src/migrations/004_invert-shift-to-clinic-hours.ts +30 -0
  766. package/src/migrations/005_composite-baileys-chat-ids.ts +60 -0
  767. package/src/migrations/migration.model.ts +27 -0
  768. package/src/migrations/migration.routes.ts +40 -0
  769. package/src/migrations/migration.runner.ts +112 -0
  770. package/src/models/ConversationClaim.ts +17 -0
  771. package/src/models/ConversationEvaluation.ts +91 -0
  772. package/src/models/Summary.ts +77 -0
  773. package/src/models/Transcription.ts +68 -0
  774. package/src/models/WhatsAppRecipient.ts +37 -0
  775. package/src/modules/admin/admin.routes.ts +2385 -0
  776. package/src/modules/admin/elevenlabs-test-chat.routes.ts +193 -0
  777. package/src/modules/admin/whatsapp-test-chat.routes.ts +244 -0
  778. package/src/modules/agents/agent.model.ts +93 -0
  779. package/src/modules/agents/agent.routes.ts +65 -0
  780. package/src/modules/appointment-validation/appointment-validation.model.ts +163 -0
  781. package/src/modules/appointment-validation/appointment-validation.routes.ts +275 -0
  782. package/src/modules/appointment-validation/appointment-validation.service.ts +1028 -0
  783. package/src/modules/auth/auth.model.ts +42 -0
  784. package/src/modules/auth/auth.routes.ts +199 -0
  785. package/src/modules/auth/auth.service.ts +210 -0
  786. package/src/modules/auth/refresh-token.model.ts +26 -0
  787. package/src/modules/billing/billing-alert.model.ts +28 -0
  788. package/src/modules/billing/billing-period-snapshot.model.ts +68 -0
  789. package/src/modules/billing/billing.model.ts +42 -0
  790. package/src/modules/billing/billing.routes.ts +67 -0
  791. package/src/modules/billing/billing.service.ts +562 -0
  792. package/src/modules/billing/payment.model.ts +42 -0
  793. package/src/modules/calls/call.model.ts +102 -0
  794. package/src/modules/calls/call.routes.ts +118 -0
  795. package/src/modules/campaigns/campaign.model.ts +111 -0
  796. package/src/modules/campaigns/campaign.routes.ts +402 -0
  797. package/src/modules/campaigns/campaign.service.ts +99 -0
  798. package/src/modules/google-calendar/google-calendar.routes.ts +31 -0
  799. package/src/modules/inbound-call/inbound-call-config.model.ts +49 -0
  800. package/src/modules/inbound-call/inbound-call.routes.ts +289 -0
  801. package/src/modules/leads/lead.model.ts +40 -0
  802. package/src/modules/leads/lead.routes.ts +246 -0
  803. package/src/modules/logs/log.model.ts +27 -0
  804. package/src/modules/logs/log.routes.ts +102 -0
  805. package/src/modules/plans/plan.model.ts +45 -0
  806. package/src/modules/surveys/survey.model.ts +70 -0
  807. package/src/modules/surveys/survey.routes.ts +187 -0
  808. package/src/modules/tenants/tenant.model.ts +181 -0
  809. package/src/modules/tenants/tenant.routes.ts +78 -0
  810. package/src/modules/users/user.routes.ts +126 -0
  811. package/src/modules/webhooks/elevenlabs-tool.routes.ts +94 -0
  812. package/src/modules/webhooks/elevenlabs-tool.service.ts +491 -0
  813. package/src/modules/webhooks/elevenlabs.routes.ts +34 -0
  814. package/src/modules/webhooks/elevenlabs.service.ts +565 -0
  815. package/src/modules/webhooks/unipile.routes.ts +917 -0
  816. package/src/modules/whatsapp/appointment.model.ts +47 -0
  817. package/src/modules/whatsapp/operator-request.model.ts +58 -0
  818. package/src/modules/whatsapp/stt-usage.model.ts +30 -0
  819. package/src/modules/whatsapp/whatsapp-chat.model.ts +39 -0
  820. package/src/modules/whatsapp/whatsapp-contact-profile.model.ts +41 -0
  821. package/src/modules/whatsapp/whatsapp-message.model.ts +41 -0
  822. package/src/modules/whatsapp/whatsapp-session.model.ts +60 -0
  823. package/src/modules/whatsapp/whatsapp.routes.ts +1435 -0
  824. package/src/plugins/cors.ts +7 -0
  825. package/src/plugins/jwt.ts +18 -0
  826. package/src/plugins/rawBody.ts +12 -0
  827. package/src/plugins/rbac.ts +24 -0
  828. package/src/routes/admin.routes.ts +208 -0
  829. package/src/routes/health.routes.ts +12 -0
  830. package/src/routes/webhook.routes.ts +12 -0
  831. package/src/services/ai/base.ai.ts +132 -0
  832. package/src/services/ai/gemini.service.ts +41 -0
  833. package/src/services/ai/index.ts +24 -0
  834. package/src/services/ai/openai.service.ts +48 -0
  835. package/src/services/elevenlabs.service.ts +532 -0
  836. package/src/services/google-calendar.service.ts +656 -0
  837. package/src/services/inbound-call-schedule.service.ts +174 -0
  838. package/src/services/netgsm.service.ts +128 -0
  839. package/src/services/unipile.service.ts +200 -0
  840. package/src/services/whatsapp-agent.service.ts +2479 -0
  841. package/src/services/whatsapp.service.ts +245 -0
  842. package/src/templates/index.ts +71 -0
  843. package/src/templates/receptionist.ts +44 -0
  844. package/src/templates/survey.ts +44 -0
  845. package/src/types/index.ts +218 -0
  846. package/src/utils/logger.ts +83 -0
  847. package/tsconfig.json +19 -0
  848. package/web/.dockerignore +4 -0
  849. package/web/Dockerfile +18 -0
  850. package/web/README.md +73 -0
  851. package/web/components.json +23 -0
  852. package/web/eslint.config.js +23 -0
  853. package/web/index.html +14 -0
  854. package/web/nginx.conf +18 -0
  855. package/web/package-lock.json +10292 -0
  856. package/web/package.json +48 -0
  857. package/web/public/favicon.ico +0 -0
  858. package/web/public/vite.svg +1 -0
  859. package/web/src/App.tsx +191 -0
  860. package/web/src/assets/react.svg +1 -0
  861. package/web/src/components/Layout.tsx +261 -0
  862. package/web/src/components/LeadConversation.tsx +251 -0
  863. package/web/src/components/ProtectedRoute.tsx +20 -0
  864. package/web/src/components/conversation-review/CardAudioPlayer.tsx +200 -0
  865. package/web/src/components/conversation-review/CardEvaluation.tsx +351 -0
  866. package/web/src/components/conversation-review/CardMetadata.tsx +44 -0
  867. package/web/src/components/conversation-review/ConversationCard.tsx +95 -0
  868. package/web/src/components/conversation-review/ReviewContainer.tsx +120 -0
  869. package/web/src/components/conversation-review/ReviewFilters.tsx +88 -0
  870. package/web/src/components/empty-state.tsx +15 -0
  871. package/web/src/components/page-header.tsx +19 -0
  872. package/web/src/components/page-loader.tsx +32 -0
  873. package/web/src/components/pagination.tsx +38 -0
  874. package/web/src/components/stat-card.tsx +27 -0
  875. package/web/src/components/status-badge.tsx +125 -0
  876. package/web/src/components/ui/alert.tsx +66 -0
  877. package/web/src/components/ui/badge.tsx +48 -0
  878. package/web/src/components/ui/button.tsx +64 -0
  879. package/web/src/components/ui/card.tsx +92 -0
  880. package/web/src/components/ui/checkbox.tsx +32 -0
  881. package/web/src/components/ui/dialog.tsx +158 -0
  882. package/web/src/components/ui/dropdown-menu.tsx +255 -0
  883. package/web/src/components/ui/input.tsx +21 -0
  884. package/web/src/components/ui/label.tsx +22 -0
  885. package/web/src/components/ui/progress.tsx +29 -0
  886. package/web/src/components/ui/select.tsx +188 -0
  887. package/web/src/components/ui/separator.tsx +28 -0
  888. package/web/src/components/ui/sheet.tsx +123 -0
  889. package/web/src/components/ui/skeleton.tsx +13 -0
  890. package/web/src/components/ui/sonner.tsx +35 -0
  891. package/web/src/components/ui/table.tsx +116 -0
  892. package/web/src/components/ui/tabs.tsx +89 -0
  893. package/web/src/components/ui/textarea.tsx +18 -0
  894. package/web/src/components/ui/tooltip.tsx +57 -0
  895. package/web/src/components/whatsapp/ChatDetail.tsx +417 -0
  896. package/web/src/components/whatsapp/ChatHeader.tsx +78 -0
  897. package/web/src/components/whatsapp/ChatList.tsx +107 -0
  898. package/web/src/components/whatsapp/ChatListItem.tsx +60 -0
  899. package/web/src/components/whatsapp/MessageBubble.tsx +46 -0
  900. package/web/src/components/whatsapp/MessageInput.tsx +63 -0
  901. package/web/src/components/whatsapp/MessageStream.tsx +135 -0
  902. package/web/src/components/whatsapp/SessionDivider.tsx +65 -0
  903. package/web/src/components/whatsapp/SessionHistory.tsx +119 -0
  904. package/web/src/components/whatsapp/settings/AiSettingsTab.tsx +268 -0
  905. package/web/src/components/whatsapp/settings/CalendarTab.tsx +339 -0
  906. package/web/src/components/whatsapp/settings/ConnectionTab.tsx +236 -0
  907. package/web/src/components/whatsapp/settings/NotificationsTab.tsx +109 -0
  908. package/web/src/components/whatsapp/settings/OperatorTab.tsx +303 -0
  909. package/web/src/contexts/AuthContext.tsx +103 -0
  910. package/web/src/index.css +130 -0
  911. package/web/src/lib/api.ts +92 -0
  912. package/web/src/lib/utils.ts +6 -0
  913. package/web/src/main.tsx +10 -0
  914. package/web/src/pages/AppointmentDetail.tsx +206 -0
  915. package/web/src/pages/AppointmentValidation.tsx +157 -0
  916. package/web/src/pages/AppointmentValidationDetail.tsx +617 -0
  917. package/web/src/pages/AppointmentValidationNew.tsx +1005 -0
  918. package/web/src/pages/Appointments.tsx +283 -0
  919. package/web/src/pages/Billing.tsx +126 -0
  920. package/web/src/pages/CallDetail.tsx +293 -0
  921. package/web/src/pages/CallSettings.tsx +313 -0
  922. package/web/src/pages/Calls.tsx +188 -0
  923. package/web/src/pages/CampaignDetail.tsx +216 -0
  924. package/web/src/pages/CampaignNew.tsx +277 -0
  925. package/web/src/pages/CampaignResults.tsx +185 -0
  926. package/web/src/pages/Campaigns.tsx +171 -0
  927. package/web/src/pages/Dashboard.tsx +336 -0
  928. package/web/src/pages/InboundSchedule.tsx +246 -0
  929. package/web/src/pages/LeadDetail.tsx +183 -0
  930. package/web/src/pages/Leads.tsx +258 -0
  931. package/web/src/pages/Login.tsx +99 -0
  932. package/web/src/pages/Register.tsx +129 -0
  933. package/web/src/pages/Settings.tsx +133 -0
  934. package/web/src/pages/SurveyDetail.tsx +232 -0
  935. package/web/src/pages/SurveyPreview.tsx +179 -0
  936. package/web/src/pages/Surveys.tsx +207 -0
  937. package/web/src/pages/Users.tsx +199 -0
  938. package/web/src/pages/WhatsApp.tsx +147 -0
  939. package/web/src/pages/WhatsAppSettings.tsx +215 -0
  940. package/web/src/pages/admin/AdminBaileysMessageLog.tsx +331 -0
  941. package/web/src/pages/admin/AdminBaileysRawMessages.tsx +318 -0
  942. package/web/src/pages/admin/AdminConversationReview.tsx +116 -0
  943. package/web/src/pages/admin/AdminCredits.tsx +467 -0
  944. package/web/src/pages/admin/AdminElevenLabsAgentDetail.tsx +332 -0
  945. package/web/src/pages/admin/AdminElevenLabsAgents.tsx +164 -0
  946. package/web/src/pages/admin/AdminElevenLabsBatchCallDetail.tsx +214 -0
  947. package/web/src/pages/admin/AdminElevenLabsBatchCalls.tsx +293 -0
  948. package/web/src/pages/admin/AdminElevenLabsConversationDetail.tsx +230 -0
  949. package/web/src/pages/admin/AdminElevenLabsConversations.tsx +228 -0
  950. package/web/src/pages/admin/AdminElevenLabsInboundCalls.tsx +293 -0
  951. package/web/src/pages/admin/AdminElevenLabsPhoneNumbers.tsx +506 -0
  952. package/web/src/pages/admin/AdminElevenLabsTestChat.tsx +258 -0
  953. package/web/src/pages/admin/AdminElevenLabsWebhooks.tsx +289 -0
  954. package/web/src/pages/admin/AdminEvaluationDashboard.tsx +520 -0
  955. package/web/src/pages/admin/AdminLeads.tsx +339 -0
  956. package/web/src/pages/admin/AdminLogs.tsx +283 -0
  957. package/web/src/pages/admin/AdminMigrations.tsx +247 -0
  958. package/web/src/pages/admin/AdminPlans.tsx +313 -0
  959. package/web/src/pages/admin/AdminSystem.tsx +391 -0
  960. package/web/src/pages/admin/AdminTenantBillableItems.tsx +464 -0
  961. package/web/src/pages/admin/AdminTenantDetail.tsx +1317 -0
  962. package/web/src/pages/admin/AdminTenants.tsx +274 -0
  963. package/web/src/pages/admin/AdminWhatsApp.tsx +618 -0
  964. package/web/src/pages/admin/AdminWhatsAppTestChat.tsx +328 -0
  965. package/web/src/types/index.ts +242 -0
  966. package/web/tsconfig.app.json +32 -0
  967. package/web/tsconfig.json +13 -0
  968. package/web/tsconfig.node.json +26 -0
  969. package/web/vite.config.ts +23 -0
@@ -0,0 +1,780 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = unipileWebhookRoutes;
7
+ const config_1 = __importDefault(require("../../config"));
8
+ const logger_1 = __importDefault(require("../../utils/logger"));
9
+ const tenant_model_1 = __importDefault(require("../tenants/tenant.model"));
10
+ const agent_model_1 = __importDefault(require("../agents/agent.model"));
11
+ const whatsapp_chat_model_1 = __importDefault(require("../whatsapp/whatsapp-chat.model"));
12
+ const whatsapp_session_model_1 = __importDefault(require("../whatsapp/whatsapp-session.model"));
13
+ const whatsapp_message_model_1 = __importDefault(require("../whatsapp/whatsapp-message.model"));
14
+ const whatsapp_contact_profile_model_1 = __importDefault(require("../whatsapp/whatsapp-contact-profile.model"));
15
+ const whatsapp_agent_service_1 = __importDefault(require("../../services/whatsapp-agent.service"));
16
+ const unipile_service_1 = __importDefault(require("../../services/unipile.service"));
17
+ const elevenlabs_service_1 = __importDefault(require("../../services/elevenlabs.service"));
18
+ const operator_request_model_1 = __importDefault(require("../whatsapp/operator-request.model"));
19
+ const stt_usage_model_1 = __importDefault(require("../whatsapp/stt-usage.model"));
20
+ async function unipileWebhookRoutes(fastify) {
21
+ // Accept any content type Unipile might send
22
+ fastify.addContentTypeParser('*', { parseAs: 'string' }, (_request, payload, done) => {
23
+ try {
24
+ done(null, JSON.parse(payload));
25
+ }
26
+ catch {
27
+ done(null, payload);
28
+ }
29
+ });
30
+ fastify.post('/', async (request, reply) => {
31
+ // Validate shared secret — accept if it matches either provider's secret
32
+ const secret = request.headers['x-unipile-secret'];
33
+ if (secret) {
34
+ const validSecrets = [config_1.default.unipile.webhookSecret, config_1.default.baileys.webhookSecret].filter(Boolean);
35
+ if (validSecrets.length > 0 && !validSecrets.includes(secret)) {
36
+ logger_1.default.warn('Invalid webhook secret');
37
+ return reply.status(401).send({ error: 'Invalid secret' });
38
+ }
39
+ }
40
+ const payload = request.body;
41
+ // Respond immediately
42
+ reply.status(200).send({ status: 'received' });
43
+ // Process in background
44
+ processUnipileWebhook(payload).catch(error => {
45
+ logger_1.default.error('Unipile webhook processing failed', {
46
+ error: error.message,
47
+ stack: error.stack,
48
+ });
49
+ });
50
+ });
51
+ }
52
+ function isValidPhoneNumber(phone) {
53
+ return /^\+?\d{10,15}$/.test(phone.replace(/[\s\-()]/g, ''));
54
+ }
55
+ /**
56
+ * Create a new WhatsAppSession for a chat and link it.
57
+ */
58
+ async function createSession(chat, tenantId, gracePeriodSeconds) {
59
+ const deadline = new Date(Date.now() + gracePeriodSeconds * 1000);
60
+ const session = await whatsapp_session_model_1.default.create({
61
+ chat_id: chat._id,
62
+ tenant_id: tenantId,
63
+ status: 'waiting',
64
+ grace_deadline: deadline,
65
+ started_at: new Date(),
66
+ });
67
+ chat.active_session_id = session._id;
68
+ if (chat.is_closed)
69
+ chat.is_closed = false;
70
+ if (chat.deleted_at)
71
+ chat.deleted_at = null;
72
+ await chat.save();
73
+ // Increment profile session count
74
+ if (chat.contact_profile_id) {
75
+ await whatsapp_contact_profile_model_1.default.updateOne({ _id: chat.contact_profile_id }, { $inc: { total_sessions: 1 } });
76
+ }
77
+ return session;
78
+ }
79
+ const UNIPILE_MEDIA_PLACEHOLDER = '-- Unipile cannot display this type of message yet';
80
+ const FRIENDLY_MEDIA_TEXT = 'Bu mesajı görüntülemek için WhatsApp uygulamasını kontrol edin';
81
+ async function processUnipileWebhook(payload) {
82
+ logger_1.default.info('Unipile webhook payload', { payload: JSON.stringify(payload).substring(0, 1000) });
83
+ // Only handle message_received events
84
+ if (payload.event !== 'message_received') {
85
+ logger_1.default.debug('Skipping non-message Unipile event', { event: payload.event });
86
+ return;
87
+ }
88
+ // Only handle WhatsApp
89
+ if (payload.account_type !== 'WHATSAPP') {
90
+ logger_1.default.debug('Skipping non-WhatsApp Unipile event', { type: payload.account_type });
91
+ return;
92
+ }
93
+ // Skip empty messages
94
+ if (!payload.message?.trim()) {
95
+ logger_1.default.debug('Ignoring empty message');
96
+ return;
97
+ }
98
+ // ─── System message detection (two layers) ───────────────
99
+ // Layer 1: Unipile structural fields (most reliable)
100
+ // Layer 2: Text pattern fallback (for event_type=0 / unsupported events)
101
+ // System messages are stored for visibility but never trigger AI or sessions.
102
+ const SYSTEM_MESSAGE_PATTERNS = [
103
+ // EN — Voice/video calls
104
+ /^(incoming|missed|declined|unanswered|group) (voice|video) call/i,
105
+ /^voice call(,| ended| answered)/i,
106
+ /^video call(,| ended| answered)/i,
107
+ // TR — Voice/video calls
108
+ /^(gelen|cevaps[ıi]z|reddedilen|cevaplanmayan|grup) (sesli|g[öo]r[üu]nt[üu]l[üu]) arama/i,
109
+ /^sesli arama (sona erdi|cevapland)/i,
110
+ /^g[öo]r[üu]nt[üu]l[üu] arama (sona erdi|cevapland)/i,
111
+ // EN — Message deletion
112
+ /^this message was deleted/i,
113
+ /^you deleted this message/i,
114
+ // TR — Message deletion
115
+ /^bu mesaj silindi/i,
116
+ /^bu mesaj[ıi] sildiniz/i,
117
+ /taraf[ıi]ndan silindi/i,
118
+ // EN — Encryption & security
119
+ /^messages and calls are end-to-end encrypted/i,
120
+ /security code.*(changed|verified)/i,
121
+ // TR — Encryption & security
122
+ /mesajlar ve aramalar u[çc]tan u[çc]a [şs]ifrelidir/i,
123
+ /g[üu]venlik kodu.* de[gğ]i[şs]ti/i,
124
+ // EN — Disappearing messages
125
+ /turned (on|off) disappearing messages/i,
126
+ /disappearing messages are (on|off)/i,
127
+ // TR — Disappearing messages
128
+ /s[üu]reli mesajlar[ıi] (a[çc]t|kapatt)/i,
129
+ /s[üu]reli mesajlar (a[çc][ıi]k|kapal[ıi])/i,
130
+ // EN — Waiting / pending
131
+ /^waiting for this message/i,
132
+ // TR — Waiting / pending
133
+ /^bu mesaj bekleniyor/i,
134
+ // EN — Live location
135
+ /is sharing live location/i,
136
+ /stopped sharing live location/i,
137
+ // TR — Live location
138
+ /canl[ıi] konum payla[şs]/i,
139
+ // EN — Payment
140
+ /^payment (sent|received)/i,
141
+ /^payment request (sent|received)/i,
142
+ // TR — Payment
143
+ /^[öo]deme (g[öo]nderildi|al[ıi]nd[ıi])/i,
144
+ /^[öo]deme iste[gğ]i (g[öo]nderildi|al[ıi]nd[ıi])/i,
145
+ // EN — Business account
146
+ /this chat is with a business account/i,
147
+ /uses a secure service from Meta/i,
148
+ // TR — Business account
149
+ /bu sohbet bir i[şs]letme hesab[ıi]yla/i,
150
+ // EN — Group management
151
+ /^you were (added|removed)/i,
152
+ /^you('re| are) now an admin/i,
153
+ /^you('re| are) no longer an admin/i,
154
+ /is now an admin$/i,
155
+ /is no longer an admin$/i,
156
+ /joined using this group's invite link/i,
157
+ /pinned a message$/i,
158
+ /unpinned a message$/i,
159
+ /changed the (subject|group description)/i,
160
+ /changed this group's icon/i,
161
+ /only admins can (send messages|edit)/i,
162
+ /all participants can (send messages|edit)/i,
163
+ // TR — Group management
164
+ /art[ıi]k bir y[öo]netici/i,
165
+ /art[ıi]k y[öo]netici de[gğ]il/i,
166
+ /grubun davet ba[gğ]lant[ıi]s[ıi]n[ıi] kullanarak kat[ıi]ld[ıi]/i,
167
+ /bir mesaj[ıi] sabitledi/i,
168
+ /sabitlenen mesaj[ıi] kald[ıi]rd[ıi]/i,
169
+ // EN — View once
170
+ /sent a view once (message|photo|video)/i,
171
+ // TR — View once
172
+ /bir kez g[öo]r[üu]nt[üu]lenen (mesaj|foto|video)/i,
173
+ // EN — Old version
174
+ /using an old(er)? version of WhatsApp/i,
175
+ /need(s)? to update WhatsApp/i,
176
+ ];
177
+ const trimmedMessage = payload.message.trim();
178
+ const isSystemMessage = payload.is_event === true ||
179
+ payload.hidden === true ||
180
+ SYSTEM_MESSAGE_PATTERNS.some(pattern => pattern.test(trimmedMessage));
181
+ if (isSystemMessage) {
182
+ logger_1.default.info('System message detected — AI will be skipped', {
183
+ chat_id: payload.chat_id,
184
+ message: trimmedMessage.substring(0, 80),
185
+ is_event: payload.is_event,
186
+ event_type: payload.event_type,
187
+ hidden: payload.hidden,
188
+ });
189
+ }
190
+ // Detect Unipile media placeholder and replace with friendly text.
191
+ // Baileys now sends descriptive labels like "[Kullanıcı fotoğraf gönderdi]"
192
+ // which pass through to the AI. Only the legacy generic placeholder gets replaced.
193
+ const isLegacyMediaPlaceholder = payload.message === UNIPILE_MEDIA_PLACEHOLDER;
194
+ if (isLegacyMediaPlaceholder) {
195
+ payload.message = FRIENDLY_MEDIA_TEXT;
196
+ }
197
+ // Find tenant by unipile_account_id
198
+ const tenant = await tenant_model_1.default.findOne({
199
+ 'settings.whatsapp_agent.unipile_account_id': payload.account_id,
200
+ enabled_features: 'whatsapp_agent',
201
+ });
202
+ if (!tenant) {
203
+ logger_1.default.warn('No tenant found for Unipile account', { account_id: payload.account_id });
204
+ return;
205
+ }
206
+ // ─── Voice message transcription (STT) ──────────────────────
207
+ if (payload.content_type === 'audioMessage' &&
208
+ !payload.is_sender &&
209
+ tenant.enabled_features.includes('voice_transcription') &&
210
+ payload.message_id) {
211
+ try {
212
+ const provider = unipile_service_1.default.getProviderForTenant(tenant);
213
+ const { audio_base64 } = await unipile_service_1.default.downloadAudio(provider, payload.chat_id, payload.message_id);
214
+ const audioBuffer = Buffer.from(audio_base64, 'base64');
215
+ const { text: transcript, durationSeconds, detectedLanguage } = await elevenlabs_service_1.default.speechToText(audioBuffer);
216
+ if (transcript.trim()) {
217
+ payload.message = transcript;
218
+ payload.content_type = 'transcribedAudio';
219
+ logger_1.default.info('Voice message transcribed', {
220
+ chatId: payload.chat_id,
221
+ transcriptLength: transcript.length,
222
+ durationSeconds,
223
+ detectedLanguage,
224
+ preview: transcript.substring(0, 100),
225
+ });
226
+ // Log STT usage
227
+ stt_usage_model_1.default.create({
228
+ tenant_id: tenant._id,
229
+ chat_id: payload.chat_id,
230
+ message_id: payload.message_id,
231
+ duration_seconds: durationSeconds,
232
+ character_count: transcript.length,
233
+ language: detectedLanguage,
234
+ }).catch(() => { });
235
+ }
236
+ }
237
+ catch (err) {
238
+ logger_1.default.error('Voice message transcription failed, using label fallback', {
239
+ chatId: payload.chat_id,
240
+ error: err.message,
241
+ });
242
+ }
243
+ }
244
+ // ─── Operator / forwarding reply detection ──────────────────
245
+ // Check for REF code from operator_phone or conversation_forwarding_phone
246
+ const operatorSettings = tenant.settings?.whatsapp_agent;
247
+ const knownOperatorPhones = [];
248
+ if (operatorSettings?.operator_workflow_enabled && operatorSettings.operator_phone) {
249
+ knownOperatorPhones.push(operatorSettings.operator_phone.replace(/[^0-9]/g, ''));
250
+ }
251
+ if (tenant.enabled_features.includes('conversation_forwarding') && operatorSettings?.conversation_forwarding_phone) {
252
+ const cfPhone = operatorSettings.conversation_forwarding_phone.replace(/[^0-9]/g, '');
253
+ if (!knownOperatorPhones.includes(cfPhone))
254
+ knownOperatorPhones.push(cfPhone);
255
+ }
256
+ if (knownOperatorPhones.length > 0) {
257
+ // Check for REF code in the message text OR in the quoted (replied-to) message
258
+ const refMatch = payload.message.trim().match(/\b([A-Z]-[A-Z0-9]{4})\b/)
259
+ || (payload.quoted_text?.match(/\b([A-Z]-[A-Z0-9]{4})\b/) ?? null);
260
+ if (refMatch) {
261
+ // Scenario 1: incoming message from a known operator/forwarding phone
262
+ if (!payload.is_sender) {
263
+ const senderPhone = payload.attendees?.[0]?.attendee_specifics?.phone_number
264
+ || payload.attendees?.[0]?.attendee_provider_id || '';
265
+ const normalizedSender = senderPhone.replace(/[^0-9]/g, '');
266
+ if (normalizedSender && knownOperatorPhones.includes(normalizedSender)) {
267
+ logger_1.default.info('Operator message detected (incoming)', { phone: senderPhone, message: payload.message.substring(0, 100) });
268
+ await handleOperatorReply(tenant, payload);
269
+ return;
270
+ }
271
+ }
272
+ // Scenario 2: self-sent message containing a REF code (operator uses same device)
273
+ if (payload.is_sender) {
274
+ // Verify this REF code exists and is pending or recently responded (for follow-up documents)
275
+ const pendingRequest = await operator_request_model_1.default.findOne({
276
+ ref_code: refMatch[1],
277
+ tenant_id: tenant._id,
278
+ status: { $in: ['pending', 'responded'] },
279
+ });
280
+ if (pendingRequest) {
281
+ logger_1.default.info('Operator message detected (self-sent)', { refCode: refMatch[1], message: payload.message.substring(0, 100) });
282
+ await handleOperatorReply(tenant, payload);
283
+ return;
284
+ }
285
+ }
286
+ }
287
+ }
288
+ // Determine contact info from the other party (attendees[0] is always the contact)
289
+ const contact = payload.attendees?.[0];
290
+ const contactName = contact?.attendee_name || 'Bilinmiyor';
291
+ const contactPhone = contact?.attendee_specifics?.phone_number
292
+ || contact?.attendee_provider_id || '';
293
+ // Filter out group chats — only process valid phone numbers
294
+ if (!isValidPhoneNumber(contactPhone)) {
295
+ logger_1.default.debug('Ignoring non-phone chat (group?)', { chat_id: payload.chat_id, contactPhone });
296
+ return;
297
+ }
298
+ // Upsert contact profile
299
+ const profile = await whatsapp_contact_profile_model_1.default.findOneAndUpdate({ tenant_id: tenant._id, phone: contactPhone }, {
300
+ $set: { display_name: contactName, last_seen_at: new Date() },
301
+ $setOnInsert: { first_seen_at: new Date(), notes: '', tags: [], ai_summary: '', metadata: {} },
302
+ }, { upsert: true, new: true });
303
+ const gracePeriodSeconds = tenant.settings?.whatsapp_agent?.grace_period_seconds ?? 180;
304
+ const agentObjId = tenant.settings?.whatsapp_agent?.agent_id;
305
+ const agentDoc = agentObjId ? await agent_model_1.default.findById(agentObjId).lean() : null;
306
+ const agentId = (agentDoc && agentDoc.is_active) ? agentDoc.elevenlabs_agent_id : '';
307
+ const blacklist = tenant.settings?.whatsapp_agent?.blacklisted_numbers || [];
308
+ const isBlacklisted = blacklist.includes(contactPhone);
309
+ const skipAI = isBlacklisted || isSystemMessage || isLegacyMediaPlaceholder;
310
+ // Find or create WhatsAppChat
311
+ let chat = await whatsapp_chat_model_1.default.findOne({ unipile_chat_id: payload.chat_id });
312
+ // If not found by chat_id, try matching by phone (handles provider migration)
313
+ if (!chat && contactPhone) {
314
+ chat = await whatsapp_chat_model_1.default.findOne({ tenant_id: tenant._id, contact_phone: contactPhone, deleted_at: null });
315
+ if (chat) {
316
+ const oldChatId = chat.unipile_chat_id;
317
+ chat.unipile_chat_id = payload.chat_id;
318
+ chat.unipile_account_id = payload.account_id;
319
+ // Clear stale session from previous provider
320
+ if (chat.active_session_id) {
321
+ const staleSession = await whatsapp_session_model_1.default.findById(chat.active_session_id);
322
+ if (staleSession && staleSession.status !== 'resolved') {
323
+ staleSession.status = 'resolved';
324
+ staleSession.resolved_at = new Date();
325
+ await staleSession.save();
326
+ }
327
+ chat.active_session_id = null;
328
+ }
329
+ await chat.save();
330
+ logger_1.default.info('Relinked existing chat after provider migration', {
331
+ chatId: chat._id, oldChatId, newChatId: payload.chat_id,
332
+ });
333
+ }
334
+ }
335
+ // ─── New chat ──────────────────────────────────────────────
336
+ if (!chat) {
337
+ chat = await whatsapp_chat_model_1.default.create({
338
+ tenant_id: tenant._id,
339
+ unipile_chat_id: payload.chat_id,
340
+ unipile_account_id: payload.account_id,
341
+ contact_name: contactName,
342
+ contact_phone: contactPhone,
343
+ contact_profile_id: profile._id,
344
+ is_closed: false,
345
+ last_message_at: new Date(payload.timestamp),
346
+ last_message_preview: payload.message.substring(0, 100),
347
+ message_count: 0,
348
+ });
349
+ // Self-sent first message — our user initiated the chat
350
+ if (payload.is_sender) {
351
+ const session = await whatsapp_session_model_1.default.create({
352
+ chat_id: chat._id,
353
+ tenant_id: tenant._id,
354
+ status: 'active',
355
+ started_at: new Date(),
356
+ taken_over_at: new Date(),
357
+ });
358
+ chat.active_session_id = session._id;
359
+ await chat.save();
360
+ await whatsapp_message_model_1.default.create({
361
+ chat_id: chat._id,
362
+ session_id: session._id,
363
+ tenant_id: tenant._id,
364
+ unipile_message_id: payload.message_id || null,
365
+ sender: 'human',
366
+ sender_name: 'WhatsApp',
367
+ text: payload.message,
368
+ sent_via_unipile: false,
369
+ });
370
+ chat.message_count = 1;
371
+ session.message_count = 1;
372
+ await Promise.all([chat.save(), session.save()]);
373
+ logger_1.default.info('Created new chat from self-sent message (human mode)', {
374
+ chatId: chat._id,
375
+ sessionId: session._id,
376
+ contact: contactName,
377
+ });
378
+ return;
379
+ }
380
+ if (skipAI) {
381
+ await whatsapp_message_model_1.default.create({
382
+ chat_id: chat._id,
383
+ session_id: null,
384
+ tenant_id: tenant._id,
385
+ unipile_message_id: payload.message_id || null,
386
+ sender: 'contact',
387
+ sender_name: contactName,
388
+ text: payload.message,
389
+ media_type: payload.content_type || null,
390
+ ad_context: payload.ad_context || null,
391
+ sent_via_unipile: false,
392
+ });
393
+ chat.message_count = 1;
394
+ await chat.save();
395
+ await whatsapp_contact_profile_model_1.default.updateOne({ _id: profile._id }, { $inc: { total_messages: 1 } });
396
+ logger_1.default.info('Created new chat — AI skipped', {
397
+ chatId: chat._id,
398
+ contact: contactName,
399
+ reason: isSystemMessage ? 'system_message' : 'blacklisted',
400
+ });
401
+ return;
402
+ }
403
+ const session = await createSession(chat, tenant._id.toString(), gracePeriodSeconds);
404
+ await whatsapp_message_model_1.default.create({
405
+ chat_id: chat._id,
406
+ session_id: session._id,
407
+ tenant_id: tenant._id,
408
+ unipile_message_id: payload.message_id || null,
409
+ sender: 'contact',
410
+ sender_name: contactName,
411
+ text: payload.message,
412
+ media_type: payload.content_type || null,
413
+ ad_context: payload.ad_context || null,
414
+ sent_via_unipile: false,
415
+ });
416
+ chat.message_count = 1;
417
+ await chat.save();
418
+ await whatsapp_contact_profile_model_1.default.updateOne({ _id: profile._id }, { $inc: { total_messages: 1 } });
419
+ session.message_count = 1;
420
+ await session.save();
421
+ logger_1.default.info('Created new WhatsApp chat with session', {
422
+ chatId: chat._id,
423
+ sessionId: session._id,
424
+ contact: contactName,
425
+ gracePeriodSeconds,
426
+ });
427
+ // Send fixed first message if enabled
428
+ const fixedMsg = tenant.settings?.whatsapp_agent?.fixed_first_message;
429
+ if (tenant.settings?.whatsapp_agent?.fixed_first_message_enabled && fixedMsg) {
430
+ const provider = unipile_service_1.default.getProviderForTenant(tenant);
431
+ const chatRef = chat;
432
+ unipile_service_1.default.sendMessage(provider, chatRef.unipile_chat_id, fixedMsg).then(async (result) => {
433
+ await whatsapp_message_model_1.default.create({
434
+ chat_id: chatRef._id,
435
+ session_id: session._id,
436
+ tenant_id: tenant._id,
437
+ unipile_message_id: result.message_id || null,
438
+ sender: 'system',
439
+ sender_name: 'AI',
440
+ text: fixedMsg,
441
+ sent_via_unipile: true,
442
+ });
443
+ chatRef.message_count += 1;
444
+ await chatRef.save();
445
+ }).catch(err => {
446
+ logger_1.default.error('Failed to send fixed first message', { chatId: chatRef._id, error: err.message });
447
+ });
448
+ }
449
+ await whatsapp_agent_service_1.default.startSession({
450
+ session,
451
+ gracePeriodSeconds,
452
+ agentId,
453
+ tenantId: tenant._id.toString(),
454
+ });
455
+ return;
456
+ }
457
+ // Backfill profile link on existing chats
458
+ if (!chat.contact_profile_id) {
459
+ chat.contact_profile_id = profile._id;
460
+ await chat.save();
461
+ }
462
+ // Update contact name if changed
463
+ if (chat.contact_name !== contactName && contactName !== 'Bilinmiyor') {
464
+ chat.contact_name = contactName;
465
+ await chat.save();
466
+ }
467
+ // ─── Self-sent messages (is_sender=true) ───────────────────
468
+ if (payload.is_sender) {
469
+ // Look up active session
470
+ let session = chat.active_session_id
471
+ ? await whatsapp_session_model_1.default.findById(chat.active_session_id)
472
+ : null;
473
+ if (session && session.status === 'resolved')
474
+ session = null;
475
+ // If session is waiting, human responded — cancel timer & resolve
476
+ let resolvedSessionId = null;
477
+ if (session && session.status === 'waiting') {
478
+ resolvedSessionId = session._id;
479
+ await whatsapp_agent_service_1.default.resolveSessionAsHuman(session);
480
+ session = null; // session is now resolved
481
+ }
482
+ // Check if we already stored this message (sent via our API)
483
+ // Primary dedup: by unipile_message_id (if Unipile returned one when we sent)
484
+ // Fallback dedup: by text + sent_via_unipile + recent timestamp (covers cases
485
+ // where Unipile didn't return a message_id in the send response)
486
+ if (payload.message_id) {
487
+ const exists = await whatsapp_message_model_1.default.findOne({ unipile_message_id: payload.message_id });
488
+ if (exists) {
489
+ logger_1.default.debug('Self-sent message already in DB (by message_id), skipping', { message_id: payload.message_id });
490
+ return;
491
+ }
492
+ }
493
+ const recentCutoff = new Date(Date.now() - 60_000); // 1 minute window
494
+ const existsByContent = await whatsapp_message_model_1.default.findOne({
495
+ chat_id: chat._id,
496
+ sent_via_unipile: true,
497
+ text: payload.message,
498
+ createdAt: { $gte: recentCutoff },
499
+ });
500
+ if (existsByContent) {
501
+ // Update the stored message with the real unipile_message_id for future dedup
502
+ if (payload.message_id && !existsByContent.unipile_message_id) {
503
+ await whatsapp_message_model_1.default.updateOne({ _id: existsByContent._id }, { unipile_message_id: payload.message_id });
504
+ }
505
+ logger_1.default.debug('Self-sent message already in DB (by content), skipping', { message_id: payload.message_id });
506
+ return;
507
+ }
508
+ // Store the message — link to the session it resolved (if any) or the current active session
509
+ await whatsapp_message_model_1.default.create({
510
+ chat_id: chat._id,
511
+ session_id: resolvedSessionId || session?._id || null,
512
+ tenant_id: tenant._id,
513
+ unipile_message_id: payload.message_id,
514
+ sender: 'human',
515
+ sender_name: 'WhatsApp',
516
+ text: payload.message,
517
+ sent_via_unipile: false,
518
+ });
519
+ chat.last_message_at = new Date(payload.timestamp);
520
+ chat.last_message_preview = payload.message.substring(0, 100);
521
+ chat.message_count += 1;
522
+ await chat.save();
523
+ // If session is active with no takeover (AI mode), switch to human
524
+ if (session && session.status === 'active' && !session.taken_over_by && !session.taken_over_at) {
525
+ session.taken_over_at = new Date();
526
+ await session.save();
527
+ whatsapp_agent_service_1.default.closeConnection(chat.unipile_chat_id);
528
+ logger_1.default.info('Auto-switched to human mode (manual WhatsApp reply detected)', {
529
+ chatId: chat._id,
530
+ sessionId: session._id,
531
+ });
532
+ }
533
+ logger_1.default.info('Stored self-sent message from WhatsApp', { chatId: chat._id });
534
+ return;
535
+ }
536
+ // ─── Incoming contact message (is_sender=false) ────────────
537
+ // Blacklisted numbers & system messages: store but never trigger session or AI
538
+ if (skipAI) {
539
+ await whatsapp_message_model_1.default.create({
540
+ chat_id: chat._id,
541
+ session_id: null,
542
+ tenant_id: tenant._id,
543
+ unipile_message_id: payload.message_id || null,
544
+ sender: 'contact',
545
+ sender_name: contactName,
546
+ text: payload.message,
547
+ media_type: payload.content_type || null,
548
+ ad_context: payload.ad_context || null,
549
+ sent_via_unipile: false,
550
+ });
551
+ chat.last_message_at = new Date(payload.timestamp);
552
+ chat.last_message_preview = payload.message.substring(0, 100);
553
+ chat.message_count += 1;
554
+ if (chat.is_closed)
555
+ chat.is_closed = false;
556
+ if (chat.deleted_at)
557
+ chat.deleted_at = null;
558
+ await chat.save();
559
+ await whatsapp_contact_profile_model_1.default.updateOne({ _id: profile._id }, { $inc: { total_messages: 1 } });
560
+ logger_1.default.info('Message stored — AI skipped', {
561
+ chatId: chat._id,
562
+ reason: isSystemMessage ? 'system_message' : 'blacklisted',
563
+ });
564
+ return;
565
+ }
566
+ // Look up active session
567
+ let session = chat.active_session_id
568
+ ? await whatsapp_session_model_1.default.findById(chat.active_session_id)
569
+ : null;
570
+ if (session && session.status === 'resolved') {
571
+ session = null;
572
+ chat.active_session_id = null;
573
+ await chat.save();
574
+ }
575
+ // If session is waiting, just store the message (timer already running)
576
+ if (session && session.status === 'waiting') {
577
+ await whatsapp_message_model_1.default.create({
578
+ chat_id: chat._id,
579
+ session_id: session._id,
580
+ tenant_id: tenant._id,
581
+ unipile_message_id: payload.message_id || null,
582
+ sender: 'contact',
583
+ sender_name: contactName,
584
+ text: payload.message,
585
+ media_type: payload.content_type || null,
586
+ ad_context: payload.ad_context || null,
587
+ sent_via_unipile: false,
588
+ });
589
+ chat.last_message_at = new Date(payload.timestamp);
590
+ chat.last_message_preview = payload.message.substring(0, 100);
591
+ chat.message_count += 1;
592
+ await chat.save();
593
+ session.message_count += 1;
594
+ await session.save();
595
+ await whatsapp_contact_profile_model_1.default.updateOne({ _id: profile._id }, { $inc: { total_messages: 1 } });
596
+ logger_1.default.info('Message stored during grace period', { chatId: chat._id, sessionId: session._id });
597
+ return;
598
+ }
599
+ // If session is active, forward to agent service
600
+ if (session && session.status === 'active') {
601
+ await whatsapp_agent_service_1.default.handleIncomingMessage({
602
+ tenantId: tenant._id.toString(),
603
+ agentId,
604
+ chat,
605
+ session,
606
+ messageText: payload.message,
607
+ senderName: contactName,
608
+ unipileMessageId: payload.message_id,
609
+ contentType: payload.content_type,
610
+ });
611
+ await whatsapp_contact_profile_model_1.default.updateOne({ _id: profile._id }, { $inc: { total_messages: 1 } });
612
+ return;
613
+ }
614
+ // No active session → create new one (closed or idle chat)
615
+ session = await createSession(chat, tenant._id.toString(), gracePeriodSeconds);
616
+ await whatsapp_message_model_1.default.create({
617
+ chat_id: chat._id,
618
+ session_id: session._id,
619
+ tenant_id: tenant._id,
620
+ unipile_message_id: payload.message_id || null,
621
+ sender: 'contact',
622
+ sender_name: contactName,
623
+ text: payload.message,
624
+ media_type: payload.content_type || null,
625
+ ad_context: payload.ad_context || null,
626
+ sent_via_unipile: false,
627
+ });
628
+ chat.last_message_at = new Date(payload.timestamp);
629
+ chat.last_message_preview = payload.message.substring(0, 100);
630
+ chat.message_count += 1;
631
+ await chat.save();
632
+ await whatsapp_contact_profile_model_1.default.updateOne({ _id: profile._id }, { $inc: { total_messages: 1 } });
633
+ session.message_count = 1;
634
+ await session.save();
635
+ await whatsapp_agent_service_1.default.startSession({
636
+ session,
637
+ gracePeriodSeconds,
638
+ agentId,
639
+ tenantId: tenant._id.toString(),
640
+ });
641
+ logger_1.default.info('Started new session for chat', {
642
+ chatId: chat._id,
643
+ sessionId: session._id,
644
+ });
645
+ }
646
+ // ─── Operator Workflow Helpers ──────────────────────────────
647
+ /**
648
+ * Handle a reply from the operator (doctor) that contains a REF code.
649
+ * Matches the reply to the correct patient session and injects the response.
650
+ */
651
+ async function handleOperatorReply(tenant, payload) {
652
+ const messageText = payload.message.trim();
653
+ const provider = unipile_service_1.default.getProviderForTenant(tenant);
654
+ // Extract REF code from the message text or quoted (replied-to) message
655
+ const refMatch = messageText.match(/\b([A-Z]-[A-Z0-9]{4})\b/)
656
+ || (payload.quoted_text?.match(/\b([A-Z]-[A-Z0-9]{4})\b/) ?? null);
657
+ if (!refMatch) {
658
+ logger_1.default.info('Operator message without REF code — storing as normal', {
659
+ message: messageText.substring(0, 100),
660
+ });
661
+ // No REF code — this is just a normal message from the doctor, ignore it
662
+ // (could also store it as a chat message, but the doctor is messaging the clinic number)
663
+ return;
664
+ }
665
+ const refCode = refMatch[1];
666
+ // Match pending OR recently responded (estetisyen may send document after text reply)
667
+ const request = await operator_request_model_1.default.findOne({
668
+ ref_code: refCode,
669
+ tenant_id: tenant._id,
670
+ status: { $in: ['pending', 'responded'] },
671
+ }).sort({ createdAt: -1 });
672
+ if (!request) {
673
+ logger_1.default.warn('No matching OperatorRequest for REF code', { refCode, tenantId: tenant._id });
674
+ return;
675
+ }
676
+ // Check if operator sent media (document/image) along with the reply
677
+ const hasMedia = !!payload.content_type && payload.content_type !== 'conversation' && payload.content_type !== 'extendedTextMessage';
678
+ logger_1.default.info('Operator reply media check', {
679
+ refCode,
680
+ content_type: payload.content_type,
681
+ message_id: payload.message_id,
682
+ chat_id: payload.chat_id,
683
+ hasMedia,
684
+ is_sender: payload.is_sender,
685
+ });
686
+ const alreadyResponded = request.status === 'responded';
687
+ // Update the operator request
688
+ request.status = 'responded';
689
+ if (!alreadyResponded) {
690
+ request.operator_response = messageText;
691
+ request.responded_at = new Date();
692
+ }
693
+ if (hasMedia) {
694
+ request.response_media_type = payload.content_type;
695
+ request.response_message_id = payload.message_id;
696
+ }
697
+ await request.save();
698
+ // If operator sent media, forward it directly to the patient
699
+ let mediaForwarded = false;
700
+ if (hasMedia) {
701
+ const chat = await whatsapp_chat_model_1.default.findById(request.chat_id).lean();
702
+ if (chat) {
703
+ try {
704
+ // Small delay to ensure Baileys worker has cached the media
705
+ await new Promise(resolve => setTimeout(resolve, 1000));
706
+ await unipile_service_1.default.forwardMedia(provider, payload.chat_id, payload.message_id, chat.contact_phone, '');
707
+ mediaForwarded = true;
708
+ logger_1.default.info('Forwarded operator document to patient', {
709
+ refCode, mediaType: payload.content_type, patientPhone: chat.contact_phone,
710
+ });
711
+ }
712
+ catch (err) {
713
+ logger_1.default.error('Failed to forward operator document to patient', {
714
+ refCode, error: err.message,
715
+ });
716
+ }
717
+ }
718
+ }
719
+ if (!alreadyResponded) {
720
+ // First reply: inject the operator's text response into the patient's AI session
721
+ await whatsapp_agent_service_1.default.injectOperatorResponse({
722
+ tenantId: tenant._id.toString(),
723
+ sessionId: request.session_id.toString(),
724
+ chatId: request.chat_id.toString(),
725
+ operatorResponse: messageText,
726
+ refCode,
727
+ hasDocument: mediaForwarded,
728
+ });
729
+ // Send confirmation to the estetisyen
730
+ try {
731
+ const accountId = tenant.settings.whatsapp_agent.unipile_account_id;
732
+ if (accountId) {
733
+ const operatorPhone = tenant.settings.whatsapp_agent.operator_phone;
734
+ const operatorJid = operatorPhone.replace(/[^0-9]/g, '') + '@s.whatsapp.net';
735
+ const operatorChatId = `${accountId}:${operatorJid}`;
736
+ const confirmMsg = mediaForwarded
737
+ ? `✓ Yanıt ve belge hastaya iletildi. Ref: ${refCode}`
738
+ : `✓ Yanıt alındı. Ref: ${refCode} — Hastaya iletilecek.`;
739
+ await unipile_service_1.default.sendMessage(provider, operatorChatId, confirmMsg);
740
+ }
741
+ }
742
+ catch (err) {
743
+ logger_1.default.error('Failed to send confirmation to operator', { refCode, error: err.message });
744
+ }
745
+ }
746
+ else if (mediaForwarded) {
747
+ // Follow-up: estetisyen sent document after initial text reply
748
+ // Notify AI about the document so it can tell the patient
749
+ await whatsapp_agent_service_1.default.injectOperatorResponse({
750
+ tenantId: tenant._id.toString(),
751
+ sessionId: request.session_id.toString(),
752
+ chatId: request.chat_id.toString(),
753
+ operatorResponse: '',
754
+ refCode,
755
+ hasDocument: true,
756
+ });
757
+ // Send confirmation
758
+ try {
759
+ const accountId = tenant.settings.whatsapp_agent.unipile_account_id;
760
+ if (accountId) {
761
+ const operatorPhone = tenant.settings.whatsapp_agent.operator_phone;
762
+ const operatorJid = operatorPhone.replace(/[^0-9]/g, '') + '@s.whatsapp.net';
763
+ const operatorChatId = `${accountId}:${operatorJid}`;
764
+ await unipile_service_1.default.sendMessage(provider, operatorChatId, `✓ Belge hastaya iletildi. Ref: ${refCode}`);
765
+ }
766
+ }
767
+ catch (err) {
768
+ logger_1.default.error('Failed to send confirmation to operator', { refCode, error: err.message });
769
+ }
770
+ }
771
+ logger_1.default.info('Operator reply processed', {
772
+ refCode,
773
+ sessionId: request.session_id,
774
+ hasMedia,
775
+ mediaForwarded,
776
+ alreadyResponded,
777
+ response: messageText.substring(0, 100),
778
+ });
779
+ }
780
+ //# sourceMappingURL=unipile.routes.js.map