django-unicom 25.3.45.dev1__tar.gz → 25.3.45.dev3__tar.gz

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 (304) hide show
  1. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/PKG-INFO +3 -3
  2. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/README.md +2 -2
  3. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/django_unicom.egg-info/PKG-INFO +3 -3
  4. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/_version.py +2 -2
  5. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/send_email_message.py +17 -2
  6. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/template_renderer.py +59 -0
  7. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/.cursor/rules/match_codebase_style.mdc +0 -0
  8. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/.cursor/rules/preserve_comments_and_irrelevant_code.mdc +0 -0
  9. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/.cursor/rules/require_context_before_changes.mdc +0 -0
  10. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/Dockerfile +0 -0
  11. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/MANIFEST.in +0 -0
  12. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/Makefile +0 -0
  13. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/conftest.py +0 -0
  14. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/data/definitions/bots/__init__.py +0 -0
  15. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/data/definitions/tools/__init__.py +0 -0
  16. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/django_unicom.egg-info/SOURCES.txt +0 -0
  17. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/django_unicom.egg-info/dependency_links.txt +0 -0
  18. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/django_unicom.egg-info/requires.txt +0 -0
  19. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/django_unicom.egg-info/top_level.txt +0 -0
  20. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/docker-compose.yaml +0 -0
  21. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/entrypoint.sh +0 -0
  22. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/internal-channel-modernization.md +0 -0
  23. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/manage.py +0 -0
  24. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/pyproject.toml +0 -0
  25. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/pytest.ini +0 -0
  26. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/requirements.txt +0 -0
  27. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/scripts/reacher_validate.sh +0 -0
  28. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/setup.cfg +0 -0
  29. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/setup.py +0 -0
  30. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/__init__.py +0 -0
  31. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/test_cross_platform_buttons.py +0 -0
  32. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/test_email_authentication.py +0 -0
  33. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/test_email_live.py +0 -0
  34. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/test_email_request_processing.py +0 -0
  35. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/test_telegram_live.py +0 -0
  36. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/test_webchat.py +0 -0
  37. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/test_webchat_branching.py +0 -0
  38. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/tests/utils.py +0 -0
  39. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/todo.md +0 -0
  40. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/__init__.py +0 -0
  41. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/admin.py +0 -0
  42. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/apps.py +0 -0
  43. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/migrations/0001_initial.py +0 -0
  44. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/migrations/0002_tool_bot_tools.py +0 -0
  45. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/migrations/0003_encryptedcredential.py +0 -0
  46. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/migrations/0004_credentialfielddefinition_and_more.py +0 -0
  47. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/migrations/0005_alter_credentialfielddefinition_key.py +0 -0
  48. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/migrations/__init__.py +0 -0
  49. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/models.py +0 -0
  50. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/services/__init__.py +0 -0
  51. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/services/credentials.py +0 -0
  52. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/services/llm_handler.py +0 -0
  53. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/services/tool_exceptions.py +0 -0
  54. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/services/tool_result_handler.py +0 -0
  55. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/templatetags/__init__.py +0 -0
  56. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/templatetags/unibot_extras.py +0 -0
  57. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/tests.py +0 -0
  58. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/urls.py +0 -0
  59. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unibot/views.py +0 -0
  60. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/__init__.py +0 -0
  61. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/__init__.py +0 -0
  62. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/account_admin.py +0 -0
  63. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/channel_admin.py +0 -0
  64. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/chat_admin.py +0 -0
  65. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/draft_message_admin.py +0 -0
  66. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/email_inline_image_admin.py +0 -0
  67. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/filters.py +0 -0
  68. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/member_admin.py +0 -0
  69. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/message_admin.py +0 -0
  70. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/message_template_admin.py +0 -0
  71. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/admin/request_admin.py +0 -0
  72. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/apps.py +0 -0
  73. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/consumers/__init__.py +0 -0
  74. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/consumers/webchat_consumer.py +0 -0
  75. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/management/__init__.py +0 -0
  76. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/management/commands/__init__.py +0 -0
  77. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/management/commands/mail_inbox_listen.py +0 -0
  78. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/management/commands/run_as_llm_chat.py +0 -0
  79. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/management/commands/send_scheduled_messages.py +0 -0
  80. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/management/commands/start_imap_listeners.py +0 -0
  81. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0001_initial.py +0 -0
  82. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0002_emailinlineimage.py +0 -0
  83. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0003_alter_emailinlineimage_email_message.py +0 -0
  84. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0004_messagetemplateinlineimage.py +0 -0
  85. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0005_emailinlineimage_hash_and_more.py +0 -0
  86. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0006_channel_created_at_channel_created_by_and_more.py +0 -0
  87. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0007_message_imap_uid.py +0 -0
  88. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0008_add_all_members_group.py +0 -0
  89. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0009_add_tool_call_types.py +0 -0
  90. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0010_request_initial_request_request_llm_calls_count_and_more.py +0 -0
  91. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0011_toolcall_add_message_reference.py +0 -0
  92. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0012_message_response_to_tool_call_and_more.py +0 -0
  93. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0013_callbackexecution.py +0 -0
  94. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0014_rename_authorized_user_to_intended_account.py +0 -0
  95. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0015_simplify_callback_execution.py +0 -0
  96. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0016_callbackexecution_tool_call.py +0 -0
  97. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0017_add_chat_metadata.py +0 -0
  98. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0018_message_bounce_details_message_bounce_reason_and_more.py +0 -0
  99. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0019_draftmessage_skip_reacher_validation.py +0 -0
  100. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0020_message_provider_message_id.py +0 -0
  101. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0021_message_open_count.py +0 -0
  102. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0022_toolcall_result_status.py +0 -0
  103. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/0023_toolcall_progress_updates_for_user.py +0 -0
  104. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/migrations/__init__.py +0 -0
  105. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/__init__.py +0 -0
  106. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/account.py +0 -0
  107. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/account_chat.py +0 -0
  108. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/callback_execution.py +0 -0
  109. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/channel.py +0 -0
  110. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/chat.py +0 -0
  111. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/constants.py +0 -0
  112. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/draft_message.py +0 -0
  113. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/fields.py +0 -0
  114. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/member.py +0 -0
  115. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/member_group.py +0 -0
  116. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/message.py +0 -0
  117. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/message_template.py +0 -0
  118. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/request.py +0 -0
  119. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/request_category.py +0 -0
  120. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/tool_call.py +0 -0
  121. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/models/update.py +0 -0
  122. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/__init__.py +0 -0
  123. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/chat_summary.py +0 -0
  124. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/crossplatform/__init__.py +0 -0
  125. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/crossplatform/reply_to_message.py +0 -0
  126. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/crossplatform/scheduler.py +0 -0
  127. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/crossplatform/send_message.py +0 -0
  128. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/decode_base64_image.py +0 -0
  129. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/IMAP_thread_manager.py +0 -0
  130. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/__init__.py +0 -0
  131. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/email_tracking.py +0 -0
  132. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/listen_to_IMAP.py +0 -0
  133. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/quote_filter.py +0 -0
  134. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/replace_cid_images_with_base64.py +0 -0
  135. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/save_email_message.py +0 -0
  136. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/email/validate_email_config.py +0 -0
  137. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/get_public_origin.py +0 -0
  138. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/html_inline_images.py +0 -0
  139. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/internal/__init__.py +0 -0
  140. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/internal/generate_text_message_data.py +0 -0
  141. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/internal/save_internal_message.py +0 -0
  142. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/internal/send_internal_message.py +0 -0
  143. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/llm/README.md +0 -0
  144. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/llm/__init__.py +0 -0
  145. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/llm/tool_calls.py +0 -0
  146. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/__init__.py +0 -0
  147. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/answer_callback_query.py +0 -0
  148. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/create_inline_keyboard.py +0 -0
  149. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/download_file.py +0 -0
  150. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/edit_telegram_message.py +0 -0
  151. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/escape_markdown.py +0 -0
  152. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/get_file_path.py +0 -0
  153. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/handle_telegram_callback.py +0 -0
  154. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/save_telegram_message.py +0 -0
  155. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/send_telegram_message.py +0 -0
  156. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/set_telegram_webhook.py +0 -0
  157. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/start_typing_in_telegram.py +0 -0
  158. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/telegram/stop_typing_in_telegram.py +0 -0
  159. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/webchat/__init__.py +0 -0
  160. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/webchat/generate_chat_title.py +0 -0
  161. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/webchat/get_or_create_account.py +0 -0
  162. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/webchat/migrate_guest_to_user.py +0 -0
  163. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/webchat/save_webchat_message.py +0 -0
  164. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/webchat/send_webchat_message.py +0 -0
  165. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/whatsapp/__init__.py +0 -0
  166. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/whatsapp/get_template.py +0 -0
  167. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/whatsapp/save_whatsapp_message.py +0 -0
  168. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/whatsapp/save_whatsapp_message_status.py +0 -0
  169. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/services/whatsapp/send_whatsapp_message.py +0 -0
  170. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/signals.py +0 -0
  171. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/css/bootstrap_scoped.css +0 -0
  172. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/css/draft_message_mobile.css +0 -0
  173. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/js/channel_config.js +0 -0
  174. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/js/tinymce_ai_template.js +0 -0
  175. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/js/tinymce_init.js +0 -0
  176. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/components/chat-list.js +0 -0
  177. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/components/media-preview.js +0 -0
  178. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/components/message-input.js +0 -0
  179. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/components/message-item.js +0 -0
  180. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/components/message-list.js +0 -0
  181. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/components/voice-recorder.js +0 -0
  182. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/utils/api-client.js +0 -0
  183. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/utils/datetime-formatter.js +0 -0
  184. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/utils/font-awesome-loader.js +0 -0
  185. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/utils/realtime-client.js +0 -0
  186. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/webchat-component.js +0 -0
  187. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/webchat-styles.js +0 -0
  188. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/static/unicom/webchat/webchat-with-sidebar.js +0 -0
  189. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/chat/change_list.html +0 -0
  190. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/chat/compose.html +0 -0
  191. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/chat_history.html +0 -0
  192. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/forms/email_message_form.html +0 -0
  193. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/forms/text_message_form.html +0 -0
  194. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/includes/ai_template_modal.html +0 -0
  195. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/includes/loading_indicators.html +0 -0
  196. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/includes/message_actions_menu.html +0 -0
  197. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/admin/unicom/messagetemplate/change_form.html +0 -0
  198. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/code_templates/category_processor.py +0 -0
  199. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templates/unicom/webchat_demo.html +0 -0
  200. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templatetags/__init__.py +0 -0
  201. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/templatetags/unicom_tags.py +0 -0
  202. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/tests/test_email_tracking_unsubscribe.py +0 -0
  203. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/tests/test_template_renderer_context.py +0 -0
  204. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/tests/test_template_renderer_unicom.py +0 -0
  205. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/urls.py +0 -0
  206. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/__init__.py +0 -0
  207. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/chat_history_view.py +0 -0
  208. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/compose_view.py +0 -0
  209. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/email_tracking.py +0 -0
  210. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/inline_image.py +0 -0
  211. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/message_template.py +0 -0
  212. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/telegram_webhook.py +0 -0
  213. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/webchat_demo_view.py +0 -0
  214. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/webchat_views.py +0 -0
  215. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom/views/whatsapp_webhook.py +0 -0
  216. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/__init__.py +0 -0
  217. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/apps.py +0 -0
  218. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/asgi.py +0 -0
  219. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/callback_handlers.py +0 -0
  220. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/__init__.py +0 -0
  221. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/bots/__init__.py +0 -0
  222. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/bots/assistant_bot.py +0 -0
  223. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/tools/__init__.py +0 -0
  224. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/tools/cross_platform_buttons.py +0 -0
  225. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/tools/get_system_info.py +0 -0
  226. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/tools/interactive_menu.py +0 -0
  227. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/tools/interval_alarm.py +0 -0
  228. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/tools/ip_lookup.py +0 -0
  229. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/definitions/tools/simple_timer.py +0 -0
  230. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/settings.py +0 -0
  231. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/test_button_handlers.py +0 -0
  232. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/urls.py +0 -0
  233. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicom_project/wsgi.py +0 -0
  234. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/__init__.py +0 -0
  235. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/admin.py +0 -0
  236. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/apps.py +0 -0
  237. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/forms.py +0 -0
  238. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/management/__init__.py +0 -0
  239. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/management/commands/__init__.py +0 -0
  240. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/management/commands/refresh_contact_cache.py +0 -0
  241. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/management/commands/send_scheduled_communications.py +0 -0
  242. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/management/commands/sync_getprospect_saved_contacts.py +0 -0
  243. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0001_initial.py +0 -0
  244. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0002_alter_communicationmessage_unique_together_and_more.py +0 -0
  245. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0003_remove_template_add_content.py +0 -0
  246. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0004_alter_contact_email.py +0 -0
  247. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0005_contact_user_alter_communication_status_summary.py +0 -0
  248. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0006_contact_email_bounce_type_contact_email_bounced_and_more.py +0 -0
  249. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0007_auto_enroll_and_status.py +0 -0
  250. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0008_rename_unicrm_msg_status_sched_idx_unicrm_comm_status_2f3c8e_idx_and_more.py +0 -0
  251. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0009_remove_unused_draft_field.py +0 -0
  252. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0010_communication_evergreen_refreshed_at.py +0 -0
  253. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0011_add_unique_constraints_to_company.py +0 -0
  254. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0012_company_attributes_company_industry.py +0 -0
  255. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0013_contact_insight_id.py +0 -0
  256. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0014_contact_gp_fields.py +0 -0
  257. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0015_contact_history_unsubscribe_all.py +0 -0
  258. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0016_add_unsubscribe_template_variable.py +0 -0
  259. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0017_unsubscribe_link_html_variable.py +0 -0
  260. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0018_communication_mailing_list.py +0 -0
  261. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0019_communication_follow_up_for_segment_for_followup.py +0 -0
  262. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0020_communication_skip_antispam_guards.py +0 -0
  263. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/0021_fix_unsubscribe_counts.py +0 -0
  264. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/migrations/__init__.py +0 -0
  265. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/models.py +0 -0
  266. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/seed_data.py +0 -0
  267. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/__init__.py +0 -0
  268. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/audience.py +0 -0
  269. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/audience_preview.py +0 -0
  270. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/communication_dispatcher.py +0 -0
  271. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/communication_enrollment.py +0 -0
  272. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/communication_runner.py +0 -0
  273. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/communication_scheduler.py +0 -0
  274. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/contact_interaction_cache.py +0 -0
  275. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/disk_usage_monitor.py +0 -0
  276. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/disk_usage_monitor_runner.py +0 -0
  277. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/getprospect_email_poller.py +0 -0
  278. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/lead_search.py +0 -0
  279. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/red_alerts.py +0 -0
  280. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/save_new_leads_callbacks.py +0 -0
  281. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/template_renderer.py +0 -0
  282. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/unsubscribe_links.py +0 -0
  283. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/services/user_contact_sync.py +0 -0
  284. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/signals.py +0 -0
  285. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/templatetags/__init__.py +0 -0
  286. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/templatetags/unicrm_extras.py +0 -0
  287. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/__init__.py +0 -0
  288. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_communication_compose_view.py +0 -0
  289. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_communication_dispatcher.py +0 -0
  290. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_communication_scheduler.py +0 -0
  291. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_contact_interactions.py +0 -0
  292. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_disk_usage_monitor.py +0 -0
  293. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_disk_usage_monitor_runner.py +0 -0
  294. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_red_alerts.py +0 -0
  295. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_retry_delivery_api.py +0 -0
  296. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_template_renderer.py +0 -0
  297. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_unsubscribe_view.py +0 -0
  298. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/tests/test_user_contact_sync.py +0 -0
  299. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/urls.py +0 -0
  300. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/views.py +0 -0
  301. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/views_api.py +0 -0
  302. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/views_communication.py +0 -0
  303. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/views_lead_search.py +0 -0
  304. {django_unicom-25.3.45.dev1 → django_unicom-25.3.45.dev3}/unicrm/views_public.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-unicom
