feat(baileys): upgrade to v7 (#143)
* feat(baileys): upgrade to v7 (#139) * feat(baileys): v7 * chore(zapi): match baileys changes * test: fix send on whatsapp spec * chore: simplify logic and minor fixes * fix: ensure contact avatar updates when profile picture changes * chore: add beta release workflow * feat: enhance contact update logic to handle conflicts in inbox updates (#140) * feat: update GitHub Actions workflow to trigger on release events and adjust GIT_REF handling * fix: prevent unique constraint error when LID contact inbox already exists (#141) * fix: prevent unique constraint error when LID contact inbox already exists * feat(baileys): handle ephemeral messages
This commit is contained in:
parent
17149f3dca
commit
c041090675
17
.github/workflows/publish_ee_github_docker.yml
vendored
17
.github/workflows/publish_ee_github_docker.yml
vendored
@ -5,9 +5,8 @@ permissions:
|
|||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
release:
|
||||||
tags:
|
types: [released]
|
||||||
- '*'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@ -25,10 +24,12 @@ jobs:
|
|||||||
runner: ubuntu-22.04-arm
|
runner: ubuntu-22.04-arm
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
env:
|
env:
|
||||||
GIT_REF: ${{ github.head_ref || github.ref_name || github.sha }}
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.release.tag_name || github.ref }}
|
||||||
|
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
@ -61,7 +62,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@ -75,7 +76,7 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile
|
file: docker/Dockerfile
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
|
push: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
|
||||||
tags: |
|
tags: |
|
||||||
${{ env.GITHUB_REPO }}:${{ env.SANITIZED_REF }}-ee
|
${{ env.GITHUB_REPO }}:${{ env.SANITIZED_REF }}-ee
|
||||||
${{ env.GITHUB_REPO }}:latest-ee
|
${{ env.GITHUB_REPO }}:latest-ee
|
||||||
@ -119,7 +120,7 @@ jobs:
|
|||||||
- name: Create manifest list and push
|
- name: Create manifest list and push
|
||||||
working-directory: ${{ runner.temp }}/digests
|
working-directory: ${{ runner.temp }}/digests
|
||||||
env:
|
env:
|
||||||
GIT_REF: ${{ github.head_ref || github.ref_name || github.sha }}
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
||||||
docker buildx imagetools create \
|
docker buildx imagetools create \
|
||||||
@ -129,7 +130,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Inspect image
|
- name: Inspect image
|
||||||
env:
|
env:
|
||||||
GIT_REF: ${{ github.head_ref || github.ref_name || github.sha }}
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
||||||
REPO="ghcr.io/${{ github.repository }}"
|
REPO="ghcr.io/${{ github.repository }}"
|
||||||
|
|||||||
17
.github/workflows/publish_github_docker.yml
vendored
17
.github/workflows/publish_github_docker.yml
vendored
@ -5,9 +5,8 @@ permissions:
|
|||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
release:
|
||||||
tags:
|
types: [released]
|
||||||
- '*'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@ -25,10 +24,12 @@ jobs:
|
|||||||
runner: ubuntu-22.04-arm
|
runner: ubuntu-22.04-arm
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
env:
|
env:
|
||||||
GIT_REF: ${{ github.head_ref || github.ref_name }}
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.release.tag_name || github.ref }}
|
||||||
|
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
@ -62,7 +63,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@ -76,7 +77,7 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile
|
file: docker/Dockerfile
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
|
push: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
|
||||||
tags: |
|
tags: |
|
||||||
${{ env.GITHUB_REPO }}:${{ env.SANITIZED_REF }}
|
${{ env.GITHUB_REPO }}:${{ env.SANITIZED_REF }}
|
||||||
${{ env.GITHUB_REPO }}:latest
|
${{ env.GITHUB_REPO }}:latest
|
||||||
@ -120,7 +121,7 @@ jobs:
|
|||||||
- name: Create manifest list and push
|
- name: Create manifest list and push
|
||||||
working-directory: ${{ runner.temp }}/digests
|
working-directory: ${{ runner.temp }}/digests
|
||||||
env:
|
env:
|
||||||
GIT_REF: ${{ github.head_ref || github.ref_name }}
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
||||||
docker buildx imagetools create \
|
docker buildx imagetools create \
|
||||||
@ -130,7 +131,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Inspect image
|
- name: Inspect image
|
||||||
env:
|
env:
|
||||||
GIT_REF: ${{ github.head_ref || github.ref_name }}
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
||||||
REPO="ghcr.io/${{ github.repository }}"
|
REPO="ghcr.io/${{ github.repository }}"
|
||||||
|
|||||||
139
.github/workflows/publish_github_docker_beta.yml
vendored
Normal file
139
.github/workflows/publish_github_docker_beta.yml
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
name: Publish Chatwoot beta docker images to GitHub
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [prereleased]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_REPO: ghcr.io/${{ github.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- platform: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-22.04-arm
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
env:
|
||||||
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.release.tag_name || github.ref }}
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Strip enterprise code
|
||||||
|
run: |
|
||||||
|
rm -rf enterprise
|
||||||
|
rm -rf spec/enterprise
|
||||||
|
|
||||||
|
- name: Update version in app.yml
|
||||||
|
run: |
|
||||||
|
if [[ "$GIT_REF" =~ ^v(.+)$ ]]; then
|
||||||
|
VERSION="${BASH_REMATCH[1]}"
|
||||||
|
echo "Updating version to: $VERSION"
|
||||||
|
sed -i "s/version: '.*'/version: '$VERSION'/" config/app.yml
|
||||||
|
else
|
||||||
|
echo "No version tag found, keeping existing version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Set Docker Tags
|
||||||
|
run: |
|
||||||
|
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
||||||
|
echo "SANITIZED_REF=${SANITIZED_REF}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push to GitHub Container Registry
|
||||||
|
id: build-ghcr
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/Dockerfile
|
||||||
|
platforms: ${{ matrix.platform }}
|
||||||
|
push: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
|
||||||
|
tags: |
|
||||||
|
${{ env.GITHUB_REPO }}:${{ env.SANITIZED_REF }}
|
||||||
|
${{ env.GITHUB_REPO }}:beta
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ runner.temp }}/digests
|
||||||
|
digest="${{ steps.build-ghcr.outputs.digest }}"
|
||||||
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: digests-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: ${{ runner.temp }}/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
steps:
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: ${{ runner.temp }}/digests
|
||||||
|
pattern: digests-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Create manifest list and push
|
||||||
|
working-directory: ${{ runner.temp }}/digests
|
||||||
|
env:
|
||||||
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
||||||
|
docker buildx imagetools create \
|
||||||
|
-t ghcr.io/${{ github.repository }}:${SANITIZED_REF} \
|
||||||
|
-t ghcr.io/${{ github.repository }}:beta \
|
||||||
|
$(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *)
|
||||||
|
|
||||||
|
- name: Inspect image
|
||||||
|
env:
|
||||||
|
GIT_REF: ${{ github.event.release.tag_name || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g')
|
||||||
|
REPO="ghcr.io/${{ github.repository }}"
|
||||||
|
docker buildx imagetools inspect ${REPO}:${SANITIZED_REF}
|
||||||
|
docker buildx imagetools inspect ${REPO}:beta
|
||||||
@ -86,8 +86,9 @@ class Channel::Whatsapp < ApplicationRecord
|
|||||||
def toggle_typing_status(typing_status, conversation:)
|
def toggle_typing_status(typing_status, conversation:)
|
||||||
return unless provider_service.respond_to?(:toggle_typing_status)
|
return unless provider_service.respond_to?(:toggle_typing_status)
|
||||||
|
|
||||||
|
recipient_id = conversation.contact.identifier || conversation.contact.phone_number
|
||||||
last_message = conversation.messages.last
|
last_message = conversation.messages.last
|
||||||
provider_service.toggle_typing_status(typing_status, last_message: last_message, phone_number: conversation.contact.phone_number)
|
provider_service.toggle_typing_status(typing_status, last_message: last_message, recipient_id: recipient_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_presence(status)
|
def update_presence(status)
|
||||||
@ -101,7 +102,13 @@ class Channel::Whatsapp < ApplicationRecord
|
|||||||
# NOTE: This is the default behavior, so `mark_as_read` being `nil` is the same as `true`.
|
# NOTE: This is the default behavior, so `mark_as_read` being `nil` is the same as `true`.
|
||||||
return if provider_config&.dig('mark_as_read') == false
|
return if provider_config&.dig('mark_as_read') == false
|
||||||
|
|
||||||
provider_service.read_messages(messages, phone_number: conversation.contact.phone_number)
|
recipient_id = if provider == 'zapi'
|
||||||
|
conversation.contact.phone_number
|
||||||
|
else
|
||||||
|
conversation.contact.identifier || conversation.contact.phone_number
|
||||||
|
end
|
||||||
|
|
||||||
|
provider_service.read_messages(messages, recipient_id: recipient_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unread_conversation(conversation)
|
def unread_conversation(conversation)
|
||||||
@ -122,7 +129,8 @@ class Channel::Whatsapp < ApplicationRecord
|
|||||||
def received_messages(messages, conversation)
|
def received_messages(messages, conversation)
|
||||||
return unless provider_service.respond_to?(:received_messages)
|
return unless provider_service.respond_to?(:received_messages)
|
||||||
|
|
||||||
provider_service.received_messages(conversation.contact.phone_number, messages)
|
recipient_id = conversation.contact.identifier || conversation.contact.phone_number
|
||||||
|
provider_service.received_messages(recipient_id, messages)
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_whatsapp(phone_number)
|
def on_whatsapp(phone_number)
|
||||||
|
|||||||
@ -3,14 +3,12 @@ module Whatsapp::BaileysHandlers::Helpers # rubocop:disable Metrics/ModuleLength
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def raw_message_id
|
def unwrap_ephemeral_message(msg)
|
||||||
@raw_message[:key][:id]
|
msg.key?(:ephemeralMessage) ? msg.dig(:ephemeralMessage, :message) : msg
|
||||||
end
|
end
|
||||||
|
|
||||||
def sender_lid
|
def raw_message_id
|
||||||
return @raw_message[:key][:senderLid] if @raw_message[:key].key?(:senderLid)
|
@raw_message[:key][:id]
|
||||||
|
|
||||||
@raw_message[:key][:remoteJid] if jid_type == 'lid'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def incoming?
|
def incoming?
|
||||||
@ -42,7 +40,7 @@ module Whatsapp::BaileysHandlers::Helpers # rubocop:disable Metrics/ModuleLength
|
|||||||
end
|
end
|
||||||
|
|
||||||
def message_type # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength,Metrics/AbcSize
|
def message_type # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength,Metrics/AbcSize
|
||||||
msg = @raw_message[:message]
|
msg = unwrap_ephemeral_message(@raw_message[:message])
|
||||||
if msg.key?(:conversation) || msg.dig(:extendedTextMessage, :text).present?
|
if msg.key?(:conversation) || msg.dig(:extendedTextMessage, :text).present?
|
||||||
'text'
|
'text'
|
||||||
elsif msg.key?(:imageMessage)
|
elsif msg.key?(:imageMessage)
|
||||||
@ -72,22 +70,23 @@ module Whatsapp::BaileysHandlers::Helpers # rubocop:disable Metrics/ModuleLength
|
|||||||
end
|
end
|
||||||
|
|
||||||
def message_content # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength
|
def message_content # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength
|
||||||
|
msg = unwrap_ephemeral_message(@raw_message[:message])
|
||||||
case message_type
|
case message_type
|
||||||
when 'text'
|
when 'text'
|
||||||
@raw_message.dig(:message, :conversation) || @raw_message.dig(:message, :extendedTextMessage, :text)
|
msg[:conversation] || msg.dig(:extendedTextMessage, :text)
|
||||||
when 'image'
|
when 'image'
|
||||||
@raw_message.dig(:message, :imageMessage, :caption)
|
msg.dig(:imageMessage, :caption)
|
||||||
when 'video'
|
when 'video'
|
||||||
@raw_message.dig(:message, :videoMessage, :caption)
|
msg.dig(:videoMessage, :caption)
|
||||||
when 'file'
|
when 'file'
|
||||||
@raw_message.dig(:message, :documentMessage, :caption).presence ||
|
msg.dig(:documentMessage, :caption).presence ||
|
||||||
@raw_message.dig(:message, :documentWithCaptionMessage, :message, :documentMessage, :caption)
|
msg.dig(:documentWithCaptionMessage, :message, :documentMessage, :caption)
|
||||||
when 'reaction'
|
when 'reaction'
|
||||||
@raw_message.dig(:message, :reactionMessage, :text)
|
msg.dig(:reactionMessage, :text)
|
||||||
when 'contact'
|
when 'contact'
|
||||||
# FIXME: Missing specs
|
# FIXME: Missing specs
|
||||||
display_name = @raw_message.dig(:message, :contactMessage, :displayName)
|
display_name = msg.dig(:contactMessage, :displayName)
|
||||||
vcard = @raw_message.dig(:message, :contactMessage, :vcard)
|
vcard = msg.dig(:contactMessage, :vcard)
|
||||||
match_phone_number = vcard&.match(/waid=(\d+)/)
|
match_phone_number = vcard&.match(/waid=(\d+)/)
|
||||||
|
|
||||||
return display_name unless match_phone_number
|
return display_name unless match_phone_number
|
||||||
@ -97,6 +96,24 @@ module Whatsapp::BaileysHandlers::Helpers # rubocop:disable Metrics/ModuleLength
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reply_to_message_id # rubocop:disable Metrics/CyclomaticComplexity
|
||||||
|
msg = unwrap_ephemeral_message(@raw_message[:message])
|
||||||
|
message_key = case message_type
|
||||||
|
when 'text' then :extendedTextMessage
|
||||||
|
when 'image' then :imageMessage
|
||||||
|
when 'sticker' then :stickerMessage
|
||||||
|
when 'audio' then :audioMessage
|
||||||
|
when 'video' then :videoMessage
|
||||||
|
when 'contact' then :contactMessage
|
||||||
|
when 'file'
|
||||||
|
context_info = msg.dig(:documentMessage, :contextInfo).presence ||
|
||||||
|
msg.dig(:documentWithCaptionMessage, :message, :documentMessage, :contextInfo)
|
||||||
|
return context_info&.dig(:stanzaId)
|
||||||
|
end
|
||||||
|
|
||||||
|
msg.dig(message_key, :contextInfo, :stanzaId) if message_key
|
||||||
|
end
|
||||||
|
|
||||||
def file_content_type
|
def file_content_type
|
||||||
return :image if message_type.in?(%w[image sticker])
|
return :image if message_type.in?(%w[image sticker])
|
||||||
return :video if message_type.in?(%w[video video_note])
|
return :video if message_type.in?(%w[video video_note])
|
||||||
@ -106,28 +123,31 @@ module Whatsapp::BaileysHandlers::Helpers # rubocop:disable Metrics/ModuleLength
|
|||||||
end
|
end
|
||||||
|
|
||||||
def message_mimetype
|
def message_mimetype
|
||||||
|
msg = unwrap_ephemeral_message(@raw_message[:message])
|
||||||
case message_type
|
case message_type
|
||||||
when 'image'
|
when 'image'
|
||||||
@raw_message.dig(:message, :imageMessage, :mimetype)
|
msg.dig(:imageMessage, :mimetype)
|
||||||
when 'sticker'
|
when 'sticker'
|
||||||
@raw_message.dig(:message, :stickerMessage, :mimetype)
|
msg.dig(:stickerMessage, :mimetype)
|
||||||
when 'video'
|
when 'video'
|
||||||
@raw_message.dig(:message, :videoMessage, :mimetype)
|
msg.dig(:videoMessage, :mimetype)
|
||||||
when 'audio'
|
when 'audio'
|
||||||
@raw_message.dig(:message, :audioMessage, :mimetype)
|
msg.dig(:audioMessage, :mimetype)
|
||||||
when 'file'
|
when 'file'
|
||||||
@raw_message.dig(:message, :documentMessage, :mimetype).presence ||
|
msg.dig(:documentMessage, :mimetype).presence ||
|
||||||
@raw_message.dig(:message, :documentWithCaptionMessage, :message, :documentMessage, :mimetype)
|
msg.dig(:documentWithCaptionMessage, :message, :documentMessage, :mimetype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def phone_number_from_jid
|
def extract_from_jid(type:)
|
||||||
reference_field = jid_type == 'lid' ? :senderPn : :remoteJid
|
addressing_mode = @raw_message[:key][:addressingMode]
|
||||||
|
reference_field = addressing_mode && addressing_mode != type ? :remoteJidAlt : :remoteJid
|
||||||
|
|
||||||
jid = @raw_message[:key][reference_field]
|
jid = @raw_message[:key][reference_field]
|
||||||
return unless jid
|
return unless jid
|
||||||
|
|
||||||
# NOTE: jid shape is `<user>_<agent>:<device>@<server>`
|
# NOTE: jid shape is `<user>_<agent>:<device>@<server>`
|
||||||
# https://github.com/WhiskeySockets/Baileys/blob/v6.7.16/src/WABinary/jid-utils.ts#L19
|
# https://github.com/WhiskeySockets/Baileys/blob/v7.0.0-rc.6/src/WABinary/jid-utils.ts#L52
|
||||||
jid.split('@').first.split(':').first.split('_').first
|
jid.split('@').first.split(':').first.split('_').first
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -136,12 +156,17 @@ module Whatsapp::BaileysHandlers::Helpers # rubocop:disable Metrics/ModuleLength
|
|||||||
name = @raw_message[:verifiedBizName].presence || @raw_message[:pushName]
|
name = @raw_message[:verifiedBizName].presence || @raw_message[:pushName]
|
||||||
return name if name.presence && (self_message? || incoming?)
|
return name if name.presence && (self_message? || incoming?)
|
||||||
|
|
||||||
phone_number_from_jid
|
extract_from_jid(type: 'pn') || extract_from_jid(type: 'lid')
|
||||||
end
|
end
|
||||||
|
|
||||||
def self_message?
|
def self_message?
|
||||||
# TODO: Handle denormalized Brazilian phone numbers
|
normalize_phone_number(extract_from_jid(type: 'pn')) == normalize_phone_number(inbox.channel.phone_number.delete('+'))
|
||||||
phone_number_from_jid == inbox.channel.phone_number.delete('+')
|
end
|
||||||
|
|
||||||
|
def normalize_phone_number(phone_number)
|
||||||
|
return unless phone_number
|
||||||
|
|
||||||
|
Whatsapp::PhoneNormalizers::BrazilPhoneNormalizer.new.normalize(phone_number)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ignore_message?
|
def ignore_message?
|
||||||
@ -162,22 +187,23 @@ module Whatsapp::BaileysHandlers::Helpers # rubocop:disable Metrics/ModuleLength
|
|||||||
# TODO: Current logic will never update the contact avatar if their profile picture changes on WhatsApp.
|
# TODO: Current logic will never update the contact avatar if their profile picture changes on WhatsApp.
|
||||||
return if @contact.avatar.attached?
|
return if @contact.avatar.attached?
|
||||||
|
|
||||||
profile_pic_url = fetch_profile_picture_url(phone_number_from_jid)
|
phone = extract_from_jid(type: 'pn')
|
||||||
|
profile_pic_url = fetch_profile_picture_url(phone) if phone
|
||||||
::Avatar::AvatarFromUrlJob.perform_later(@contact, profile_pic_url) if profile_pic_url
|
::Avatar::AvatarFromUrlJob.perform_later(@contact, profile_pic_url) if profile_pic_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_under_process?
|
def message_under_process?
|
||||||
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: raw_message_id)
|
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
|
||||||
Redis::Alfred.get(key)
|
Redis::Alfred.get(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_message_source_id_in_redis
|
def cache_message_source_id_in_redis
|
||||||
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: raw_message_id)
|
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
|
||||||
::Redis::Alfred.setex(key, true)
|
::Redis::Alfred.setex(key, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_message_source_id_from_redis
|
def clear_message_source_id_from_redis
|
||||||
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: raw_message_id)
|
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
|
||||||
::Redis::Alfred.delete(key)
|
::Redis::Alfred.delete(key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
module Whatsapp::BaileysHandlers::MessagesUpsert
|
module Whatsapp::BaileysHandlers::MessagesUpsert # rubocop:disable Metrics/ModuleLength
|
||||||
include Whatsapp::BaileysHandlers::Helpers
|
include Whatsapp::BaileysHandlers::Helpers
|
||||||
include BaileysHelper
|
include BaileysHelper
|
||||||
|
|
||||||
@ -20,9 +20,9 @@ module Whatsapp::BaileysHandlers::MessagesUpsert
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_message # rubocop:disable Metrics/CyclomaticComplexity
|
def handle_message
|
||||||
return unless %w[lid user].include?(jid_type)
|
return unless %w[lid user].include?(jid_type)
|
||||||
return if jid_type == 'lid' && !phone_number_from_jid
|
return unless extract_from_jid(type: 'lid')
|
||||||
return if ignore_message?
|
return if ignore_message?
|
||||||
return if find_message_by_source_id(raw_message_id) || message_under_process?
|
return if find_message_by_source_id(raw_message_id) || message_under_process?
|
||||||
|
|
||||||
@ -42,27 +42,51 @@ module Whatsapp::BaileysHandlers::MessagesUpsert
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_contact
|
def set_contact
|
||||||
|
phone = extract_from_jid(type: 'pn')
|
||||||
|
source_id = extract_from_jid(type: 'lid')
|
||||||
|
identifier = "#{source_id}@lid"
|
||||||
|
|
||||||
|
update_existing_contact_inbox(phone, source_id, identifier) if phone
|
||||||
|
|
||||||
contact_inbox = ::ContactInboxWithContactBuilder.new(
|
contact_inbox = ::ContactInboxWithContactBuilder.new(
|
||||||
# FIXME: update the source_id to use sender LID
|
source_id: source_id,
|
||||||
source_id: phone_number_from_jid,
|
|
||||||
inbox: inbox,
|
inbox: inbox,
|
||||||
contact_attributes: { name: contact_name, phone_number: "+#{phone_number_from_jid}", identifier: sender_lid }
|
contact_attributes: { name: contact_name, phone_number: ("+#{phone}" if phone), identifier: identifier }
|
||||||
).perform
|
).perform
|
||||||
|
|
||||||
@contact_inbox = contact_inbox
|
@contact_inbox = contact_inbox
|
||||||
@contact = contact_inbox.contact
|
@contact = contact_inbox.contact
|
||||||
|
|
||||||
update_contact_information
|
update_contact_info(phone, source_id, identifier)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_contact_information
|
def update_existing_contact_inbox(phone, source_id, identifier)
|
||||||
updates = {}
|
# NOTE: This is useful when we create a new contact manually, so we don't have information about contact LID;
|
||||||
updates[:identifier] = sender_lid if @contact.identifier.blank?
|
# With this, when we receive a message from that contact, we can link it properly.
|
||||||
updates[:phone_number] = "+#{phone_number_from_jid}" if @contact.phone_number.blank?
|
existing_contact_inbox = inbox.contact_inboxes.find_by(source_id: phone)
|
||||||
updates[:name] = contact_name if @contact.name == phone_number_from_jid || @contact.name == sender_lid
|
return unless existing_contact_inbox
|
||||||
|
return if inbox.contact_inboxes.exists?(source_id: source_id)
|
||||||
|
|
||||||
@contact.update!(updates) if updates.present?
|
existing_contact = existing_contact_inbox.contact
|
||||||
|
conflicting_identifier = inbox.account.contacts.find_by(identifier: identifier)
|
||||||
|
conflicting_phone = inbox.account.contacts.find_by(phone_number: "+#{phone}")
|
||||||
|
|
||||||
|
return if conflicting_identifier && conflicting_identifier.id != existing_contact.id
|
||||||
|
return if conflicting_phone && conflicting_phone.id != existing_contact.id
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
existing_contact_inbox.update!(source_id: source_id)
|
||||||
|
existing_contact.update!(identifier: identifier, phone_number: "+#{phone}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_contact_info(phone, source_id, identifier)
|
||||||
|
update_params = {}
|
||||||
|
update_params[:phone_number] = "+#{phone}" if phone
|
||||||
|
update_params[:identifier] = identifier
|
||||||
|
update_params[:name] = contact_name if @contact.name.in?([phone, source_id, identifier])
|
||||||
|
|
||||||
|
@contact.update!(update_params) if update_params.present?
|
||||||
try_update_contact_avatar
|
try_update_contact_avatar
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -91,10 +115,13 @@ module Whatsapp::BaileysHandlers::MessagesUpsert
|
|||||||
|
|
||||||
def message_content_attributes
|
def message_content_attributes
|
||||||
type = message_type
|
type = message_type
|
||||||
|
msg = unwrap_ephemeral_message(@raw_message[:message])
|
||||||
content_attributes = { external_created_at: baileys_extract_message_timestamp(@raw_message[:messageTimestamp]) }
|
content_attributes = { external_created_at: baileys_extract_message_timestamp(@raw_message[:messageTimestamp]) }
|
||||||
if type == 'reaction'
|
if type == 'reaction'
|
||||||
content_attributes[:in_reply_to_external_id] = @raw_message.dig(:message, :reactionMessage, :key, :id)
|
content_attributes[:in_reply_to_external_id] = msg.dig(:reactionMessage, :key, :id)
|
||||||
content_attributes[:is_reaction] = true
|
content_attributes[:is_reaction] = true
|
||||||
|
elsif reply_to_message_id
|
||||||
|
content_attributes[:in_reply_to_external_id] = reply_to_message_id
|
||||||
elsif type == 'unsupported'
|
elsif type == 'unsupported'
|
||||||
content_attributes[:is_unsupported] = true
|
content_attributes[:is_unsupported] = true
|
||||||
end
|
end
|
||||||
@ -104,13 +131,14 @@ module Whatsapp::BaileysHandlers::MessagesUpsert
|
|||||||
|
|
||||||
def handle_attach_media
|
def handle_attach_media
|
||||||
attachment_file = download_attachment_file
|
attachment_file = download_attachment_file
|
||||||
|
msg = unwrap_ephemeral_message(@raw_message[:message])
|
||||||
|
|
||||||
attachment = @message.attachments.build(
|
attachment = @message.attachments.build(
|
||||||
account_id: @message.account_id,
|
account_id: @message.account_id,
|
||||||
file_type: file_content_type.to_s,
|
file_type: file_content_type.to_s,
|
||||||
file: { io: attachment_file, filename: filename, content_type: message_mimetype }
|
file: { io: attachment_file, filename: filename, content_type: message_mimetype }
|
||||||
)
|
)
|
||||||
attachment.meta = { is_recorded_audio: true } if @raw_message.dig(:message, :audioMessage, :ptt)
|
attachment.meta = { is_recorded_audio: true } if msg.dig(:audioMessage, :ptt)
|
||||||
rescue Down::Error => e
|
rescue Down::Error => e
|
||||||
@message.update!(is_unsupported: true)
|
@message.update!(is_unsupported: true)
|
||||||
|
|
||||||
@ -122,7 +150,8 @@ module Whatsapp::BaileysHandlers::MessagesUpsert
|
|||||||
end
|
end
|
||||||
|
|
||||||
def filename
|
def filename
|
||||||
filename = @raw_message.dig(:message, :documentMessage, :fileName)
|
msg = unwrap_ephemeral_message(@raw_message[:message])
|
||||||
|
filename = msg.dig(:documentMessage, :fileName)
|
||||||
return filename if filename.present?
|
return filename if filename.present?
|
||||||
|
|
||||||
ext = ".#{message_mimetype.split(';').first.split('/').last}" if message_mimetype.present?
|
ext = ".#{message_mimetype.split(';').first.split('/').last}" if message_mimetype.present?
|
||||||
|
|||||||
@ -55,9 +55,9 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_message(phone_number, message)
|
def send_message(recipient_id, message)
|
||||||
@message = message
|
@message = message
|
||||||
@phone_number = phone_number
|
@recipient_id = recipient_id
|
||||||
|
|
||||||
if message.content_attributes[:is_reaction]
|
if message.content_attributes[:is_reaction]
|
||||||
@message_content = reaction_message_content
|
@message_content = reaction_message_content
|
||||||
@ -94,8 +94,8 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
process_response(response)
|
process_response(response)
|
||||||
end
|
end
|
||||||
|
|
||||||
def toggle_typing_status(typing_status, phone_number:, **)
|
def toggle_typing_status(typing_status, recipient_id:, **)
|
||||||
@phone_number = phone_number
|
@recipient_id = recipient_id
|
||||||
status_map = {
|
status_map = {
|
||||||
Events::Types::CONVERSATION_TYPING_ON => 'composing',
|
Events::Types::CONVERSATION_TYPING_ON => 'composing',
|
||||||
Events::Types::CONVERSATION_RECORDING => 'recording',
|
Events::Types::CONVERSATION_RECORDING => 'recording',
|
||||||
@ -136,8 +136,8 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_messages(messages, phone_number:, **)
|
def read_messages(messages, recipient_id:, **)
|
||||||
@phone_number = phone_number
|
@recipient_id = recipient_id
|
||||||
|
|
||||||
response = HTTParty.post(
|
response = HTTParty.post(
|
||||||
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/read-messages",
|
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/read-messages",
|
||||||
@ -158,8 +158,8 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def unread_message(phone_number, message) # rubocop:disable Metrics/MethodLength
|
def unread_message(recipient_id, message) # rubocop:disable Metrics/MethodLength
|
||||||
@phone_number = phone_number
|
@recipient_id = recipient_id
|
||||||
|
|
||||||
response = HTTParty.post(
|
response = HTTParty.post(
|
||||||
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/chat-modify",
|
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/chat-modify",
|
||||||
@ -185,8 +185,8 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def received_messages(phone_number, messages)
|
def received_messages(recipient_id, messages)
|
||||||
@phone_number = phone_number
|
@recipient_id = recipient_id
|
||||||
|
|
||||||
response = HTTParty.post(
|
response = HTTParty.post(
|
||||||
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/send-receipts",
|
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/send-receipts",
|
||||||
@ -220,8 +220,8 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
response.parsed_response
|
response.parsed_response
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_whatsapp(phone_number)
|
def on_whatsapp(recipient_id)
|
||||||
@phone_number = phone_number
|
@recipient_id = recipient_id
|
||||||
|
|
||||||
response = HTTParty.post(
|
response = HTTParty.post(
|
||||||
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/on-whatsapp",
|
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/on-whatsapp",
|
||||||
@ -233,7 +233,7 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
|
|
||||||
raise ProviderUnavailableError unless process_response(response)
|
raise ProviderUnavailableError unless process_response(response)
|
||||||
|
|
||||||
response.parsed_response&.first || { 'jid' => remote_jid, 'exists' => false, 'lid' => nil }
|
response.parsed_response&.first || { 'jid' => remote_jid, 'exists' => false }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -303,9 +303,10 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
response.success?
|
response.success?
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME: Make this a function with argument, instead of using instance variable
|
|
||||||
def remote_jid
|
def remote_jid
|
||||||
"#{@phone_number.delete('+')}@s.whatsapp.net"
|
return @recipient_id if @recipient_id.ends_with?('@lid')
|
||||||
|
|
||||||
|
"#{@recipient_id.delete('+')}@s.whatsapp.net"
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_external_created_at(response)
|
def update_external_created_at(response)
|
||||||
|
|||||||
@ -80,7 +80,7 @@ class Whatsapp::Providers::WhatsappZapiService < Whatsapp::Providers::BaseServic
|
|||||||
response.parsed_response['value']
|
response.parsed_response['value']
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_messages(messages, phone_number:, **)
|
def read_messages(messages, recipient_id:, **)
|
||||||
# NOTE: Z-API will handle marking previous messages as read.
|
# NOTE: Z-API will handle marking previous messages as read.
|
||||||
last_message = messages.last
|
last_message = messages.last
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ class Whatsapp::Providers::WhatsappZapiService < Whatsapp::Providers::BaseServic
|
|||||||
"#{api_instance_path_with_token}/read-message",
|
"#{api_instance_path_with_token}/read-message",
|
||||||
headers: api_headers,
|
headers: api_headers,
|
||||||
body: {
|
body: {
|
||||||
phone: phone_number.delete('+'),
|
phone: recipient_id.delete('+'),
|
||||||
messageId: last_message.source_id
|
messageId: last_message.source_id
|
||||||
}.to_json
|
}.to_json
|
||||||
)
|
)
|
||||||
|
|||||||
@ -51,14 +51,10 @@ class Whatsapp::SendOnWhatsappService < Base::SendOnChannelService
|
|||||||
end
|
end
|
||||||
|
|
||||||
def recipient_id
|
def recipient_id
|
||||||
return message.conversation.contact_inbox.source_id unless channel.provider == 'zapi'
|
return message.conversation.contact_inbox.source_id unless %w[baileys zapi].include?(channel.provider)
|
||||||
|
|
||||||
if message.conversation.contact.phone_number.present?
|
# NOTE: `identifier` must be in the WhatsApp LID format
|
||||||
message.conversation.contact.phone_number.gsub(/[^\d]/, '')
|
message.conversation.contact.phone_number&.gsub(/[^\d]/, '') || message.conversation.contact.identifier
|
||||||
else
|
|
||||||
# NOTE: `identifier` is the WhatsApp LID format. See `Whatsapp::ZapiHandlers::ReceivedCallback#set_contact`
|
|
||||||
message.conversation.contact.identifier
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def template_params
|
def template_params
|
||||||
|
|||||||
@ -12,17 +12,17 @@ module Whatsapp::ZapiHandlers::Helpers
|
|||||||
end
|
end
|
||||||
|
|
||||||
def cache_message_source_id_in_redis
|
def cache_message_source_id_in_redis
|
||||||
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: raw_message_id)
|
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
|
||||||
Redis::Alfred.setex(key, true)
|
Redis::Alfred.setex(key, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_message_source_id_from_redis
|
def clear_message_source_id_from_redis
|
||||||
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: raw_message_id)
|
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
|
||||||
Redis::Alfred.delete(key)
|
Redis::Alfred.delete(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_under_process?
|
def message_under_process?
|
||||||
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: raw_message_id)
|
key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
|
||||||
Redis::Alfred.get(key)
|
Redis::Alfred.get(key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -184,9 +184,10 @@ RSpec.describe Channel::Whatsapp do
|
|||||||
let(:conversation) { create(:conversation) }
|
let(:conversation) { create(:conversation) }
|
||||||
|
|
||||||
it 'calls provider service method' do
|
it 'calls provider service method' do
|
||||||
|
message = create(:message, conversation: conversation)
|
||||||
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, toggle_typing_status: nil)
|
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, toggle_typing_status: nil)
|
||||||
allow(provider_double).to receive(:toggle_typing_status)
|
allow(provider_double).to receive(:toggle_typing_status)
|
||||||
.with(Events::Types::CONVERSATION_TYPING_ON, phone_number: conversation.contact.phone_number)
|
.with(Events::Types::CONVERSATION_TYPING_ON, recipient_id: conversation.contact.identifier, last_message: message)
|
||||||
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
||||||
.with(whatsapp_channel: channel)
|
.with(whatsapp_channel: channel)
|
||||||
.and_return(provider_double)
|
.and_return(provider_double)
|
||||||
@ -242,11 +243,11 @@ RSpec.describe Channel::Whatsapp do
|
|||||||
create(:channel_whatsapp, provider: 'baileys', provider_config: { mark_as_read: true }, validate_provider_config: false, sync_templates: false)
|
create(:channel_whatsapp, provider: 'baileys', provider_config: { mark_as_read: true }, validate_provider_config: false, sync_templates: false)
|
||||||
end
|
end
|
||||||
let(:conversation) { create(:conversation) }
|
let(:conversation) { create(:conversation) }
|
||||||
let(:message) { create(:message) }
|
let(:message) { create(:message, conversation: conversation) }
|
||||||
|
|
||||||
it 'calls provider service method' do
|
it 'calls provider service method' do
|
||||||
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, read_messages: nil)
|
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, read_messages: nil)
|
||||||
allow(provider_double).to receive(:read_messages).with([message], phone_number: conversation.contact.phone_number)
|
allow(provider_double).to receive(:read_messages).with([message], recipient_id: conversation.contact.identifier)
|
||||||
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
||||||
.with(whatsapp_channel: channel)
|
.with(whatsapp_channel: channel)
|
||||||
.and_return(provider_double)
|
.and_return(provider_double)
|
||||||
@ -259,7 +260,7 @@ RSpec.describe Channel::Whatsapp do
|
|||||||
it 'call method when the provider config mark_as_read is nil' do
|
it 'call method when the provider config mark_as_read is nil' do
|
||||||
channel.update!(provider_config: {})
|
channel.update!(provider_config: {})
|
||||||
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, read_messages: nil)
|
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, read_messages: nil)
|
||||||
allow(provider_double).to receive(:read_messages).with([message], phone_number: conversation.contact.phone_number)
|
allow(provider_double).to receive(:read_messages).with([message], recipient_id: conversation.contact.identifier)
|
||||||
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
||||||
.with(whatsapp_channel: channel)
|
.with(whatsapp_channel: channel)
|
||||||
.and_return(provider_double)
|
.and_return(provider_double)
|
||||||
@ -332,7 +333,7 @@ RSpec.describe Channel::Whatsapp do
|
|||||||
|
|
||||||
it 'calls provider service method' do
|
it 'calls provider service method' do
|
||||||
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, received_messages: nil)
|
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, received_messages: nil)
|
||||||
allow(provider_double).to receive(:received_messages).with(conversation.contact.phone_number, messages)
|
allow(provider_double).to receive(:received_messages).with(conversation.contact.identifier, messages)
|
||||||
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
||||||
.with(whatsapp_channel: channel)
|
.with(whatsapp_channel: channel)
|
||||||
.and_return(provider_double)
|
.and_return(provider_double)
|
||||||
@ -415,4 +416,51 @@ RSpec.describe Channel::Whatsapp do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#provider_connection_data' do
|
||||||
|
let(:channel) do
|
||||||
|
create(:channel_whatsapp, provider: 'baileys', validate_provider_config: false, sync_templates: false,
|
||||||
|
provider_connection: {
|
||||||
|
'connection' => 'open',
|
||||||
|
'qr_data_url' => 'data:image/png;base64,test',
|
||||||
|
'error' => 'some_error'
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is an administrator' do
|
||||||
|
it 'includes qr_data_url and error in the response' do
|
||||||
|
account_user = create(:account_user, account: channel.account, role: :administrator)
|
||||||
|
allow(Current).to receive(:account_user).and_return(account_user)
|
||||||
|
|
||||||
|
data = channel.provider_connection_data
|
||||||
|
|
||||||
|
expect(data).to eq({
|
||||||
|
connection: 'open',
|
||||||
|
qr_data_url: 'data:image/png;base64,test',
|
||||||
|
error: 'some_error'
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not an administrator' do
|
||||||
|
it 'excludes qr_data_url and error from the response' do
|
||||||
|
account_user = create(:account_user, account: channel.account, role: :agent)
|
||||||
|
allow(Current).to receive(:account_user).and_return(account_user)
|
||||||
|
|
||||||
|
data = channel.provider_connection_data
|
||||||
|
|
||||||
|
expect(data).to eq({ connection: 'open' })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Current.account_user is nil' do
|
||||||
|
it 'excludes qr_data_url and error from the response' do
|
||||||
|
allow(Current).to receive(:account_user).and_return(nil)
|
||||||
|
|
||||||
|
data = channel.provider_connection_data
|
||||||
|
|
||||||
|
expect(data).to eq({ connection: 'open' })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
319
spec/services/whatsapp/baileys_handlers/messages_upsert_spec.rb
Normal file
319
spec/services/whatsapp/baileys_handlers/messages_upsert_spec.rb
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Whatsapp::BaileysHandlers::MessagesUpsert do
|
||||||
|
let(:webhook_verify_token) { 'valid_token' }
|
||||||
|
let!(:whatsapp_channel) do
|
||||||
|
create(:channel_whatsapp,
|
||||||
|
provider: 'baileys',
|
||||||
|
provider_config: { webhook_verify_token: webhook_verify_token },
|
||||||
|
validate_provider_config: false,
|
||||||
|
received_messages: false)
|
||||||
|
end
|
||||||
|
let(:inbox) { whatsapp_channel.inbox }
|
||||||
|
let(:timestamp) { Time.current.to_i }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:get, /profile-picture-url/)
|
||||||
|
.to_return(
|
||||||
|
status: 200,
|
||||||
|
body: { data: { profilePictureUrl: nil } }.to_json
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#update_existing_contact_inbox' do
|
||||||
|
context 'when updating contact inbox with LID information' do
|
||||||
|
let(:phone) { '5511912345678' }
|
||||||
|
let(:lid) { '12345678' }
|
||||||
|
let(:source_id) { lid }
|
||||||
|
let(:identifier) { "#{lid}@lid" }
|
||||||
|
|
||||||
|
context 'when there is no conflict' do
|
||||||
|
it 'updates existing contact_inbox source_id from phone to LID' do
|
||||||
|
contact = create(:contact, account: inbox.account, phone_number: "+#{phone}", identifier: nil)
|
||||||
|
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: phone)
|
||||||
|
|
||||||
|
raw_message = {
|
||||||
|
key: { id: 'msg_123', remoteJid: "#{lid}@lid", remoteJidAlt: "#{phone}@s.whatsapp.net", fromMe: false, addressingMode: 'lid' },
|
||||||
|
pushName: 'John Doe',
|
||||||
|
messageTimestamp: timestamp,
|
||||||
|
message: { conversation: 'Hello' }
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
webhookVerifyToken: webhook_verify_token,
|
||||||
|
event: 'messages.upsert',
|
||||||
|
data: { type: 'notify', messages: [raw_message] }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Whatsapp::IncomingMessageBaileysService.new(inbox: inbox, params: params).perform
|
||||||
|
end.not_to raise_error
|
||||||
|
|
||||||
|
expect(contact_inbox.reload.source_id).to eq(source_id)
|
||||||
|
expect(contact.reload.identifier).to eq(identifier)
|
||||||
|
expect(contact.phone_number).to eq("+#{phone}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when identifier is already taken by a different contact (race condition)' do
|
||||||
|
it 'does not raise validation error and skips the update' do
|
||||||
|
original_contact = create(:contact, account: inbox.account, phone_number: nil, identifier: nil, name: 'Original Contact')
|
||||||
|
original_contact_inbox = create(:contact_inbox, inbox: inbox, contact: original_contact, source_id: phone)
|
||||||
|
|
||||||
|
conflicting_contact = create(:contact, account: inbox.account, phone_number: "+#{phone}", identifier: identifier,
|
||||||
|
name: 'Conflicting Contact')
|
||||||
|
create(:contact_inbox, inbox: inbox, contact: conflicting_contact, source_id: source_id)
|
||||||
|
|
||||||
|
raw_message = {
|
||||||
|
key: { id: 'msg_123', remoteJid: "#{lid}@lid", remoteJidAlt: "#{phone}@s.whatsapp.net", fromMe: false, addressingMode: 'lid' },
|
||||||
|
pushName: 'John Doe',
|
||||||
|
messageTimestamp: timestamp,
|
||||||
|
message: { conversation: 'Hello' }
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
webhookVerifyToken: webhook_verify_token,
|
||||||
|
event: 'messages.upsert',
|
||||||
|
data: { type: 'notify', messages: [raw_message] }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Whatsapp::IncomingMessageBaileysService.new(inbox: inbox, params: params).perform
|
||||||
|
end.not_to raise_error
|
||||||
|
|
||||||
|
expect(original_contact_inbox.reload.source_id).to eq(phone)
|
||||||
|
expect(original_contact.reload.identifier).to be_nil
|
||||||
|
|
||||||
|
message = inbox.messages.last
|
||||||
|
expect(message).to be_present
|
||||||
|
expect(message.sender).to eq(conflicting_contact)
|
||||||
|
expect(message.conversation.contact).to eq(conflicting_contact)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when phone number is already taken by a different contact (race condition)' do
|
||||||
|
it 'does not raise validation error and skips the update' do
|
||||||
|
original_contact = create(:contact, account: inbox.account, phone_number: nil, identifier: nil)
|
||||||
|
create(:contact_inbox, inbox: inbox, contact: original_contact, source_id: phone)
|
||||||
|
|
||||||
|
different_lid = '87654321'
|
||||||
|
different_identifier = "#{different_lid}@lid"
|
||||||
|
conflicting_contact = create(:contact, account: inbox.account, phone_number: "+#{phone}", identifier: different_identifier)
|
||||||
|
create(:contact_inbox, inbox: inbox, contact: conflicting_contact, source_id: different_lid)
|
||||||
|
|
||||||
|
raw_message = {
|
||||||
|
key: { id: 'msg_123', remoteJid: "#{lid}@lid", remoteJidAlt: "#{phone}@s.whatsapp.net", fromMe: false, addressingMode: 'lid' },
|
||||||
|
pushName: 'John Doe',
|
||||||
|
messageTimestamp: timestamp,
|
||||||
|
message: { conversation: 'Hello' }
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
webhookVerifyToken: webhook_verify_token,
|
||||||
|
event: 'messages.upsert',
|
||||||
|
data: { type: 'notify', messages: [raw_message] }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Whatsapp::IncomingMessageBaileysService.new(inbox: inbox, params: params).perform
|
||||||
|
end.not_to raise_error
|
||||||
|
|
||||||
|
expect(original_contact.reload.phone_number).to be_nil
|
||||||
|
expect(original_contact.identifier).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when LID contact_inbox already exists (race condition)' do
|
||||||
|
it 'does not raise unique constraint error and skips the update' do
|
||||||
|
original_contact = create(:contact, account: inbox.account, phone_number: nil, identifier: nil)
|
||||||
|
create(:contact_inbox, inbox: inbox, contact: original_contact, source_id: phone)
|
||||||
|
|
||||||
|
lid_contact = create(:contact, account: inbox.account, phone_number: "+#{phone}", identifier: identifier)
|
||||||
|
create(:contact_inbox, inbox: inbox, contact: lid_contact, source_id: source_id)
|
||||||
|
|
||||||
|
raw_message = {
|
||||||
|
key: { id: 'msg_123', remoteJid: "#{lid}@lid", remoteJidAlt: "#{phone}@s.whatsapp.net", fromMe: false, addressingMode: 'lid' },
|
||||||
|
pushName: 'John Doe',
|
||||||
|
messageTimestamp: timestamp,
|
||||||
|
message: { conversation: 'Hello' }
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
webhookVerifyToken: webhook_verify_token,
|
||||||
|
event: 'messages.upsert',
|
||||||
|
data: { type: 'notify', messages: [raw_message] }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Whatsapp::IncomingMessageBaileysService.new(inbox: inbox, params: params).perform
|
||||||
|
end.not_to raise_error
|
||||||
|
|
||||||
|
message = inbox.messages.last
|
||||||
|
expect(message).to be_present
|
||||||
|
expect(message.sender).to eq(lid_contact)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when updating the same contact (no conflict)' do
|
||||||
|
it 'successfully updates the contact' do
|
||||||
|
contact = create(:contact, account: inbox.account, phone_number: "+#{phone}", identifier: nil)
|
||||||
|
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: phone)
|
||||||
|
|
||||||
|
raw_message = {
|
||||||
|
key: { id: 'msg_123', remoteJid: "#{lid}@lid", remoteJidAlt: "#{phone}@s.whatsapp.net", fromMe: false, addressingMode: 'lid' },
|
||||||
|
pushName: 'John Doe',
|
||||||
|
messageTimestamp: timestamp,
|
||||||
|
message: { conversation: 'Hello' }
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
webhookVerifyToken: webhook_verify_token,
|
||||||
|
event: 'messages.upsert',
|
||||||
|
data: { type: 'notify', messages: [raw_message] }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Whatsapp::IncomingMessageBaileysService.new(inbox: inbox, params: params).perform
|
||||||
|
end.not_to raise_error
|
||||||
|
|
||||||
|
expect(contact_inbox.reload.source_id).to eq(source_id)
|
||||||
|
expect(contact.reload.identifier).to eq(identifier)
|
||||||
|
expect(contact.phone_number).to eq("+#{phone}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'ephemeral message handling' do
|
||||||
|
let(:phone) { '5511912345678' }
|
||||||
|
let(:lid) { '12345678' }
|
||||||
|
|
||||||
|
context 'when receiving an ephemeral text message' do
|
||||||
|
it 'correctly unwraps and processes the message' do
|
||||||
|
raw_message = {
|
||||||
|
key: { id: 'msg_ephemeral_123', remoteJid: "#{phone}@s.whatsapp.net", remoteJidAlt: "#{lid}@lid", fromMe: false,
|
||||||
|
addressingMode: 'pn' },
|
||||||
|
pushName: 'Gabriel',
|
||||||
|
messageTimestamp: timestamp,
|
||||||
|
message: {
|
||||||
|
messageContextInfo: {
|
||||||
|
deviceListMetadata: {},
|
||||||
|
deviceListMetadataVersion: 2
|
||||||
|
},
|
||||||
|
ephemeralMessage: {
|
||||||
|
message: {
|
||||||
|
extendedTextMessage: {
|
||||||
|
text: 'This is a disappearing message',
|
||||||
|
contextInfo: {
|
||||||
|
expiration: 604_800,
|
||||||
|
disappearingMode: { initiator: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
webhookVerifyToken: webhook_verify_token,
|
||||||
|
event: 'messages.upsert',
|
||||||
|
data: { type: 'notify', messages: [raw_message] }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Whatsapp::IncomingMessageBaileysService.new(inbox: inbox, params: params).perform
|
||||||
|
end.to change(inbox.messages, :count).by(1)
|
||||||
|
|
||||||
|
message = inbox.messages.last
|
||||||
|
expect(message.content).to eq('This is a disappearing message')
|
||||||
|
expect(message.message_type).to eq('incoming')
|
||||||
|
expect(message.is_unsupported).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when receiving an ephemeral image message' do
|
||||||
|
it 'correctly unwraps and processes the message with media' do
|
||||||
|
raw_message = {
|
||||||
|
key: { id: 'msg_ephemeral_image_123', remoteJid: "#{phone}@s.whatsapp.net", remoteJidAlt: "#{lid}@lid", fromMe: false,
|
||||||
|
addressingMode: 'pn' },
|
||||||
|
pushName: 'Gabriel',
|
||||||
|
messageTimestamp: timestamp,
|
||||||
|
message: {
|
||||||
|
messageContextInfo: {
|
||||||
|
deviceListMetadata: {},
|
||||||
|
deviceListMetadataVersion: 2
|
||||||
|
},
|
||||||
|
ephemeralMessage: {
|
||||||
|
message: {
|
||||||
|
imageMessage: {
|
||||||
|
caption: 'Check this out',
|
||||||
|
mimetype: 'image/jpeg',
|
||||||
|
url: 'https://example.com/image.jpg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
webhookVerifyToken: webhook_verify_token,
|
||||||
|
event: 'messages.upsert',
|
||||||
|
data: { type: 'notify', messages: [raw_message] }
|
||||||
|
}
|
||||||
|
|
||||||
|
stub_request(:get, whatsapp_channel.media_url('msg_ephemeral_image_123'))
|
||||||
|
.to_return(status: 200, body: 'fake image data')
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Whatsapp::IncomingMessageBaileysService.new(inbox: inbox, params: params).perform
|
||||||
|
end.to change(inbox.messages, :count).by(1)
|
||||||
|
|
||||||
|
message = inbox.messages.last
|
||||||
|
expect(message.content).to eq('Check this out')
|
||||||
|
expect(message.message_type).to eq('incoming')
|
||||||
|
expect(message.is_unsupported).to be_falsey
|
||||||
|
expect(message.attachments.count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when receiving an ephemeral reaction message' do
|
||||||
|
it 'correctly unwraps and processes the reaction' do
|
||||||
|
# First create the original message
|
||||||
|
contact = create(:contact, account: inbox.account, phone_number: "+#{phone}", identifier: "#{lid}@lid")
|
||||||
|
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: lid)
|
||||||
|
conversation = create(:conversation, inbox: inbox, contact_inbox: contact_inbox)
|
||||||
|
original_message = create(:message, inbox: inbox, conversation: conversation, source_id: 'original_msg_id')
|
||||||
|
|
||||||
|
raw_message = {
|
||||||
|
key: { id: 'msg_ephemeral_reaction_123', remoteJid: "#{phone}@s.whatsapp.net", remoteJidAlt: "#{lid}@lid", fromMe: false,
|
||||||
|
addressingMode: 'pn' },
|
||||||
|
pushName: 'Gabriel',
|
||||||
|
messageTimestamp: timestamp,
|
||||||
|
message: {
|
||||||
|
messageContextInfo: {
|
||||||
|
deviceListMetadata: {},
|
||||||
|
deviceListMetadataVersion: 2
|
||||||
|
},
|
||||||
|
ephemeralMessage: {
|
||||||
|
message: {
|
||||||
|
reactionMessage: {
|
||||||
|
text: '👍',
|
||||||
|
key: { id: 'original_msg_id' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
webhookVerifyToken: webhook_verify_token,
|
||||||
|
event: 'messages.upsert',
|
||||||
|
data: { type: 'notify', messages: [raw_message] }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Whatsapp::IncomingMessageBaileysService.new(inbox: inbox, params: params).perform
|
||||||
|
end.to change(conversation.messages, :count).by(1)
|
||||||
|
|
||||||
|
reaction = conversation.messages.last
|
||||||
|
expect(reaction.content).to eq('👍')
|
||||||
|
expect(reaction.message_type).to eq('incoming')
|
||||||
|
expect(reaction.content_attributes['is_reaction']).to be_truthy
|
||||||
|
expect(reaction.in_reply_to).to eq(original_message.id)
|
||||||
|
expect(reaction.in_reply_to_external_id).to eq(original_message.source_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -171,7 +171,7 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
let(:timestamp) { Time.current.to_i }
|
let(:timestamp) { Time.current.to_i }
|
||||||
let(:raw_message) do
|
let(:raw_message) do
|
||||||
{
|
{
|
||||||
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false, senderLid: '12345678@lid' },
|
key: { id: 'msg_123', remoteJid: '12345678@lid', remoteJidAlt: '5511912345678@s.whatsapp.net', fromMe: false, addressingMode: 'lid' },
|
||||||
pushName: 'John Doe',
|
pushName: 'John Doe',
|
||||||
messageTimestamp: timestamp,
|
messageTimestamp: timestamp,
|
||||||
message: { conversation: 'Hello from Baileys' }
|
message: { conversation: 'Hello from Baileys' }
|
||||||
@ -360,23 +360,6 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
expect(message.message_type).to eq('outgoing')
|
expect(message.message_type).to eq('outgoing')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates an outgoing self message' do
|
|
||||||
self_jid = "#{whatsapp_channel.phone_number.delete('+')}@s.whatsapp.net"
|
|
||||||
raw_message[:key][:remoteJid] = self_jid
|
|
||||||
raw_message[:key][:fromMe] = true
|
|
||||||
create(:account_user, account: inbox.account)
|
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
|
||||||
|
|
||||||
conversation = inbox.conversations.last
|
|
||||||
message = conversation.messages.last
|
|
||||||
|
|
||||||
expect(message).to be_present
|
|
||||||
expect(message.content).to eq('Hello from Baileys')
|
|
||||||
expect(conversation.contact.name).to eq('John Doe')
|
|
||||||
expect(message.message_type).to eq('outgoing')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the contact name when name is the phone number and message has a pushName' do
|
it 'updates the contact name when name is the phone number and message has a pushName' do
|
||||||
create(:contact, account: inbox.account, name: '5511912345678')
|
create(:contact, account: inbox.account, name: '5511912345678')
|
||||||
|
|
||||||
@ -396,6 +379,23 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
expect(conversation.contact.name).to eq('Verified John')
|
expect(conversation.contact.name).to eq('Verified John')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'creates contact with LID only when phone number is not present' do
|
||||||
|
raw_message[:key][:remoteJid] = '87654321@lid'
|
||||||
|
raw_message[:key][:remoteJidAlt] = nil
|
||||||
|
|
||||||
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
|
conversation = inbox.conversations.last
|
||||||
|
contact = conversation.contact
|
||||||
|
contact_inbox = conversation.contact_inbox
|
||||||
|
|
||||||
|
expect(contact).to be_present
|
||||||
|
expect(contact.phone_number).to be_nil
|
||||||
|
expect(contact.identifier).to eq('87654321@lid')
|
||||||
|
expect(contact.name).to eq('John Doe')
|
||||||
|
expect(contact_inbox.source_id).to eq('87654321')
|
||||||
|
end
|
||||||
|
|
||||||
it 'creates contact with phone number as name on outgoing message' do
|
it 'creates contact with phone number as name on outgoing message' do
|
||||||
raw_message[:key][:fromMe] = true
|
raw_message[:key][:fromMe] = true
|
||||||
create(:account_user, account: inbox.account)
|
create(:account_user, account: inbox.account)
|
||||||
@ -416,9 +416,23 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
expect(conversation.contact.name).to eq('5511912345678')
|
expect(conversation.contact.name).to eq('5511912345678')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'migrates existing phone-based contact inbox to LID-based when receiving message with LID' do
|
||||||
|
contact = create(:contact, account: inbox.account, name: 'Existing Contact', phone_number: '+5511912345678')
|
||||||
|
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
||||||
|
|
||||||
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
|
contact_inbox.reload
|
||||||
|
contact.reload
|
||||||
|
|
||||||
|
expect(contact_inbox.source_id).to eq('12345678')
|
||||||
|
expect(contact.identifier).to eq('12345678@lid')
|
||||||
|
expect(contact.phone_number).to eq('+5511912345678')
|
||||||
|
end
|
||||||
|
|
||||||
it 'creates a message on an existing conversation' do
|
it 'creates a message on an existing conversation' do
|
||||||
contact = create(:contact, account: inbox.account, name: 'John Doe')
|
contact = create(:contact, account: inbox.account, name: 'John Doe')
|
||||||
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '12345678')
|
||||||
existing_conversation = create(:conversation, inbox: inbox, contact_inbox: contact_inbox)
|
existing_conversation = create(:conversation, inbox: inbox, contact_inbox: contact_inbox)
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
@ -476,7 +490,7 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
context 'when message type is reaction' do
|
context 'when message type is reaction' do
|
||||||
let!(:message) do
|
let!(:message) do
|
||||||
contact = create(:contact, account: inbox.account, name: '5511912345678')
|
contact = create(:contact, account: inbox.account, name: '5511912345678')
|
||||||
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '12345678')
|
||||||
conversation = create(:conversation, inbox: inbox, contact_inbox: contact_inbox)
|
conversation = create(:conversation, inbox: inbox, contact_inbox: contact_inbox)
|
||||||
create(:message, inbox: inbox, conversation: conversation, source_id: 'msg_123')
|
create(:message, inbox: inbox, conversation: conversation, source_id: 'msg_123')
|
||||||
end
|
end
|
||||||
@ -485,7 +499,7 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
raw_message[:key][:id] = 'reaction_123'
|
raw_message[:key][:id] = 'reaction_123'
|
||||||
raw_message[:message] = {
|
raw_message[:message] = {
|
||||||
reactionMessage: {
|
reactionMessage: {
|
||||||
key: { remoteJid: '5511912345678@s.whatsapp.net', fromMe: true, id: 'msg_123' },
|
key: { remoteJid: '12345678@lid', fromMe: true, id: 'msg_123' },
|
||||||
text: '👍'
|
text: '👍'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -502,7 +516,7 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
raw_message[:key][:id] = 'reaction_123'
|
raw_message[:key][:id] = 'reaction_123'
|
||||||
raw_message[:message] = {
|
raw_message[:message] = {
|
||||||
reactionMessage: {
|
reactionMessage: {
|
||||||
key: { remoteJid: '5511912345678@s.whatsapp.net', fromMe: true, id: 'msg_123' },
|
key: { remoteJid: '12345678@lid', fromMe: true, id: 'msg_123' },
|
||||||
text: ''
|
text: ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -522,7 +536,8 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
type: 'notify',
|
type: 'notify',
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false },
|
key: { id: 'msg_123', remoteJid: '12345678@lid', remoteJidAlt: '5511912345678@s.whatsapp.net', fromMe: false,
|
||||||
|
addressingMode: 'lid' },
|
||||||
message: { imageMessage: { caption: 'Hello from Baileys', mimetype: 'image/png' } },
|
message: { imageMessage: { caption: 'Hello from Baileys', mimetype: 'image/png' } },
|
||||||
pushName: 'John Doe'
|
pushName: 'John Doe'
|
||||||
}
|
}
|
||||||
@ -574,7 +589,8 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
type: 'notify',
|
type: 'notify',
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false },
|
key: { id: 'msg_123', remoteJid: '12345678@lid', remoteJidAlt: '5511912345678@s.whatsapp.net', fromMe: false,
|
||||||
|
addressingMode: 'lid' },
|
||||||
message: { videoMessage: { caption: 'Hello from Baileys', mimetype: 'video/mp4' } },
|
message: { videoMessage: { caption: 'Hello from Baileys', mimetype: 'video/mp4' } },
|
||||||
pushName: 'John Doe'
|
pushName: 'John Doe'
|
||||||
}
|
}
|
||||||
@ -617,7 +633,8 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
type: 'notify',
|
type: 'notify',
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false },
|
key: { id: 'msg_123', remoteJid: '12345678@lid', remoteJidAlt: '5511912345678@s.whatsapp.net', fromMe: false,
|
||||||
|
addressingMode: 'lid' },
|
||||||
message: { documentMessage: { fileName: filename } },
|
message: { documentMessage: { fileName: filename } },
|
||||||
pushName: 'John Doe'
|
pushName: 'John Doe'
|
||||||
}
|
}
|
||||||
@ -668,7 +685,8 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
type: 'notify',
|
type: 'notify',
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false },
|
key: { id: 'msg_123', remoteJid: '12345678@lid', remoteJidAlt: '5511912345678@s.whatsapp.net', fromMe: false,
|
||||||
|
addressingMode: 'lid' },
|
||||||
message: { audioMessage: { mimetype: 'audio/opus' } },
|
message: { audioMessage: { mimetype: 'audio/opus' } },
|
||||||
pushName: 'John Doe'
|
pushName: 'John Doe'
|
||||||
}
|
}
|
||||||
@ -699,7 +717,8 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
type: 'notify',
|
type: 'notify',
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false },
|
key: { id: 'msg_123', remoteJid: '12345678@lid', remoteJidAlt: '5511912345678@s.whatsapp.net', fromMe: false,
|
||||||
|
addressingMode: 'lid' },
|
||||||
message: { stickerMessage: { mimetype: 'image/png' } },
|
message: { stickerMessage: { mimetype: 'image/png' } },
|
||||||
pushName: 'John Doe'
|
pushName: 'John Doe'
|
||||||
}
|
}
|
||||||
@ -724,13 +743,13 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
context 'when processing multiple messages' do
|
context 'when processing multiple messages' do
|
||||||
it 'creates separate contacts and conversations for each message' do
|
it 'creates separate contacts and conversations for each message' do
|
||||||
raw_message1 = {
|
raw_message1 = {
|
||||||
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false },
|
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', remoteJidAlt: '12345678@lid', fromMe: false, addressingMode: 'pn' },
|
||||||
pushName: 'John Doe',
|
pushName: 'John Doe',
|
||||||
messageTimestamp: timestamp,
|
messageTimestamp: timestamp,
|
||||||
message: { conversation: 'Hello from Baileys' }
|
message: { conversation: 'Hello from Baileys' }
|
||||||
}
|
}
|
||||||
raw_message2 = {
|
raw_message2 = {
|
||||||
key: { id: 'msg_124', remoteJid: '5511987654321@s.whatsapp.net', fromMe: false },
|
key: { id: 'msg_124', remoteJid: '5511987654321@s.whatsapp.net', remoteJidAlt: '87654321@lid', fromMe: false, addressingMode: 'pn' },
|
||||||
pushName: 'Jane Doe',
|
pushName: 'Jane Doe',
|
||||||
messageTimestamp: timestamp,
|
messageTimestamp: timestamp,
|
||||||
message: { conversation: 'Hello from another user' }
|
message: { conversation: 'Hello from another user' }
|
||||||
@ -764,9 +783,10 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'when jid type is lid' do
|
context 'when jid type is lid' do
|
||||||
it 'processes the message with phone number from lid jid type' do
|
it 'processes the message with phone number from addressingMode pn' do
|
||||||
raw_message[:key][:remoteJid] = '12345678@lid'
|
raw_message[:key][:remoteJid] = '5511912345678@s.whatsapp.net'
|
||||||
raw_message[:key][:senderPn] = '5511912345678@s.whatsapp.net'
|
raw_message[:key][:remoteJidAlt] = '12345678@lid'
|
||||||
|
raw_message[:key][:addressingMode] = 'pn'
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
@ -778,82 +798,90 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'skips message if senderPn is not present' do
|
it 'skips message if LID cannot be extracted' do
|
||||||
raw_message[:key][:remoteJid] = '12345678@lid'
|
raw_message[:key][:remoteJid] = 'invalid_jid'
|
||||||
|
raw_message[:key][:remoteJidAlt] = nil
|
||||||
|
raw_message[:key][:addressingMode] = 'lid'
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
expect(inbox.conversations).to be_empty
|
expect(inbox.conversations).to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when handling senderLid field' do
|
context 'when handling LID field' do
|
||||||
it 'creates contact with identifier from senderLid' do
|
it 'creates contact with LID as source_id and identifier' do
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
contact = inbox.contacts.last
|
contact = inbox.contacts.last
|
||||||
|
contact_inbox = inbox.contact_inboxes.last
|
||||||
|
|
||||||
expect(contact.identifier).to eq('12345678@lid')
|
expect(contact.identifier).to eq('12345678@lid')
|
||||||
|
expect(contact_inbox.source_id).to eq('12345678')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates contact identifier for existing contacts without identifier' do
|
it 'updates existing contact_inbox from phone to LID source_id' do
|
||||||
contact = create(:contact, account: inbox.account, phone_number: '+5511912345678', identifier: nil)
|
contact = create(:contact, account: inbox.account, phone_number: '+5511912345678', identifier: nil)
|
||||||
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
|
contact_inbox = inbox.contact_inboxes.find_by(contact: contact)
|
||||||
|
expect(contact_inbox.source_id).to eq('12345678')
|
||||||
expect(contact.reload.identifier).to eq('12345678@lid')
|
expect(contact.reload.identifier).to eq('12345678@lid')
|
||||||
|
expect(contact.phone_number).to eq('+5511912345678')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not update contact identifier if already present' do
|
it 'does not update contact_inbox if source_id is already LID' do
|
||||||
contact = create(:contact, account: inbox.account, phone_number: '+5511912345678', identifier: 'existing@lid')
|
contact = create(:contact, account: inbox.account, phone_number: '+5511912345678', identifier: '12345678@lid')
|
||||||
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '12345678')
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
expect(contact.reload.identifier).to eq('existing@lid')
|
expect(contact_inbox.reload.source_id).to eq('12345678')
|
||||||
|
expect(contact.reload.identifier).to eq('12345678@lid')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when updating contact information' do
|
context 'when updating contact information' do
|
||||||
it 'updates contact phone number if blank' do
|
it 'updates contact phone number if blank' do
|
||||||
contact = create(:contact, account: inbox.account, phone_number: nil, identifier: '12345678@lid')
|
contact = create(:contact, account: inbox.account, phone_number: nil, identifier: '12345678@lid')
|
||||||
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '12345678')
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
expect(contact.reload.phone_number).to eq('+5511912345678')
|
expect(contact.reload.phone_number).to eq('+5511912345678')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not update contact phone number if already present' do
|
|
||||||
contact = create(:contact, account: inbox.account, phone_number: '+9999999999', identifier: '12345678@lid')
|
|
||||||
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
|
||||||
|
|
||||||
expect(contact.reload.phone_number).to eq('+9999999999')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates contact name if it matches phone number' do
|
it 'updates contact name if it matches phone number' do
|
||||||
contact = create(:contact, account: inbox.account, name: '5511912345678', phone_number: '+5511912345678', identifier: '12345678@lid')
|
contact = create(:contact, account: inbox.account, name: '5511912345678', phone_number: '+5511912345678', identifier: '12345678@lid')
|
||||||
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '12345678')
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
expect(contact.reload.name).to eq('John Doe')
|
expect(contact.reload.name).to eq('John Doe')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates contact name if it matches sender LID' do
|
it 'updates contact name if it matches LID source_id' do
|
||||||
|
contact = create(:contact, account: inbox.account, name: '12345678', phone_number: '+5511912345678', identifier: '12345678@lid')
|
||||||
|
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '12345678')
|
||||||
|
|
||||||
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
|
expect(contact.reload.name).to eq('John Doe')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates contact name if it matches identifier' do
|
||||||
contact = create(:contact, account: inbox.account, name: '12345678@lid', phone_number: '+5511912345678', identifier: '12345678@lid')
|
contact = create(:contact, account: inbox.account, name: '12345678@lid', phone_number: '+5511912345678', identifier: '12345678@lid')
|
||||||
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '12345678')
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
expect(contact.reload.name).to eq('John Doe')
|
expect(contact.reload.name).to eq('John Doe')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not update contact name if it is different from phone number and sender LID' do
|
it 'does not update contact name if it is different from phone number, source_id, and identifier' do
|
||||||
contact = create(:contact, account: inbox.account, name: 'Existing Name', phone_number: '+5511912345678', identifier: '12345678@lid')
|
contact = create(:contact, account: inbox.account, name: 'Existing Name', phone_number: '+5511912345678', identifier: '12345678@lid')
|
||||||
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
|
create(:contact_inbox, inbox: inbox, contact: contact, source_id: '12345678')
|
||||||
|
|
||||||
described_class.new(inbox: inbox, params: params).perform
|
described_class.new(inbox: inbox, params: params).perform
|
||||||
|
|
||||||
@ -958,7 +986,7 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def format_message_source_key(message_id)
|
def format_message_source_key(message_id)
|
||||||
format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: message_id)
|
format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{message_id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def stub_download
|
def stub_download
|
||||||
|
|||||||
@ -477,7 +477,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
body: { keys: [{ id: message.source_id, remoteJid: test_send_jid, fromMe: false }] }.to_json
|
body: { keys: [{ id: message.source_id, remoteJid: test_send_jid, fromMe: false }] }.to_json
|
||||||
).to_return(status: 200, body: '', headers: {})
|
).to_return(status: 200, body: '', headers: {})
|
||||||
|
|
||||||
result = service.read_messages([message], phone_number: test_send_phone_number)
|
result = service.read_messages([message], recipient_id: test_send_phone_number)
|
||||||
|
|
||||||
expect(result).to be(true)
|
expect(result).to be(true)
|
||||||
end
|
end
|
||||||
@ -495,7 +495,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
.to_return(status: 200)
|
.to_return(status: 200)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
service.read_messages([message], phone_number: test_send_phone_number)
|
service.read_messages([message], recipient_id: test_send_phone_number)
|
||||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -605,7 +605,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
)
|
)
|
||||||
.to_return(status: 200)
|
.to_return(status: 200)
|
||||||
|
|
||||||
service.toggle_typing_status(Events::Types::CONVERSATION_TYPING_ON, phone_number: test_send_phone_number)
|
service.toggle_typing_status(Events::Types::CONVERSATION_TYPING_ON, recipient_id: test_send_phone_number)
|
||||||
|
|
||||||
expect(request).to have_been_requested
|
expect(request).to have_been_requested
|
||||||
end
|
end
|
||||||
@ -621,7 +621,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
)
|
)
|
||||||
.to_return(status: 200)
|
.to_return(status: 200)
|
||||||
|
|
||||||
service.toggle_typing_status(Events::Types::CONVERSATION_RECORDING, phone_number: test_send_phone_number)
|
service.toggle_typing_status(Events::Types::CONVERSATION_RECORDING, recipient_id: test_send_phone_number)
|
||||||
|
|
||||||
expect(request).to have_been_requested
|
expect(request).to have_been_requested
|
||||||
end
|
end
|
||||||
@ -637,7 +637,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
)
|
)
|
||||||
.to_return(status: 200)
|
.to_return(status: 200)
|
||||||
|
|
||||||
service.toggle_typing_status(Events::Types::CONVERSATION_TYPING_OFF, phone_number: test_send_phone_number)
|
service.toggle_typing_status(Events::Types::CONVERSATION_TYPING_OFF, recipient_id: test_send_phone_number)
|
||||||
|
|
||||||
expect(request).to have_been_requested
|
expect(request).to have_been_requested
|
||||||
end
|
end
|
||||||
@ -664,7 +664,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
allow(Rails.logger).to receive(:error)
|
allow(Rails.logger).to receive(:error)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
service.toggle_typing_status(Events::Types::CONVERSATION_TYPING_ON, phone_number: test_send_phone_number)
|
service.toggle_typing_status(Events::Types::CONVERSATION_TYPING_ON, recipient_id: test_send_phone_number)
|
||||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||||
|
|
||||||
expect(Rails.logger).to have_received(:error).with('error message')
|
expect(Rails.logger).to have_received(:error).with('error message')
|
||||||
@ -730,12 +730,12 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
.to_return(
|
.to_return(
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { 'Content-Type' => 'application/json' },
|
headers: { 'Content-Type' => 'application/json' },
|
||||||
body: [{ jid: "#{phone_number.delete('+')}@s.whatsapp.net", exists: true, lid: '123@lid' }].to_json
|
body: [{ jid: "#{phone_number.delete('+')}@s.whatsapp.net", exists: true }].to_json
|
||||||
)
|
)
|
||||||
|
|
||||||
response = service.on_whatsapp(phone_number)
|
response = service.on_whatsapp(phone_number)
|
||||||
|
|
||||||
expect(response).to eq({ 'jid' => "#{phone_number.delete('+')}@s.whatsapp.net", 'exists' => true, 'lid' => '123@lid' })
|
expect(response).to eq({ 'jid' => "#{phone_number.delete('+')}@s.whatsapp.net", 'exists' => true })
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns default check response' do
|
it 'returns default check response' do
|
||||||
@ -749,7 +749,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
|
|
||||||
response = service.on_whatsapp(phone_number)
|
response = service.on_whatsapp(phone_number)
|
||||||
|
|
||||||
expect(response).to eq({ 'jid' => "#{phone_number.delete('+')}@s.whatsapp.net", 'exists' => false, 'lid' => nil })
|
expect(response).to eq({ 'jid' => "#{phone_number.delete('+')}@s.whatsapp.net", 'exists' => false })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -916,7 +916,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
whatsapp_channel.update!(provider_connection: { 'connection' => 'open' })
|
whatsapp_channel.update!(provider_connection: { 'connection' => 'open' })
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
service.toggle_typing_status(Events::Types::CONVERSATION_TYPING_ON, phone_number: test_send_phone_number)
|
service.toggle_typing_status(Events::Types::CONVERSATION_TYPING_ON, recipient_id: test_send_phone_number)
|
||||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::ProviderUnavailableError)
|
||||||
|
|
||||||
expect(whatsapp_channel.reload.provider_connection['connection']).to eq('close')
|
expect(whatsapp_channel.reload.provider_connection['connection']).to eq('close')
|
||||||
|
|||||||
@ -208,7 +208,7 @@ describe Whatsapp::Providers::WhatsappZapiService do
|
|||||||
)
|
)
|
||||||
.to_return(status: 200)
|
.to_return(status: 200)
|
||||||
|
|
||||||
result = service.read_messages(messages, phone_number: "+#{test_send_phone_number}")
|
result = service.read_messages(messages, recipient_id: "+#{test_send_phone_number}")
|
||||||
|
|
||||||
expect(result).to be(true)
|
expect(result).to be(true)
|
||||||
end
|
end
|
||||||
@ -223,7 +223,7 @@ describe Whatsapp::Providers::WhatsappZapiService do
|
|||||||
allow(Rails.logger).to receive(:error)
|
allow(Rails.logger).to receive(:error)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
service.read_messages(messages, phone_number: "+#{test_send_phone_number}")
|
service.read_messages(messages, recipient_id: "+#{test_send_phone_number}")
|
||||||
end.to raise_error(Whatsapp::Providers::WhatsappZapiService::ProviderUnavailableError)
|
end.to raise_error(Whatsapp::Providers::WhatsappZapiService::ProviderUnavailableError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -397,8 +397,9 @@ describe Whatsapp::SendOnWhatsappService do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'calls channel.send_message if channel is not locked on outgoing message' do
|
it 'calls channel.send_message if channel is not locked on outgoing message' do
|
||||||
|
conversation.contact.update!(phone_number: '+123456789')
|
||||||
message = create(:message, message_type: :outgoing, content: 'test', conversation: conversation)
|
message = create(:message, message_type: :outgoing, content: 'test', conversation: conversation)
|
||||||
allow(whatsapp_channel).to receive(:send_message).with(conversation.contact_inbox.source_id, message).and_return('123456789')
|
allow(whatsapp_channel).to receive(:send_message).with('123456789', message).and_return('123456789')
|
||||||
|
|
||||||
described_class.new(message: message).perform
|
described_class.new(message: message).perform
|
||||||
|
|
||||||
|
|||||||
@ -1217,6 +1217,6 @@ describe Whatsapp::ZapiHandlers::ReceivedCallback do
|
|||||||
private
|
private
|
||||||
|
|
||||||
def format_message_source_key(message_id)
|
def format_message_source_key(message_id)
|
||||||
format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: message_id)
|
format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{message_id}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user