Chore: Cleanup attachment handling for Facebook & Whatsapp (#1051)
* Chore: Enable file upload for facebook messenger * Chore: Fix attachments * Chore: Fix Specs * Fix ReplyBox file attachment logic * Set default value for message Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
parent
196741d975
commit
a18d54b706
14 changed files with 212 additions and 108 deletions
|
@ -15,9 +15,9 @@
|
||||||
/>
|
/>
|
||||||
<resizable-text-area
|
<resizable-text-area
|
||||||
ref="messageInput"
|
ref="messageInput"
|
||||||
v-model="message"
|
v-model.trim="message"
|
||||||
class="input"
|
class="input"
|
||||||
:placeholder="$t(messagePlaceHolder())"
|
:placeholder="messagePlaceHolder"
|
||||||
:min-height="4"
|
:min-height="4"
|
||||||
@focus="onFocus"
|
@focus="onFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
|
@ -25,46 +25,43 @@
|
||||||
<file-upload
|
<file-upload
|
||||||
v-if="showFileUpload"
|
v-if="showFileUpload"
|
||||||
:size="4096 * 4096"
|
:size="4096 * 4096"
|
||||||
accept="jpg,jpeg,png,mp3,ogg,amr,pdf,mp4"
|
accept="image/*, application/pdf, audio/mpeg, video/mp4, audio/ogg"
|
||||||
@input-file="onFileUpload"
|
@input-file="onFileUpload"
|
||||||
>
|
>
|
||||||
<i
|
<i v-if="!isUploading" class="icon ion-android-attach attachment" />
|
||||||
v-if="!isUploading.image"
|
<woot-spinner v-if="isUploading" />
|
||||||
class="icon ion-android-attach attachment"
|
|
||||||
/>
|
|
||||||
<woot-spinner v-if="isUploading.image" />
|
|
||||||
</file-upload>
|
</file-upload>
|
||||||
<i
|
<i
|
||||||
class="icon ion-happy-outline"
|
class="icon ion-happy-outline"
|
||||||
:class="{ active: showEmojiPicker }"
|
:class="{ active: showEmojiPicker }"
|
||||||
@click="toggleEmojiPicker()"
|
@click="toggleEmojiPicker"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="reply-box__bottom">
|
<div class="reply-box__bottom">
|
||||||
<ul class="tabs">
|
<ul class="tabs">
|
||||||
<li class="tabs-title" :class="{ 'is-active': !isPrivate }">
|
<li class="tabs-title" :class="{ 'is-active': !isPrivate }">
|
||||||
<a href="#" @click="makeReply">{{
|
<a href="#" @click="setReplyMode">{{
|
||||||
$t('CONVERSATION.REPLYBOX.REPLY')
|
$t('CONVERSATION.REPLYBOX.REPLY')
|
||||||
}}</a>
|
}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="tabs-title is-private" :class="{ 'is-active': isPrivate }">
|
<li class="tabs-title is-private" :class="{ 'is-active': isPrivate }">
|
||||||
<a href="#" @click="makePrivate">{{
|
<a href="#" @click="setPrivateReplyMode">{{
|
||||||
$t('CONVERSATION.REPLYBOX.PRIVATE_NOTE')
|
$t('CONVERSATION.REPLYBOX.PRIVATE_NOTE')
|
||||||
}}</a>
|
}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="message.length" class="tabs-title message-length">
|
<li v-if="message.length" class="tabs-title message-length">
|
||||||
<a :class="{ 'message-error': message.length > maxLength - 40 }">
|
<a :class="{ 'message-error': isMessageLengthReachingThreshold }">{{
|
||||||
{{ message.length }} / {{ maxLength }}
|
characterCountIndicator
|
||||||
</a>
|
}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="button send-button"
|
class="button send-button"
|
||||||
:disabled="disableButton()"
|
:disabled="isReplyButtonDisabled"
|
||||||
:class="{
|
:class="{
|
||||||
disabled: message.length === 0 || message.length > maxLength,
|
disabled: isReplyButtonDisabled,
|
||||||
warning: isPrivate,
|
warning: isPrivate,
|
||||||
}"
|
}"
|
||||||
@click="sendMessage"
|
@click="sendMessage"
|
||||||
|
@ -93,6 +90,12 @@ import FileUpload from 'vue-upload-component';
|
||||||
import EmojiInput from '../emoji/EmojiInput';
|
import EmojiInput from '../emoji/EmojiInput';
|
||||||
import CannedResponse from './CannedResponse';
|
import CannedResponse from './CannedResponse';
|
||||||
import ResizableTextArea from 'shared/components/ResizableTextArea';
|
import ResizableTextArea from 'shared/components/ResizableTextArea';
|
||||||
|
import {
|
||||||
|
isEscape,
|
||||||
|
isEnter,
|
||||||
|
hasPressedShift,
|
||||||
|
} from 'shared/helpers/KeyboardHelpers';
|
||||||
|
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -109,19 +112,38 @@ export default {
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
showEmojiPicker: false,
|
showEmojiPicker: false,
|
||||||
showCannedResponsesList: false,
|
showCannedResponsesList: false,
|
||||||
isUploading: {
|
isUploading: false,
|
||||||
audio: false,
|
|
||||||
video: false,
|
|
||||||
image: false,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({ currentChat: 'getSelectedChat' }),
|
||||||
currentChat: 'getSelectedChat',
|
inboxId() {
|
||||||
}),
|
return this.currentChat.inbox_id;
|
||||||
|
},
|
||||||
|
inbox() {
|
||||||
|
return this.$store.getters['inboxes/getInbox'](this.inboxId);
|
||||||
|
},
|
||||||
|
messagePlaceHolder() {
|
||||||
|
return this.isPrivate
|
||||||
|
? this.$t('CONVERSATION.FOOTER.PRIVATE_MSG_INPUT')
|
||||||
|
: this.$t('CONVERSATION.FOOTER.MSG_INPUT');
|
||||||
|
},
|
||||||
|
isMessageLengthReachingThreshold() {
|
||||||
|
return this.message.length > this.maxLength - 40;
|
||||||
|
},
|
||||||
|
characterCountIndicator() {
|
||||||
|
return `${this.message.length} / ${this.maxLength}`;
|
||||||
|
},
|
||||||
|
isReplyButtonDisabled() {
|
||||||
|
const isMessageEmpty = !this.message.replace(/\n/g, '').length;
|
||||||
|
return (
|
||||||
|
isMessageEmpty ||
|
||||||
|
this.message.length === 0 ||
|
||||||
|
this.message.length > this.maxLength
|
||||||
|
);
|
||||||
|
},
|
||||||
channelType() {
|
channelType() {
|
||||||
return this.currentChat.meta.channel;
|
return this.inbox.channel_type;
|
||||||
},
|
},
|
||||||
conversationType() {
|
conversationType() {
|
||||||
const { additional_attributes: additionalAttributes } = this.currentChat;
|
const { additional_attributes: additionalAttributes } = this.currentChat;
|
||||||
|
@ -129,18 +151,52 @@ export default {
|
||||||
return type || '';
|
return type || '';
|
||||||
},
|
},
|
||||||
maxLength() {
|
maxLength() {
|
||||||
if (this.channelType === 'Channel::FacebookPage') {
|
if (this.isPrivate) {
|
||||||
return 640;
|
return MESSAGE_MAX_LENGTH.GENERAL;
|
||||||
}
|
}
|
||||||
if (this.channelType === 'Channel::TwitterProfile') {
|
|
||||||
|
if (this.isAFacebookInbox) {
|
||||||
|
return MESSAGE_MAX_LENGTH.FACEBOOK;
|
||||||
|
}
|
||||||
|
if (this.isATwilioSMSChannel) {
|
||||||
|
return MESSAGE_MAX_LENGTH.TWILIO_SMS;
|
||||||
|
}
|
||||||
|
if (this.isATwitterInbox) {
|
||||||
if (this.conversationType === 'tweet') {
|
if (this.conversationType === 'tweet') {
|
||||||
return 280;
|
return MESSAGE_MAX_LENGTH.TWEET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 10000;
|
return MESSAGE_MAX_LENGTH.GENERAL;
|
||||||
|
},
|
||||||
|
isATwitterInbox() {
|
||||||
|
return this.channelType === 'Channel::TwitterProfile';
|
||||||
|
},
|
||||||
|
isAFacebookInbox() {
|
||||||
|
return this.channelType === 'Channel::FacebookPage';
|
||||||
|
},
|
||||||
|
isAWebWidgetInbox() {
|
||||||
|
return this.channelType === 'Channel::WebWidget';
|
||||||
|
},
|
||||||
|
isATwilioSMSChannel() {
|
||||||
|
const { phone_number: phoneNumber = '' } = this.inbox;
|
||||||
|
return (
|
||||||
|
this.channelType === 'Channel::TwilioSms' &&
|
||||||
|
!phoneNumber.startsWith('whatsapp')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isATwilioWhatsappChannel() {
|
||||||
|
const { phone_number: phoneNumber = '' } = this.inbox;
|
||||||
|
return (
|
||||||
|
this.channelType === 'Channel::TwilioSms' &&
|
||||||
|
phoneNumber.startsWith('whatsapp')
|
||||||
|
);
|
||||||
},
|
},
|
||||||
showFileUpload() {
|
showFileUpload() {
|
||||||
return this.channelType === 'Channel::WebWidget';
|
return (
|
||||||
|
this.isAWebWidgetInbox ||
|
||||||
|
this.isAFacebookInbox ||
|
||||||
|
this.isATwilioWhatsappChannel
|
||||||
|
);
|
||||||
},
|
},
|
||||||
replyButtonLabel() {
|
replyButtonLabel() {
|
||||||
if (this.isPrivate) {
|
if (this.isPrivate) {
|
||||||
|
@ -158,20 +214,18 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
message(val) {
|
message(updatedMessage) {
|
||||||
if (this.isPrivate) {
|
if (this.isPrivate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isSlashCommand = val[0] === '/';
|
const isSlashCommand = updatedMessage[0] === '/';
|
||||||
const hasNextWord = val.includes(' ');
|
const hasNextWord = updatedMessage.includes(' ');
|
||||||
const isShortCodeActive = isSlashCommand && !hasNextWord;
|
const isShortCodeActive = isSlashCommand && !hasNextWord;
|
||||||
if (isShortCodeActive) {
|
if (isShortCodeActive) {
|
||||||
this.showCannedResponsesList = true;
|
this.showCannedResponsesList = true;
|
||||||
if (val.length > 1) {
|
if (updatedMessage.length > 1) {
|
||||||
const searchKey = val.substr(1, val.length);
|
const searchKey = updatedMessage.substr(1, updatedMessage.length);
|
||||||
this.$store.dispatch('getCannedResponse', {
|
this.$store.dispatch('getCannedResponse', { searchKey });
|
||||||
searchKey,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.$store.dispatch('getCannedResponse');
|
this.$store.dispatch('getCannedResponse');
|
||||||
}
|
}
|
||||||
|
@ -188,26 +242,18 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleKeyEvents(e) {
|
handleKeyEvents(e) {
|
||||||
if (this.isEscape(e)) {
|
if (isEscape(e)) {
|
||||||
this.hideEmojiPicker();
|
this.hideEmojiPicker();
|
||||||
this.hideCannedResponse();
|
this.hideCannedResponse();
|
||||||
} else if (this.isEnter(e)) {
|
} else if (isEnter(e)) {
|
||||||
if (!e.shiftKey) {
|
if (!hasPressedShift(e)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.sendMessage();
|
this.sendMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isEnter(e) {
|
|
||||||
return e.keyCode === 13;
|
|
||||||
},
|
|
||||||
isEscape(e) {
|
|
||||||
return e.keyCode === 27; // ESCAPE
|
|
||||||
},
|
|
||||||
async sendMessage() {
|
async sendMessage() {
|
||||||
const isMessageEmpty = !this.message.replace(/\n/g, '').length;
|
if (this.isReplyButtonDisabled) {
|
||||||
if (isMessageEmpty) return;
|
|
||||||
if (this.message.length > this.maxLength) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newMessage = this.message;
|
const newMessage = this.message;
|
||||||
|
@ -231,11 +277,11 @@ export default {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}, 100);
|
}, 100);
|
||||||
},
|
},
|
||||||
makePrivate() {
|
setPrivateReplyMode() {
|
||||||
this.isPrivate = true;
|
this.isPrivate = true;
|
||||||
this.$refs.messageInput.focus();
|
this.$refs.messageInput.focus();
|
||||||
},
|
},
|
||||||
makeReply() {
|
setReplyMode() {
|
||||||
this.isPrivate = false;
|
this.isPrivate = false;
|
||||||
this.$refs.messageInput.focus();
|
this.$refs.messageInput.focus();
|
||||||
},
|
},
|
||||||
|
@ -258,7 +304,6 @@ export default {
|
||||||
hideCannedResponse() {
|
hideCannedResponse() {
|
||||||
this.showCannedResponsesList = false;
|
this.showCannedResponsesList = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
onBlur() {
|
onBlur() {
|
||||||
this.isFocused = false;
|
this.isFocused = false;
|
||||||
this.toggleTyping('off');
|
this.toggleTyping('off');
|
||||||
|
@ -267,9 +312,8 @@ export default {
|
||||||
this.isFocused = true;
|
this.isFocused = true;
|
||||||
this.toggleTyping('on');
|
this.toggleTyping('on');
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleTyping(status) {
|
toggleTyping(status) {
|
||||||
if (this.channelType === 'Channel::WebWidget' && !this.isPrivate) {
|
if (this.isAWebWidgetInbox && !this.isPrivate) {
|
||||||
const conversationId = this.currentChat.id;
|
const conversationId = this.currentChat.id;
|
||||||
this.$store.dispatch('conversationTypingStatus/toggleTyping', {
|
this.$store.dispatch('conversationTypingStatus/toggleTyping', {
|
||||||
status,
|
status,
|
||||||
|
@ -277,35 +321,19 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disableButton() {
|
|
||||||
const messageHasOnlyNewLines = !this.message.replace(/\n/g, '').length;
|
|
||||||
return (
|
|
||||||
this.message.length === 0 ||
|
|
||||||
this.message.length > 640 ||
|
|
||||||
messageHasOnlyNewLines
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
messagePlaceHolder() {
|
|
||||||
const placeHolder = this.isPrivate
|
|
||||||
? 'CONVERSATION.FOOTER.PRIVATE_MSG_INPUT'
|
|
||||||
: 'CONVERSATION.FOOTER.MSG_INPUT';
|
|
||||||
return placeHolder;
|
|
||||||
},
|
|
||||||
|
|
||||||
onFileUpload(file) {
|
onFileUpload(file) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isUploading.image = true;
|
this.isUploading = true;
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch('sendAttachment', [this.currentChat.id, { file: file.file }])
|
.dispatch('sendAttachment', [this.currentChat.id, { file: file.file }])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.isUploading.image = false;
|
this.isUploading = false;
|
||||||
this.$emit('scrollToMessage');
|
this.$emit('scrollToMessage');
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.isUploading.image = false;
|
this.isUploading = false;
|
||||||
this.$emit('scrollToMessage');
|
this.$emit('scrollToMessage');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,6 +12,10 @@ const initialSelectedChat = {
|
||||||
status: null,
|
status: null,
|
||||||
muted: false,
|
muted: false,
|
||||||
seen: false,
|
seen: false,
|
||||||
|
inbox_id: null,
|
||||||
|
additional_attributes: {
|
||||||
|
type: '',
|
||||||
|
},
|
||||||
dataFetched: false,
|
dataFetched: false,
|
||||||
};
|
};
|
||||||
const state = {
|
const state = {
|
||||||
|
|
11
app/javascript/shared/helpers/KeyboardHelpers.js
Normal file
11
app/javascript/shared/helpers/KeyboardHelpers.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export const isEnter = e => {
|
||||||
|
return e.keyCode === 13;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isEscape = e => {
|
||||||
|
return e.keyCode === 27;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hasPressedShift = e => {
|
||||||
|
return e.shiftKey;
|
||||||
|
};
|
|
@ -2,7 +2,7 @@ import { escapeHtml } from './HTMLSanitizer';
|
||||||
|
|
||||||
class MessageFormatter {
|
class MessageFormatter {
|
||||||
constructor(message) {
|
constructor(message) {
|
||||||
this.message = escapeHtml(message) || '';
|
this.message = escapeHtml(message || '') || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
formatMessage() {
|
formatMessage() {
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
export const isAFormMessage = message => message.content_type === 'form';
|
export const isAFormMessage = message => message.content_type === 'form';
|
||||||
export const isASubmittedFormMessage = (message = {}) =>
|
export const isASubmittedFormMessage = (message = {}) =>
|
||||||
isAFormMessage(message) && !!message.content_attributes?.submitted_values;
|
isAFormMessage(message) && !!message.content_attributes?.submitted_values;
|
||||||
|
|
||||||
|
export const MESSAGE_MAX_LENGTH = {
|
||||||
|
GENERAL: 10000,
|
||||||
|
FACEBOOK: 640,
|
||||||
|
TWILIO_SMS: 160,
|
||||||
|
TWEET: 280,
|
||||||
|
};
|
||||||
|
|
21
app/javascript/shared/helpers/specs/KeyboardHelpers.spec.js
Normal file
21
app/javascript/shared/helpers/specs/KeyboardHelpers.spec.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { isEnter, isEscape, hasPressedShift } from '../KeyboardHelpers';
|
||||||
|
|
||||||
|
describe('#KeyboardHelpers', () => {
|
||||||
|
describe('#isEnter', () => {
|
||||||
|
it('return correct values', () => {
|
||||||
|
expect(isEnter({ keyCode: 13 })).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#isEscape', () => {
|
||||||
|
it('return correct values', () => {
|
||||||
|
expect(isEscape({ keyCode: 27 })).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#hasPressedShift', () => {
|
||||||
|
it('return correct values', () => {
|
||||||
|
expect(hasPressedShift({ shiftKey: true })).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
15
app/jobs/send_reply_job.rb
Normal file
15
app/jobs/send_reply_job.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
class SendReplyJob < ApplicationJob
|
||||||
|
queue_as :high
|
||||||
|
|
||||||
|
def perform(message_id)
|
||||||
|
message = Message.find(message_id)
|
||||||
|
channel_name = message.conversation.inbox.channel.class.to_s
|
||||||
|
if channel_name == 'Channel::FacebookPage'
|
||||||
|
::Facebook::SendOnFacebookService.new(message: message).perform
|
||||||
|
elsif channel_name == 'Channel::TwitterProfile'
|
||||||
|
::Twitter::SendOnTwitterService.new(message: message).perform
|
||||||
|
elsif channel_name == 'Channel::TwilioSms'
|
||||||
|
::Twilio::SendOnTwilioService.new(message: message).perform
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -134,14 +134,7 @@ class Message < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_reply
|
def send_reply
|
||||||
channel_name = conversation.inbox.channel.class.to_s
|
::SendReplyJob.perform_later(id)
|
||||||
if channel_name == 'Channel::FacebookPage'
|
|
||||||
::Facebook::SendOnFacebookService.new(message: self).perform
|
|
||||||
elsif channel_name == 'Channel::TwitterProfile'
|
|
||||||
::Twitter::SendOnTwitterService.new(message: self).perform
|
|
||||||
elsif channel_name == 'Channel::TwilioSms'
|
|
||||||
::Twilio::SendOnTwilioService.new(message: self).perform
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def reopen_conversation
|
def reopen_conversation
|
||||||
|
|
|
@ -17,19 +17,26 @@ class Facebook::SendOnFacebookService < Base::SendOnChannelService
|
||||||
end
|
end
|
||||||
|
|
||||||
def fb_attachment_message_params
|
def fb_attachment_message_params
|
||||||
|
attachment = message.attachments.first
|
||||||
{
|
{
|
||||||
recipient: { id: contact.get_source_id(inbox.id) },
|
recipient: { id: contact.get_source_id(inbox.id) },
|
||||||
message: {
|
message: {
|
||||||
attachment: {
|
attachment: {
|
||||||
type: 'image',
|
type: attachment_type(attachment),
|
||||||
payload: {
|
payload: {
|
||||||
url: message.attachments.first.file_url
|
url: attachment.file_url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def attachment_type(attachment)
|
||||||
|
return attachment.file_type if %w[image audio video file].include? attachment.file_type
|
||||||
|
|
||||||
|
'file'
|
||||||
|
end
|
||||||
|
|
||||||
def fb_message_params
|
def fb_message_params
|
||||||
if message.attachments.blank?
|
if message.attachments.blank?
|
||||||
fb_text_message_params
|
fb_text_message_params
|
||||||
|
|
|
@ -56,10 +56,10 @@ title: "Supported features on channels"
|
||||||
| Channel | Channel greeting | Attachments | Agent Auto assignment | Slack |
|
| Channel | Channel greeting | Attachments | Agent Auto assignment | Slack |
|
||||||
| -- | -- | -- | -- | -- |
|
| -- | -- | -- | -- | -- |
|
||||||
| Website | Yes | Yes | Yes | Yes |
|
| Website | Yes | Yes | Yes | Yes |
|
||||||
| Facebook | Yes | No | Yes | No |
|
| Facebook | Yes | Yes | Yes | No |
|
||||||
| Twitter DM | Yes | No | Yes | No |
|
| Twitter DM | Yes | No | Yes | No |
|
||||||
| Tweets | Yes | No | Yes | No |
|
| Tweets | Yes | No | Yes | No |
|
||||||
| SMS | Yes | No | Yes | No |
|
| SMS | Yes | No | Yes | No |
|
||||||
| Whatsapp | Yes | No | Yes | No |
|
| Whatsapp | Yes | Yes | Yes | No |
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,8 +10,8 @@ FactoryBot.define do
|
||||||
|
|
||||||
after(:build) do |message|
|
after(:build) do |message|
|
||||||
message.sender ||= create(:user, account: message.account)
|
message.sender ||= create(:user, account: message.account)
|
||||||
message.conversation ||= create(:conversation, account: message.account)
|
|
||||||
message.inbox ||= create(:inbox, account: message.account)
|
message.inbox ||= create(:inbox, account: message.account)
|
||||||
|
message.conversation ||= create(:conversation, account: message.account, inbox: message.inbox)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ describe Facebook::SendOnFacebookService do
|
||||||
before do
|
before do
|
||||||
allow(Facebook::Messenger::Subscriptions).to receive(:subscribe).and_return(true)
|
allow(Facebook::Messenger::Subscriptions).to receive(:subscribe).and_return(true)
|
||||||
allow(bot).to receive(:deliver)
|
allow(bot).to receive(:deliver)
|
||||||
|
create(:message, message_type: :incoming, inbox: facebook_inbox, account: account, conversation: conversation)
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:account) { create(:account) }
|
let!(:account) { create(:account) }
|
||||||
|
@ -20,39 +21,43 @@ describe Facebook::SendOnFacebookService do
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
context 'without reply' do
|
context 'without reply' do
|
||||||
it 'if message is private' do
|
it 'if message is private' do
|
||||||
create(:message, message_type: 'outgoing', private: true, inbox: facebook_inbox, account: account)
|
message = create(:message, message_type: 'outgoing', private: true, inbox: facebook_inbox, account: account)
|
||||||
|
::Facebook::SendOnFacebookService.new(message: message).perform
|
||||||
expect(bot).not_to have_received(:deliver)
|
expect(bot).not_to have_received(:deliver)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if inbox channel is not facebook page' do
|
it 'if inbox channel is not facebook page' do
|
||||||
create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
|
message = create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
|
||||||
|
expect { ::Facebook::SendOnFacebookService.new(message: message).perform }.to raise_error 'Invalid channel service was called'
|
||||||
expect(bot).not_to have_received(:deliver)
|
expect(bot).not_to have_received(:deliver)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if message is not outgoing' do
|
it 'if message is not outgoing' do
|
||||||
create(:message, message_type: 'incoming', inbox: facebook_inbox, account: account)
|
message = create(:message, message_type: 'incoming', inbox: facebook_inbox, account: account)
|
||||||
|
::Facebook::SendOnFacebookService.new(message: message).perform
|
||||||
expect(bot).not_to have_received(:deliver)
|
expect(bot).not_to have_received(:deliver)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if message has an FB ID' do
|
it 'if message has an FB ID' do
|
||||||
create(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, source_id: SecureRandom.uuid)
|
message = create(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, source_id: SecureRandom.uuid)
|
||||||
|
::Facebook::SendOnFacebookService.new(message: message).perform
|
||||||
expect(bot).not_to have_received(:deliver)
|
expect(bot).not_to have_received(:deliver)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with reply' do
|
context 'with reply' do
|
||||||
it 'if message is sent from chatwoot and is outgoing' do
|
it 'if message is sent from chatwoot and is outgoing' do
|
||||||
create(:message, message_type: :incoming, inbox: facebook_inbox, account: account, conversation: conversation)
|
message = create(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, conversation: conversation)
|
||||||
create(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, conversation: conversation)
|
::Facebook::SendOnFacebookService.new(message: message).perform
|
||||||
expect(bot).to have_received(:deliver)
|
expect(bot).to have_received(:deliver)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if message with attachment is sent from chatwoot and is outgoing' do
|
it 'if message with attachment is sent from chatwoot and is outgoing' do
|
||||||
create(:message, message_type: :incoming, inbox: facebook_inbox, account: account, conversation: conversation)
|
|
||||||
message = build(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, conversation: conversation)
|
message = build(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, conversation: conversation)
|
||||||
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
|
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
|
||||||
attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
|
attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
|
||||||
message.save!
|
message.save!
|
||||||
|
::Facebook::SendOnFacebookService.new(message: message).perform
|
||||||
expect(bot).to have_received(:deliver)
|
expect(bot).to have_received(:deliver)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,22 +25,26 @@ describe Twilio::SendOnTwilioService do
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
context 'without reply' do
|
context 'without reply' do
|
||||||
it 'if message is private' do
|
it 'if message is private' do
|
||||||
create(:message, message_type: 'outgoing', private: true, inbox: twilio_inbox, account: account)
|
message = create(:message, message_type: 'outgoing', private: true, inbox: twilio_inbox, account: account)
|
||||||
|
::Twilio::SendOnTwilioService.new(message: message).perform
|
||||||
expect(twilio_client).not_to have_received(:messages)
|
expect(twilio_client).not_to have_received(:messages)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if inbox channel is not facebook page' do
|
it 'if inbox channel is not twilio' do
|
||||||
create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
|
message = create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
|
||||||
|
expect { ::Twilio::SendOnTwilioService.new(message: message).perform }.to raise_error 'Invalid channel service was called'
|
||||||
expect(twilio_client).not_to have_received(:messages)
|
expect(twilio_client).not_to have_received(:messages)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if message is not outgoing' do
|
it 'if message is not outgoing' do
|
||||||
create(:message, message_type: 'incoming', inbox: twilio_inbox, account: account)
|
message = create(:message, message_type: 'incoming', inbox: twilio_inbox, account: account)
|
||||||
|
::Twilio::SendOnTwilioService.new(message: message).perform
|
||||||
expect(twilio_client).not_to have_received(:messages)
|
expect(twilio_client).not_to have_received(:messages)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if message has an source id' do
|
it 'if message has an source id' do
|
||||||
create(:message, message_type: 'outgoing', inbox: twilio_inbox, account: account, source_id: SecureRandom.uuid)
|
message = create(:message, message_type: 'outgoing', inbox: twilio_inbox, account: account, source_id: SecureRandom.uuid)
|
||||||
|
::Twilio::SendOnTwilioService.new(message: message).perform
|
||||||
expect(twilio_client).not_to have_received(:messages)
|
expect(twilio_client).not_to have_received(:messages)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -53,6 +57,7 @@ describe Twilio::SendOnTwilioService do
|
||||||
outgoing_message = create(
|
outgoing_message = create(
|
||||||
:message, message_type: 'outgoing', inbox: twilio_inbox, account: account, conversation: conversation
|
:message, message_type: 'outgoing', inbox: twilio_inbox, account: account, conversation: conversation
|
||||||
)
|
)
|
||||||
|
::Twilio::SendOnTwilioService.new(message: outgoing_message).perform
|
||||||
|
|
||||||
expect(outgoing_message.reload.source_id).to eq('1234')
|
expect(outgoing_message.reload.source_id).to eq('1234')
|
||||||
end
|
end
|
||||||
|
@ -69,6 +74,8 @@ describe Twilio::SendOnTwilioService do
|
||||||
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
|
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
|
||||||
attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
|
attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
|
||||||
message.save!
|
message.save!
|
||||||
|
|
||||||
|
::Twilio::SendOnTwilioService.new(message: message).perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,22 +41,26 @@ describe Twitter::SendOnTwitterService do
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
context 'without reply' do
|
context 'without reply' do
|
||||||
it 'if inbox channel is not twitter profile' do
|
it 'if inbox channel is not twitter profile' do
|
||||||
create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
|
message = create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
|
||||||
|
expect { ::Twitter::SendOnTwitterService.new(message: message).perform }.to raise_error 'Invalid channel service was called'
|
||||||
expect(twitter_client).not_to have_received(:send_direct_message)
|
expect(twitter_client).not_to have_received(:send_direct_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if message is private' do
|
it 'if message is private' do
|
||||||
create(:message, message_type: 'outgoing', private: true, inbox: twitter_inbox, account: account)
|
message = create(:message, message_type: 'outgoing', private: true, inbox: twitter_inbox, account: account)
|
||||||
|
::Twitter::SendOnTwitterService.new(message: message).perform
|
||||||
expect(twitter_client).not_to have_received(:send_direct_message)
|
expect(twitter_client).not_to have_received(:send_direct_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if message has source_id' do
|
it 'if message has source_id' do
|
||||||
create(:message, message_type: 'outgoing', source_id: '123', inbox: widget_inbox, account: account)
|
message = create(:message, message_type: 'outgoing', source_id: '123', inbox: twitter_inbox, account: account)
|
||||||
|
::Twitter::SendOnTwitterService.new(message: message).perform
|
||||||
expect(twitter_client).not_to have_received(:send_direct_message)
|
expect(twitter_client).not_to have_received(:send_direct_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if message is not outgoing' do
|
it 'if message is not outgoing' do
|
||||||
create(:message, message_type: 'incoming', inbox: twitter_inbox, account: account)
|
message = create(:message, message_type: 'incoming', inbox: twitter_inbox, account: account)
|
||||||
|
::Twitter::SendOnTwitterService.new(message: message).perform
|
||||||
expect(twitter_client).not_to have_received(:send_direct_message)
|
expect(twitter_client).not_to have_received(:send_direct_message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -64,15 +68,17 @@ describe Twitter::SendOnTwitterService do
|
||||||
context 'with reply' do
|
context 'with reply' do
|
||||||
it 'if conversation is a direct message' do
|
it 'if conversation is a direct message' do
|
||||||
create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: dm_conversation)
|
create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: dm_conversation)
|
||||||
create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: dm_conversation)
|
message = create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: dm_conversation)
|
||||||
|
::Twitter::SendOnTwitterService.new(message: message).perform
|
||||||
expect(twitter_client).to have_received(:send_direct_message)
|
expect(twitter_client).to have_received(:send_direct_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'if conversation is a tweet' do
|
it 'if conversation is a tweet' do
|
||||||
create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: tweet_conversation)
|
create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: tweet_conversation)
|
||||||
tweet = create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: tweet_conversation)
|
message = create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: tweet_conversation)
|
||||||
|
::Twitter::SendOnTwitterService.new(message: message).perform
|
||||||
expect(twitter_client).to have_received(:send_tweet_reply)
|
expect(twitter_client).to have_received(:send_tweet_reply)
|
||||||
expect(tweet.reload.source_id).to eq '12345'
|
expect(message.reload.source_id).to eq '12345'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue