feat: Enable lock to single thread settings for Telegram (#12367)

This PR implements the **"Lock to Single Conversation"** option for
Telegram inboxes, bringing it to parity with WhatsApp, SMS, and other
channels.

- When **enabled**: resolved conversations can be reopened (single
thread).
- When **disabled**: new messages from a resolved conversation create a
**new conversation**.
- Added **agent name display** in outgoing Telegram messages (formatted
as `Agent Name: message`).
- Updated frontend to display agent name above messages in the dashboard
(consistent with WhatsApp behavior).

This fixes [#8046](https://github.com/chatwoot/chatwoot/issues/8046).

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality not to work as expected)
- [ ] This change requires a documentation update

## How Has This Been Tested?

- Unit tests added in
`spec/services/telegram/incoming_message_service_spec.rb`
- Scenarios covered:
  - Lock enabled → reopens resolved conversation
  - Lock disabled → creates new conversation if resolved
  - Lock disabled → appends to last open conversation
- Manual tests:
  1. Create a Telegram conversation
  2. Mark it as resolved
  3. Send a new message from same user
  4.  Expected: new conversation created (if lock disabled)


## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [ ] 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
- [ ] Any dependent changes have been merged and published in downstream
modules

## Additional Documentation

For full technical details of this implementation, please refer to:  

[TELEGRAM_LOCK_TO_SINGLE_CONVERSATION_IMPLEMENTATION_EN.md](./TELEGRAM_LOCK_TO_SINGLE_CONVERSATION_IMPLEMENTATION_EN.md)

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Macoly Melo
2025-09-24 03:05:14 -03:00
committed by GitHub
parent 44fab70048
commit e68522318b
3 changed files with 80 additions and 2 deletions

View File

@@ -206,7 +206,8 @@ export default {
this.isASmsInbox ||
this.isAWhatsAppChannel ||
this.isAFacebookInbox ||
this.isAPIInbox
this.isAPIInbox ||
this.isATelegramChannel
);
},
inboxNameLabel() {

View File

@@ -77,7 +77,13 @@ class Telegram::IncomingMessageService
end
def set_conversation
@conversation = @contact_inbox.conversations.first
# if lock to single conversation is disabled, we will create a new conversation if previous conversation is resolved
@conversation = if @inbox.lock_to_single_conversation
@contact_inbox.conversations.last
else
@contact_inbox.conversations
.where.not(status: :resolved).last
end
return if @conversation
@conversation = ::Conversation.create!(conversation_params)

View File

@@ -411,4 +411,75 @@ describe Telegram::IncomingMessageService do
end
end
end
context 'when lock to single conversation is enabled' do
before do
# ensure message_params exists in this context and has from.id
message_params[:from] ||= {}
message_params[:from][:id] ||= 23
end
it 'reopens last conversation if last conversation is resolved' do
telegram_channel.inbox.update!(lock_to_single_conversation: true)
contact_inbox = ContactInbox.find_or_create_by(inbox: telegram_channel.inbox, source_id: message_params[:from][:id]) do |ci|
ci.contact = create(:contact)
end
resolved_conversation = create(:conversation, inbox: telegram_channel.inbox, contact_inbox: contact_inbox, status: :resolved)
params = {
'update_id' => 2_342_342_343_242,
'message' => { 'text' => 'test' }.merge(message_params)
}.with_indifferent_access
described_class.new(inbox: telegram_channel.inbox, params: params).perform
expect(telegram_channel.inbox.conversations.count).to eq(1)
expect(resolved_conversation.reload.messages.last.content).to eq('test')
end
end
context 'when lock to single conversation is disabled' do
before do
# ensure message_params exists in this context and has from.id
message_params[:from] ||= {}
message_params[:from][:id] ||= 23
end
it 'creates new conversation if last conversation is resolved' do
telegram_channel.inbox.update!(lock_to_single_conversation: false)
contact_inbox = ContactInbox.find_or_create_by(inbox: telegram_channel.inbox, source_id: message_params[:from][:id]) do |ci|
ci.contact = create(:contact)
end
_resolved_conversation = create(:conversation, inbox: telegram_channel.inbox, contact_inbox: contact_inbox, status: :resolved)
params = {
'update_id' => 2_342_342_343_242,
'message' => { 'text' => 'test' }.merge(message_params)
}.with_indifferent_access
described_class.new(inbox: telegram_channel.inbox, params: params).perform
expect(telegram_channel.inbox.conversations.count).to eq(2)
expect(telegram_channel.inbox.conversations.last.messages.first.content).to eq('test')
expect(telegram_channel.inbox.conversations.last.status).to eq('open')
end
it 'appends to last conversation if last conversation is not resolved' do
telegram_channel.inbox.update!(lock_to_single_conversation: false)
contact_inbox = ContactInbox.find_or_create_by(inbox: telegram_channel.inbox, source_id: message_params[:from][:id]) do |ci|
ci.contact = create(:contact)
end
open_conversation = create(:conversation, inbox: telegram_channel.inbox, contact_inbox: contact_inbox, status: :open)
params = {
'update_id' => 2_342_342_343_242,
'message' => { 'text' => 'test' }.merge(message_params)
}.with_indifferent_access
described_class.new(inbox: telegram_channel.inbox, params: params).perform
expect(telegram_channel.inbox.conversations.count).to eq(1)
expect(open_conversation.reload.messages.last.content).to eq('test')
end
end
end