From cd2c58726f2dc094dc06b15bc42ba37cbb04887b Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Thu, 25 Sep 2025 18:19:09 +0530 Subject: [PATCH 1/4] fix: Ensure message is always present in `conversation_created` webhook for WhatsApp attachment messages (#12507) Fixes https://github.com/chatwoot/chatwoot/issues/11753 and https://github.com/chatwoot/chatwoot/issues/12442 **Problem** When a WhatsApp conversation started with a media message, the conversation created webhook would sometimes fire before the message and its relationships were fully committed to the database. This resulted in the message being missing from the webhook payload, breaking external automations that rely on this field. **Solution** Added `ActiveRecord::Base.transaction` wrapper around the core message processing operations in `Whatsapp::IncomingMessageBaseService` to ensure atomic execution: - `set_conversation` (creates conversation) - `create_messages` (creates message with account_id) - `clear_message_source_id_from_redis` (cleanup) Now the webhook only triggers after all related data is fully persisted, guaranteeing message availability. --- app/services/whatsapp/incoming_message_base_service.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/services/whatsapp/incoming_message_base_service.rb b/app/services/whatsapp/incoming_message_base_service.rb index 0aed8dba0..315182fcd 100644 --- a/app/services/whatsapp/incoming_message_base_service.rb +++ b/app/services/whatsapp/incoming_message_base_service.rb @@ -32,9 +32,11 @@ class Whatsapp::IncomingMessageBaseService set_contact return unless @contact - set_conversation - create_messages - clear_message_source_id_from_redis + ActiveRecord::Base.transaction do + set_conversation + create_messages + clear_message_source_id_from_redis + end end def process_statuses From 59f7c8aa555a47457a6af68563f779783dc8b5d8 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:29:55 +0200 Subject: [PATCH 2/4] perf: Contact optimisation fixes (#12016) - Avoids the duplicate count queries for contact end point Co-authored-by: Muhsin Keloth --- .../api/v1/accounts/contacts_controller.rb | 19 ++++++++++--------- .../v1/accounts/contacts/active.json.jbuilder | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb index 039786905..e6270c807 100644 --- a/app/controllers/api/v1/accounts/contacts_controller.rb +++ b/app/controllers/api/v1/accounts/contacts_controller.rb @@ -17,8 +17,8 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController before_action :set_include_contact_inboxes, only: [:index, :active, :search, :filter, :show, :update] def index - @contacts_count = resolved_contacts.count @contacts = fetch_contacts(resolved_contacts) + @contacts_count = @contacts.total_count end def search @@ -29,8 +29,8 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController OR contacts.additional_attributes->>\'company_name\' ILIKE :search', search: "%#{params[:q].strip}%" ) - @contacts_count = contacts.count @contacts = fetch_contacts(contacts) + @contacts_count = @contacts.total_count end def import @@ -55,8 +55,8 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController def active contacts = Current.account.contacts.where(id: ::OnlineStatusTracker .get_available_contact_ids(Current.account.id)) - @contacts_count = contacts.count @contacts = fetch_contacts(contacts) + @contacts_count = @contacts.total_count end def show; end @@ -133,13 +133,14 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController end def fetch_contacts(contacts) - contacts_with_avatar = filtrate(contacts) - .includes([{ avatar_attachment: [:blob] }]) - .page(@current_page).per(RESULTS_PER_PAGE) + # Build includes hash to avoid separate query when contact_inboxes are needed + includes_hash = { avatar_attachment: [:blob] } + includes_hash[:contact_inboxes] = { inbox: :channel } if @include_contact_inboxes - return contacts_with_avatar.includes([{ contact_inboxes: [:inbox] }]) if @include_contact_inboxes - - contacts_with_avatar + filtrate(contacts) + .includes(includes_hash) + .page(@current_page) + .per(RESULTS_PER_PAGE) end def build_contact_inbox diff --git a/app/views/api/v1/accounts/contacts/active.json.jbuilder b/app/views/api/v1/accounts/contacts/active.json.jbuilder index cde9e5445..577dff4be 100644 --- a/app/views/api/v1/accounts/contacts/active.json.jbuilder +++ b/app/views/api/v1/accounts/contacts/active.json.jbuilder @@ -5,6 +5,6 @@ end json.payload do json.array! @contacts do |contact| - json.partial! 'api/v1/models/contact', formats: [:json], resource: contact, with_contact_inboxes: true + json.partial! 'api/v1/models/contact', formats: [:json], resource: contact, with_contact_inboxes: @include_contact_inboxes end end From fcb91ab88ac603705ecb1a81d9dbfc73325ada2b Mon Sep 17 00:00:00 2001 From: Phuong Nguyen Date: Thu, 25 Sep 2025 21:06:38 +0700 Subject: [PATCH 3/4] fix: Auto resolution flaky spec (#11964) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test was failing because Current.contact was not being cleared when testing system auto-resolution. Added Current.contact = nil to ensure the system auto-resolution message is triggered instead of contact resolution. 🤖 Generated with [Claude Code](https://claude.ai/code) # Pull Request Template ## Description Please include a summary of the change and issue(s) fixed. Also, mention relevant motivation, context, and any dependencies that this change requires. Fixes # (issue) ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Claude Co-authored-by: Muhsin Keloth --- spec/models/conversation_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb index 007ce987f..72962ba54 100644 --- a/spec/models/conversation_spec.rb +++ b/spec/models/conversation_spec.rb @@ -215,7 +215,7 @@ RSpec.describe Conversation do it 'adds a message for system auto resolution if marked resolved by system' do account.update(auto_resolve_after: 40 * 24 * 60) conversation2 = create(:conversation, status: 'open', account: account, assignee: old_assignee) - Current.user = nil + Current.reset message_data = if account.auto_resolve_after >= 1440 && account.auto_resolve_after % 1440 == 0 { key: 'auto_resolved_days', count: account.auto_resolve_after / 1440 } From b00261d7c26de5dd365e900f3580d6718a8d9cc7 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:14:36 +0530 Subject: [PATCH 4/4] feat: Add password visibility toggle to form input (#12524) # Pull Request Template ## Description This PR adds a password visibility toggle to the auth form input component. ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? ### Screencast https://github.com/user-attachments/assets/17652e86-e823-46e6-a3ba-80af37c78906 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- app/javascript/v3/components/Form/Input.vue | 38 +++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/app/javascript/v3/components/Form/Input.vue b/app/javascript/v3/components/Form/Input.vue index 4a0b0dc63..8e719808f 100644 --- a/app/javascript/v3/components/Form/Input.vue +++ b/app/javascript/v3/components/Form/Input.vue @@ -1,8 +1,11 @@