3
- Version: 25.3.45.dev1
3
+ Version: 25.3.45.dev3
4
4
  Summary: Unified communication layer for Django (Telegram, WhatsApp, Email)
5
5
  Home-page: https://github.com/meena-erian/unicom
6
6
  Author: Meena (Menas) Erian
@@ -630,12 +630,12 @@ channel.send_message({
630
630
  'to': ['a@example.com'],
631
631
  'subject': 'Hello',
632
632
  'html': '<p>Hello {{ variables.name }}</p>',
633
- 'render_template': True, # enable rendering
633
+ 'render_template': True, # enable rendering (admin email compose & scheduled drafts set this for you)
634
634
  'render_variables': {'name': 'Alice'}, # merged into variables.*
635
635
  # Optionally pass a custom render_context if you want to add more fields
636
636
  })
637
637
  ```
638
- If you omit `render_template`/context/variables, the HTML is sent as-is.
638
+ If you omit `render_template`/context/variables, the HTML is sent as-is. The Django admin email composer and scheduled drafts already enable rendering by default; you only need to pass the flag when sending programmatically.
639
639
 
640
640
  ### Draft Messages & Scheduling
641
641
 
@@ -586,12 +586,12 @@ channel.send_message({
586
586
  'to': ['a@example.com'],
587
587
  'subject': 'Hello',
588
588
  'html': '<p>Hello {{ variables.name }}</p>',
589
- 'render_template': True, # enable rendering
589
+ 'render_template': True, # enable rendering (admin email compose & scheduled drafts set this for you)
590
590
  'render_variables': {'name': 'Alice'}, # merged into variables.*
591
591
  # Optionally pass a custom render_context if you want to add more fields
592
592
  })
593
593
  ```
594
- If you omit `render_template`/context/variables, the HTML is sent as-is.
594
+ If you omit `render_template`/context/variables, the HTML is sent as-is. The Django admin email composer and scheduled drafts already enable rendering by default; you only need to pass the flag when sending programmatically.
595
595
 
596
596
  ### Draft Messages & Scheduling
597
597
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-unicom
3
- Version: 25.3.45.dev1
3
+ Version: 25.3.45.dev3
4
4
  Summary: Unified communication layer for Django (Telegram, WhatsApp, Email)
5
5
  Home-page: https://github.com/meena-erian/unicom
6
6
  Author: Meena (Menas) Erian
@@ -630,12 +630,12 @@ channel.send_message({
630
630
  'to': ['a@example.com'],
631
631
  'subject': 'Hello',
632
632
  'html': '<p>Hello {{ variables.name }}</p>',
633
- 'render_template': True, # enable rendering
633
+ 'render_template': True, # enable rendering (admin email compose & scheduled drafts set this for you)
634
634
  'render_variables': {'name': 'Alice'}, # merged into variables.*
635
635
  # Optionally pass a custom render_context if you want to add more fields
636
636
  })
637
637
  ```
638
- If you omit `render_template`/context/variables, the HTML is sent as-is.
638
+ If you omit `render_template`/context/variables, the HTML is sent as-is. The Django admin email composer and scheduled drafts already enable rendering by default; you only need to pass the flag when sending programmatically.
639
639
 
640
640
  ### Draft Messages & Scheduling
641
641
 
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '25.3.45.dev1'
32
- __version_tuple__ = version_tuple = (25, 3, 45, 'dev1')
31
+ __version__ = version = '25.3.45.dev3'
32
+ __version_tuple__ = version_tuple = (25, 3, 45, 'dev3')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -20,6 +20,8 @@ from unicom.services.html_inline_images import html_shortlinks_to_base64_images,
20
20
  from unicom.services.template_renderer import (
21
21
  render_template as render_unicom_template,
22
22
  build_unicom_message_context,
23
+ extract_variable_keys,
24
+ compute_crm_variables,
23
25
  )
24
26
 
25
27
  logger = logging.getLogger(__name__)
@@ -281,9 +283,19 @@ def send_email_message(channel: Channel, params: dict, user: User=None):
281
283
 
282
284
  # Optionally render templates for non-CRM use-cases when explicitly requested.
283
285
  render_context = params.get('render_context')
284
- render_variables = params.get('render_variables')
286
+ render_variables = params.get('render_variables') or {}
285
287
  render_requested = params.get('render_template') or render_context or render_variables
286
288
  if render_requested and html_content:
289
+ crm_variables: dict[str, object] = {}
290
+ requested_keys = extract_variable_keys(html_content)
291
+ if requested_keys:
292
+ # Use first To address (if any) to resolve contact for CRM variables.
293
+ primary_email = None
294
+ to_addrs_for_crm = params.get('to') or to_addrs
295
+ if to_addrs_for_crm:
296
+ primary_email = (to_addrs_for_crm[0] or '').strip()
297
+ crm_variables = compute_crm_variables(requested_keys, primary_email)
298
+
287
299
  base_context = render_context or build_unicom_message_context(
288
300
  params=params,
289
301
  channel={
@@ -297,10 +309,13 @@ def send_email_message(channel: Channel, params: dict, user: User=None):
297
309
  'email': getattr(user, 'email', None),
298
310
  } if user else {},
299
311
  )
312
+ merged_variables = {}
313
+ merged_variables.update(crm_variables)
314
+ merged_variables.update(render_variables)
300
315
  render_result = render_unicom_template(
301
316
  html_content,
302
317
  base_context=base_context,
303
- variables=render_variables or {},
318
+ variables=merged_variables,
304
319
  )
305
320
  if render_result.errors:
306
321
  logger.warning("Template rendering errors: %s", "; ".join(render_result.errors))
@@ -7,6 +7,7 @@ import re
7
7
  from urllib.parse import unquote
8
8
 
9
9
  from django.utils import timezone
10
+ from django.db import IntegrityError
10
11
  from jinja2 import StrictUndefined, TemplateError
11
12
  from jinja2.sandbox import SandboxedEnvironment
12
13
 
@@ -138,3 +139,61 @@ def build_unicom_message_context(
138
139
  if extra:
139
140
  ctx.update(extra)
140
141
  return ctx
142
+
143
+
144
+ _VARIABLE_PLACEHOLDER_RE = re.compile(r"\{\{\s*variables\.([^\s\}]+)\s*\}\}")
145
+
146
+
147
+ def extract_variable_keys(template_html: str | None) -> set[str]:
148
+ """
149
+ Return the set of variable keys referenced as {{ variables.* }} in the template.
150
+ """
151
+ if not template_html:
152
+ return set()
153
+ unprotected = unprotect_tinymce_markup(template_html)
154
+ return {m.group(1) for m in _VARIABLE_PLACEHOLDER_RE.finditer(unprotected)}
155
+
156
+
157
+ def _load_crm_models():
158
+ """
159
+ Dynamically load CRM models if unicrm is installed; otherwise return (None, None).
160
+ """
161
+ try:
162
+ from django.apps import apps
163
+ Contact = apps.get_model('unicrm', 'Contact')
164
+ TemplateVariable = apps.get_model('unicrm', 'TemplateVariable')
165
+ if Contact is None or TemplateVariable is None:
166
+ return None, None
167
+ return Contact, TemplateVariable
168
+ except Exception:
169
+ return None, None
170
+
171
+
172
+ def compute_crm_variables(keys: set[str], contact_email: str | None) -> Dict[str, Any]:
173
+ """
174
+ Best-effort evaluation of CRM TemplateVariables for a contact resolved by email.
175
+ Safe to call when unicrm is not installed (returns {}).
176
+ """
177
+ if not keys or not contact_email:
178
+ return {}
179
+ Contact, TemplateVariable = _load_crm_models()
180
+ if Contact is None or TemplateVariable is None:
181
+ return {}
182
+ contact = Contact.objects.filter(email__iexact=contact_email).first()
183
+ if not contact:
184
+ try:
185
+ contact, _ = Contact.objects.get_or_create(
186
+ email=contact_email,
187
+ defaults={'email': contact_email},
188
+ )
189
+ except IntegrityError:
190
+ contact = Contact.objects.filter(email__iexact=contact_email).first()
191
+ if not contact:
192
+ return {}
193
+ results: Dict[str, Any] = {}
194
+ for variable in TemplateVariable.objects.filter(is_active=True, key__in=keys):
195
+ try:
196
+ results[variable.key] = variable.get_callable()(contact)
197
+ except Exception as exc: # pragma: no cover - defensive
198
+ results[variable.key] = f"<error: {exc}>"
199
+ return results