* feat: add audio transcoding support for WhatsApp Cloud API
- Introduced `Audio::TranscodeService` to handle audio transcoding to OGG/Opus format.
- Updated `Messages::MessageBuilder` to transcode audio attachments based on `transcode_audio` parameter.
- Enhanced `WhatsappCloudService` to normalize audio content types and send voice flag for recorded audio in OGG format.
- Added utility functions for audio conversion in JavaScript.
- Updated Dockerfile to include FFmpeg for audio processing.
- Added tests for audio transcoding and WhatsApp Cloud service interactions.
* feat: enhance audio handling with transcoding support and error management
* feat: improve audio transcoding error handling and enhance audio recording features
* feat: enhance audio transcoding process and error handling for better reliability
* feat: update recorded audio handling to support boolean and array formats
Fixes https://github.com/chatwoot/chatwoot/issues/13317
Fixes an issue where WhatsApp attachment messages (images, audio, video,
documents) were failing to download. Messages were being created but
without attachments.
The `phone_number_id` parameter was being passed to the `GET
/<MEDIA_ID>` endpoint when downloading incoming media. According to
Meta's documentation:
> "Note that `phone_number_id` is optional. If included, the request
will only be processed if the business phone number ID included in the
query matches the ID of the business
phone number **that the media was uploaded on**."
For incoming messages, media is uploaded by the customer, not by the
business phone number. Passing the business's `phone_number_id` causes
validation to fail with error: `Param phone_number_id is not a valid
whatsapp business phone number id ID`
This PR removes the `phone_number_id` parameter from the media URL
request for incoming messages.
* fix: z-api read messages
* fix: use threads for parallel requests
* feat: use jobs instead
* fix: refactor ZapiReadMessageJob to use service method for sending read messages
This PR add the backend changes for the feature [sending CSAT surveys
via WhatsApp message templates
](https://github.com/chatwoot/chatwoot/pull/12787)
---------
Co-authored-by: Vinay Keerthi <11478411+stonecharioteer@users.noreply.github.com>
* fix: ignore action events
* feat(zapi): allow updating instance ID
* feat: handle `chatLid` field
* test: break down handler specs into separate files
* feat: update send_message method to use recipient_id logic for zapi provider
* fix: use identifier instead of source_id for zapi
* test: fix specs
* feat: prioritize senderName over chatName for contact naming in received callback
## Description
This pull request introduces an optional parameter, `phone_number_id`,
to the WhatsApp API call responsible for retrieving media. The addition
of this parameter allows for greater flexibility when interacting with
the WhatsApp API, as it can now accommodate scenarios where specifying a
particular phone number ID is necessary. This change is backward
compatible and does not affect existing functionality if the parameter
is not provided.
Fixes # (issue)
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
The changes were tested locally by invoking the WhatsApp media retrieval
API with and without the `phone_number_id` parameter. Both scenarios
were verified to ensure that:
- When `phone_number_id` is provided, the API call includes the
parameter and functions as expected.
- When `phone_number_id` is omitted, the API call continues to work as
before, maintaining backward compatibility.
No errors or warnings were observed during testing, and all relevant
unit tests passed successfully.
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
## Description
This implementation adds support for the `media_name` parameter for
WhatsApp document templates, resolving the issue where documents appear
as "untitled" when sent via templates.
**Problem solved:** Documents sent via WhatsApp templates always
appeared as "untitled" because Chatwoot didn't process the `filename`
field required by the WhatsApp API.
**Solution:** Added support for the `media_name` parameter that maps to
the WhatsApp API's `filename` field.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update
## How Has This Been Tested?
Created and executed **7 comprehensive test scenarios**:
1. ✅ Document without `media_name` (backward compatibility)
2. ✅ Document with valid `media_name`
3. ✅ Document with blank `media_name`
4. ✅ Document with null `media_name`
5. ✅ Image with `media_name` (ignored as expected)
6. ✅ Video with `media_name` (ignored as expected)
7. ✅ Blank URL (returns nil appropriately)
**All tests passed** and confirmed **100% backward compatibility**.
## Technical Implementation
**Backend Changes:**
- `PopulateTemplateParametersService`: Added `media_name` parameter
support
- `TemplateProcessorService`: Pass `media_name` to parameter builder
- `WhatsappCloudService`: Updated documentation with `media_name`
example
**Frontend Changes:**
- `WhatsAppTemplateParser.vue`: Added UI field for document filename
input
- `templateHelper.js`: Include `media_name` for document templates
- `whatsappTemplates.json`: Added translation key for document name
placeholder
**Key Features:**
- 🔄 **100% Backward Compatible** - Existing templates continue working
- 📝 **Document Filename Support** - Users can specify custom filenames
- 🎯 **Document-Only Feature** - Only affects document media types
- ✅ **Comprehensive Testing** - All edge cases covered
## Expected Behavior
**Before:**
```ruby
# All documents appear as "untitled"
{
type: 'document',
document: { link: 'https://example.com/document.pdf' }
}
```
**After:**
```ruby
# With media_name - displays custom filename
{
type: 'document',
document: {
link: 'https://example.com/document.pdf',
filename: 'Invoice_2025.pdf'
}
}
# Without media_name - works as before
{
type: 'document',
document: { link: 'https://example.com/document.pdf' }
}
```
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] 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
- [x] Any dependent changes have been merged and published in downstream
modules
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
* feat(baileys): add status method to fetch Baileys API version and availability
* test: specs
* chore: raise error instead of returning error in version
* feat(whatsapp): add profile picture fetching for Baileys provider
- Add `get_profile_pic` method to `WhatsappBaileysService` for fetching contact avatars
- Include `avatar_url` in contact attributes during contact creation
- Update `MessagesUpsert` handler to fetch and set profile pictures
- Add proper error handling and graceful degradation when avatar fetching fails
* test(whatsapp): add test coverage for Baileys profile picture fetching
- Add test suite for `get_profile_pic` method
- Include test scenarios for successful responses, missing avatars, and error handling
- Add profile picture HTTP stubbing and avatar job expectations to message processing tests
* feat(whatsapp): optimize Baileys avatar fetching and update avatar for existing inboxes
Previously, the profile picture was fetched on every incoming message and avatar updates were only attempted for new inboxes.
This change optimizes the process by:
- Only fetching the WhatsApp profile picture if the contact does not already have an avatar.
- Attempting to update the avatar for both new and existing inboxes.
This logic is based on Telegram's version (app/services/telegram/incoming_message_service.rb).
* refactor(whatsapp): remove unnecessary safe navigation when accessing contact
This reverts an unnecessary change introduced in b918a92.
* refactor(whatsapp): manually rollback change introduced in b918a92
* refactor(whatsapp): rename endpoint to profile-picture-url
* refactor(whatsapp): simplify profile picture response handling
Replaced manual JSON parsing with HTTParty's parsed_response and removed redundant JSON parsing tests.
* test(whatsapp): separate avatar processing test from message creation test
* test(whatsapp): update profile picture test to match actual API behavior
Change test to expect a 404 response when no profile picture exists, instead of a 200 response with partial data.
* refactor: move new functions to helper module
* test: more specs
---------
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
WhatsApp template message errors were not being properly handled because
the `@message instance` variable was only set in the `send_message`
method but not in `send_template`. When template sending failed, the
`handle_error` method couldn't update the message status due to the
missing @message reference, resulting in silent failures with no user
feedback.
* feat: add received messages handling in WhatsApp services
* feat: ensure received messages are processed in the conversation
* tests: add received messages specs
* tests: add spec for call provider received_messages after save incoming message
* test: add spec to prevent calling received_messages for outgoing messages
* feat: add received_messages to error handling methods
* test: add spec for sending received messages request in WhatsappBaileysService
* fix: update fromMe flag in WhatsappBaileysService
* test: add spec to verify received_messages is not called for protocol messages
* test: improve received_messages stub
---------
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
* fix: fill whatsapp provider url with value from inbox provider config
* chore: compact blank on setup channel provider
* fix: deadlock on `reconnecting` state
* fix: correct structure of lastMessages in message content
* fix: update send_message to store external_created_at timestamp with result from request
* fix: filter incoming messages for unread conversation logic
* fix: improve extraction of message timestamp in send_message method
* feat: enhance message timestamp extraction and update logic in send_message method
* refactor: send_message to improve error handling and update external_created_at timestamp
* chore: move message timestamp extraction to helper
* fix: use message timestamp extract on incoming
* fix: update unread_conversation to handle last message correctly for Baileys provider
---------
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
* feat: add MESSAGES_UNREAD event and implement unread action in ConversationsController
* feat: rename MESSAGES_UNREAD event to CONVERSATION_UNREAD and update unread action in ConversationsController
* feat: implement send_unread_conversation method and update conversation_unread event handling
* feat: add test for dispatching conversation.unread event in ConversationsController
* feat: add conversation_unread event handling in ChannelListener
* feat: add send_unread_conversation method and corresponding tests in Whatsapp channel
* feat: add logging for nil last_message in send_unread_conversation method
* feat: add send_unread_conversation to error handling methods
* feat: implement send_unread_conversation method with logging for nil last_message
* fix: add nil check and logging for last_message in send_unread_conversation method
* fix: update conversation_unread method to call unread_conversation instead of send_unread_conversation
* refactor: rename send_unread_conversation to unread_conversation and update method implementation
* refactor: rename send_unread_conversation to send_unread_messages and update implementation to handle multiple messages
* refactor: rename send_read_messages to read_messages and send_unread_messages to unread_message for clarity
* refactor: rename send_read_messages to read_messages and send_unread_messages to unread_message for clarity
* refactor: rename send_read_messages to read_messages for consistency
* feat: add get_media method to retrieve media from provider service
* feat: implement media attachment handling and add media_url method
* feat: add includeMedia option to webhook request in WhatsappBaileysService
* feat: add get_media method to error handling in WhatsappBaileysService
* feat: add includeMedia option to webhook request in WhatsappBaileysService spec
* feat: add media_url method to retrieve media URL in WhatsappBaileysService spec
* refactor: remove get_media method from WhatsappBaileysService and Channel::Whatsapp
* refactor: streamline message handling in IncomingMessageBaileysService spec
* chore: add todo comment about includeMedia
* refactor: simplify media download stubbing in IncomingMessageBaileysService spec
* refactor: simplify media download stubbing in IncomingMessageBaileysService spec
* fix: add missing end statements missed in rebase
---------
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
* feat: implement send_read_messages method for WhatsApp channel
* feat: implement messages_read event handling and dispatch for conversations
* feat: enhance messages_read handling to include last_seen_at and conversation context
* feat: update last_seen handling to reference agent_last_seen_at for messages read event
* chore: fix rebase
* feat: update error handling
* feat: update send_read_messages to mark received messages as not sent by the user
* test: controller spec
* test: channel listener
* test: channel and provider
---------
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>