Dyte Integration V1
This commit is contained in:
parent
38587b3aa1
commit
5fca522721
17 changed files with 368 additions and 87 deletions
|
@ -0,0 +1,27 @@
|
|||
class Api::V1::Accounts::Integrations::DyteController < Api::V1::Accounts::BaseController
|
||||
before_action :fetch_conversation, only: [:create_a_meeting]
|
||||
before_action :fetch_message, only: [:add_participant_to_meeting]
|
||||
|
||||
def create_a_meeting
|
||||
dyte_processor_service.create_a_meeting(Current.user)
|
||||
head :ok
|
||||
end
|
||||
|
||||
def add_participant_to_meeting
|
||||
dyte_processor_service.create_a_meeting(Current.user)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dyte_processor_service
|
||||
Integrations::Dyte::ProcessorService.new(account: Current.account, conversation: @conversation)
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.permit(:conversation_id, :meeting_id)
|
||||
end
|
||||
|
||||
def fetch_conversation
|
||||
@conversation = Current.account.conversations.find_by(display_id: permitted_params[:conversation_id])
|
||||
end
|
||||
end
|
23
app/javascript/dashboard/api/integrations/dyte.js
Normal file
23
app/javascript/dashboard/api/integrations/dyte.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* global axios */
|
||||
|
||||
import ApiClient from '../ApiClient';
|
||||
|
||||
class DyteAPI extends ApiClient {
|
||||
constructor() {
|
||||
super('integrations/dyte', { accountScoped: true });
|
||||
}
|
||||
|
||||
createAMeeting(conversationId) {
|
||||
return axios.post(`${this.url}/create_a_meeting`, {
|
||||
conversation_id: conversationId,
|
||||
});
|
||||
}
|
||||
|
||||
addParticipantToMeeting(messageId) {
|
||||
return axios.post(this.baseUrl(), {
|
||||
message_id: messageId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new DyteAPI();
|
|
@ -87,6 +87,7 @@
|
|||
:title="'Whatsapp Templates'"
|
||||
@click="$emit('selectWhatsappTemplate')"
|
||||
/>
|
||||
<video-call :conversation-id="conversationId" />
|
||||
<transition name="modal-fade">
|
||||
<div
|
||||
v-show="$refs.upload && $refs.upload.dropActive"
|
||||
|
@ -124,13 +125,13 @@ import {
|
|||
ALLOWED_FILE_TYPES,
|
||||
ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP,
|
||||
} from 'shared/constants/messages';
|
||||
|
||||
import VideoCall from './VideoCall.vue';
|
||||
import { REPLY_EDITOR_MODES } from './constants';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'ReplyBottomPanel',
|
||||
components: { FileUpload },
|
||||
components: { FileUpload, VideoCall },
|
||||
mixins: [eventListenerMixins, uiSettingsMixin, inboxMixin],
|
||||
props: {
|
||||
mode: {
|
||||
|
@ -169,6 +170,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
conversationId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
toggleEmojiPicker: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<woot-button
|
||||
v-if="isVideoIntegrationEnabled"
|
||||
v-tooltip.top-end="'Start a new video call with the customer'"
|
||||
icon="video"
|
||||
:is-loading="isLoading"
|
||||
color-scheme="secondary"
|
||||
variant="smooth"
|
||||
size="small"
|
||||
:title="'Whatsapp Templates'"
|
||||
@click="onClick"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import DyteAPI from '../../../api/integrations/dyte';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
conversationId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return { isLoading: false };
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
appIntegrations: 'integrations/getAppIntegrations',
|
||||
}),
|
||||
isVideoIntegrationEnabled() {
|
||||
return this.appIntegrations.find(integration => {
|
||||
return integration.id === 'dyte' && !!integration.hooks.length;
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.appIntegrations.length) {
|
||||
this.$store.dispatch('integrations/get');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onClick() {
|
||||
this.isLoading = true;
|
||||
|
||||
try {
|
||||
await DyteAPI.createAMeeting(this.conversationId);
|
||||
} catch (error) {
|
||||
// Ignore Error
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,8 +1,5 @@
|
|||
<template>
|
||||
<li
|
||||
v-if="hasAttachments || data.content || isEmailContentType"
|
||||
:class="alignBubble"
|
||||
>
|
||||
<li v-if="shouldRenderMessage" :class="alignBubble">
|
||||
<div :class="wrapClass">
|
||||
<div v-tooltip.top-start="messageToolTip" :class="bubbleClass">
|
||||
<bubble-mail-head
|
||||
|
@ -18,6 +15,7 @@
|
|||
:readable-time="readableTime"
|
||||
:display-quoted-button="displayQuotedButton"
|
||||
/>
|
||||
|
||||
<span
|
||||
v-if="isPending && hasAttachments"
|
||||
class="chat-bubble has-attachment agent"
|
||||
|
@ -183,6 +181,17 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
shouldRenderMessage() {
|
||||
return (
|
||||
this.hasAttachments ||
|
||||
this.data.content ||
|
||||
this.isEmailContentType ||
|
||||
this.isAnIntegrationMessage
|
||||
);
|
||||
},
|
||||
isAnIntegrationMessage() {
|
||||
return this.data.contentType === 'integrations';
|
||||
},
|
||||
emailMessageContent() {
|
||||
const {
|
||||
html_content: { full: fullHTMLContent } = {},
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
:show-editor-toggle="isAPIInbox && !isOnPrivateNote"
|
||||
:enable-multiple-file-upload="enableMultipleFileUpload"
|
||||
:has-whatsapp-templates="hasWhatsappTemplates"
|
||||
:conversation-id="conversationId"
|
||||
@selectWhatsappTemplate="openWhatsappTemplateModal"
|
||||
@toggle-editor="toggleRichContentEditor"
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<template>
|
||||
<video-call
|
||||
v-if="contentAttributes.type === 'dyte'"
|
||||
:message-id="messageId"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import VideoCall from './VideoCall.vue';
|
||||
export default {
|
||||
components: { VideoCall },
|
||||
props: {
|
||||
messageId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
contentAttributes: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<woot-button
|
||||
size="tiny"
|
||||
variant="smooth"
|
||||
color-scheme="secondary"
|
||||
icon="save"
|
||||
@click="onClickOpenAddFoldersModal"
|
||||
>
|
||||
Join the call
|
||||
</woot-button>
|
||||
</template>
|
||||
<script></script>
|
|
@ -16,14 +16,16 @@ const state = {
|
|||
},
|
||||
};
|
||||
|
||||
const isAValidAppIntegration = integration => {
|
||||
return ['fullcontact', 'dialogflow', 'dyte'].includes(integration.id);
|
||||
};
|
||||
|
||||
export const getters = {
|
||||
getIntegrations($state) {
|
||||
return $state.records.filter(
|
||||
item => item.id !== 'fullcontact' && item.id !== 'dialogflow'
|
||||
);
|
||||
return $state.records.filter(item => !isAValidAppIntegration(item));
|
||||
},
|
||||
getAppIntegrations($state) {
|
||||
return $state.records.filter(item => item.id === 'dialogflow');
|
||||
return $state.records.filter(item => isAValidAppIntegration(item));
|
||||
},
|
||||
getIntegration: $state => integrationId => {
|
||||
const [integration] = $state.records.filter(
|
||||
|
|
|
@ -57,7 +57,8 @@ class Message < ApplicationRecord
|
|||
form: 6,
|
||||
article: 7,
|
||||
incoming_email: 8,
|
||||
input_csat: 9
|
||||
input_csat: 9,
|
||||
integrations: 10
|
||||
}
|
||||
enum status: { sent: 0, delivered: 1, read: 2, failed: 3 }
|
||||
# [:submitted_email, :items, :submitted_values] : Used for bot message types
|
||||
|
|
|
@ -30,50 +30,68 @@ dialogflow:
|
|||
action: /dialogflow
|
||||
hook_type: inbox
|
||||
allow_multiple_hooks: true
|
||||
settings_json_schema: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project_id": { "type": "string" },
|
||||
"credentials": { "type": "object" }
|
||||
},
|
||||
"required": ["project_id", "credentials"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
settings_form_schema: [
|
||||
settings_json_schema:
|
||||
{
|
||||
"label": "Dialogflow Project ID",
|
||||
"type": "text",
|
||||
"name": "project_id",
|
||||
"validation": "required",
|
||||
"validationName": 'Project Id',
|
||||
},
|
||||
{
|
||||
"label": "Dialogflow Project Key File",
|
||||
"type": "textarea",
|
||||
"name": "credentials",
|
||||
"validation": "required|JSON",
|
||||
"validationName": 'Credentials',
|
||||
"validation-messages": {
|
||||
"JSON": "Invalid JSON",
|
||||
"required": "Credentials is required"
|
||||
}
|
||||
'type': 'object',
|
||||
'properties':
|
||||
{
|
||||
'project_id': { 'type': 'string' },
|
||||
'credentials': { 'type': 'object' },
|
||||
},
|
||||
'required': ['project_id', 'credentials'],
|
||||
'additionalProperties': false,
|
||||
}
|
||||
]
|
||||
settings_form_schema:
|
||||
[
|
||||
{
|
||||
'label': 'Dialogflow Project ID',
|
||||
'type': 'text',
|
||||
'name': 'project_id',
|
||||
'validation': 'required',
|
||||
'validationName': 'Project Id',
|
||||
},
|
||||
{
|
||||
'label': 'Dialogflow Project Key File',
|
||||
'type': 'textarea',
|
||||
'name': 'credentials',
|
||||
'validation': 'required|JSON',
|
||||
'validationName': 'Credentials',
|
||||
'validation-messages':
|
||||
{ 'JSON': 'Invalid JSON', 'required': 'Credentials is required' },
|
||||
},
|
||||
]
|
||||
visible_properties: ['project_id']
|
||||
fullcontact:
|
||||
id: fullcontact
|
||||
logo: fullcontact.png
|
||||
i18n_key: fullcontact
|
||||
action: /fullcontact
|
||||
dyte:
|
||||
id: dyte
|
||||
logo: dyte.png
|
||||
i18n_key: dyte
|
||||
action: /dyte
|
||||
hook_type: account
|
||||
allow_multiple_hooks: false
|
||||
settings_json_schema:
|
||||
{
|
||||
'type': 'object',
|
||||
'properties': { 'api_key': { 'type': 'string' } },
|
||||
'required': ['api_key'],
|
||||
'properties':
|
||||
{
|
||||
'api_key': { 'type': 'string' },
|
||||
'organization_id': { 'type': 'string' },
|
||||
},
|
||||
'required': ['api_key', 'organization_id'],
|
||||
'additionalProperties': false,
|
||||
}
|
||||
settings_form_schema:
|
||||
[{ 'label': 'API Key', 'type': 'text', 'name': 'api_key',"validation": "required", }]
|
||||
visible_properties: ['api_key']
|
||||
[
|
||||
{
|
||||
'label': 'API Key',
|
||||
'type': 'text',
|
||||
'name': 'api_key',
|
||||
'validation': 'required',
|
||||
},
|
||||
{
|
||||
'label': 'Organization ID',
|
||||
'type': 'text',
|
||||
'name': 'organization_id',
|
||||
'validation': 'required',
|
||||
},
|
||||
]
|
||||
visible_properties: ['organization_id']
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
# available at https://guides.rubyonrails.org/i18n.html.
|
||||
|
||||
en:
|
||||
hello: "Hello world"
|
||||
hello: 'Hello world'
|
||||
messages:
|
||||
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
|
||||
reset_password_failure: Uh ho! We could not find any user with the specified email.
|
||||
|
@ -43,7 +43,7 @@ en:
|
|||
signup:
|
||||
disposable_email: We do not allow disposable emails
|
||||
invalid_email: You have entered an invalid email
|
||||
email_already_exists: "You have already signed up for an account with %{email}"
|
||||
email_already_exists: 'You have already signed up for an account with %{email}'
|
||||
failed: Signup failed
|
||||
data_import:
|
||||
data_type:
|
||||
|
@ -51,7 +51,7 @@ en:
|
|||
contacts:
|
||||
import:
|
||||
failed: File is blank
|
||||
email:
|
||||
email:
|
||||
invalid: Invalid email
|
||||
phone_number:
|
||||
invalid: should be in e164 format
|
||||
|
@ -105,66 +105,69 @@ en:
|
|||
|
||||
notifications:
|
||||
notification_title:
|
||||
conversation_creation: "[New conversation] - #%{display_id} has been created in %{inbox_name}"
|
||||
conversation_assignment: "[Assigned to you] - #%{display_id} has been assigned to you"
|
||||
assigned_conversation_new_message: "[New message] - #%{display_id} %{content}"
|
||||
conversation_mention: "You have been mentioned in conversation [ID - %{display_id}] by %{name}"
|
||||
conversation_creation: '[New conversation] - #%{display_id} has been created in %{inbox_name}'
|
||||
conversation_assignment: '[Assigned to you] - #%{display_id} has been assigned to you'
|
||||
assigned_conversation_new_message: '[New message] - #%{display_id} %{content}'
|
||||
conversation_mention: 'You have been mentioned in conversation [ID - %{display_id}] by %{name}'
|
||||
conversations:
|
||||
messages:
|
||||
instagram_story_content: "%{story_sender} mentioned you in the story: "
|
||||
instagram_story_content: '%{story_sender} mentioned you in the story: '
|
||||
instagram_deleted_story_content: This story is no longer available.
|
||||
deleted: This message was deleted
|
||||
activity:
|
||||
status:
|
||||
resolved: "Conversation was marked resolved by %{user_name}"
|
||||
contact_resolved: "Conversation was resolved by %{contact_name}"
|
||||
open: "Conversation was reopened by %{user_name}"
|
||||
pending: "Conversation was marked as pending by %{user_name}"
|
||||
snoozed: "Conversation was snoozed by %{user_name}"
|
||||
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
|
||||
resolved: 'Conversation was marked resolved by %{user_name}'
|
||||
contact_resolved: 'Conversation was resolved by %{contact_name}'
|
||||
open: 'Conversation was reopened by %{user_name}'
|
||||
pending: 'Conversation was marked as pending by %{user_name}'
|
||||
snoozed: 'Conversation was snoozed by %{user_name}'
|
||||
auto_resolved: 'Conversation was marked resolved by system due to %{duration} days of inactivity'
|
||||
assignee:
|
||||
self_assigned: "%{user_name} self-assigned this conversation"
|
||||
assigned: "Assigned to %{assignee_name} by %{user_name}"
|
||||
removed: "Conversation unassigned by %{user_name}"
|
||||
self_assigned: '%{user_name} self-assigned this conversation'
|
||||
assigned: 'Assigned to %{assignee_name} by %{user_name}'
|
||||
removed: 'Conversation unassigned by %{user_name}'
|
||||
team:
|
||||
assigned: "Assigned to %{team_name} by %{user_name}"
|
||||
assigned_with_assignee: "Assigned to %{assignee_name} via %{team_name} by %{user_name}"
|
||||
removed: "Unassigned from %{team_name} by %{user_name}"
|
||||
assigned: 'Assigned to %{team_name} by %{user_name}'
|
||||
assigned_with_assignee: 'Assigned to %{assignee_name} via %{team_name} by %{user_name}'
|
||||
removed: 'Unassigned from %{team_name} by %{user_name}'
|
||||
labels:
|
||||
added: "%{user_name} added %{labels}"
|
||||
removed: "%{user_name} removed %{labels}"
|
||||
muted: "%{user_name} has muted the conversation"
|
||||
unmuted: "%{user_name} has unmuted the conversation"
|
||||
added: '%{user_name} added %{labels}'
|
||||
removed: '%{user_name} removed %{labels}'
|
||||
muted: '%{user_name} has muted the conversation'
|
||||
unmuted: '%{user_name} has unmuted the conversation'
|
||||
templates:
|
||||
greeting_message_body: "%{account_name} typically replies in a few hours."
|
||||
ways_to_reach_you_message_body: "Give the team a way to reach you."
|
||||
email_input_box_message_body: "Get notified by email"
|
||||
csat_input_message_body: "Please rate the conversation"
|
||||
greeting_message_body: '%{account_name} typically replies in a few hours.'
|
||||
ways_to_reach_you_message_body: 'Give the team a way to reach you.'
|
||||
email_input_box_message_body: 'Get notified by email'
|
||||
csat_input_message_body: 'Please rate the conversation'
|
||||
reply:
|
||||
email:
|
||||
header:
|
||||
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
|
||||
reply_with_name: '%{assignee_name} from %{inbox_name} <reply+%{reply_email}>'
|
||||
email_subject: "New messages on this conversation"
|
||||
transcript_subject: "Conversation Transcript"
|
||||
email_subject: 'New messages on this conversation'
|
||||
transcript_subject: 'Conversation Transcript'
|
||||
survey:
|
||||
response: "Please rate this conversation, %{link}"
|
||||
response: 'Please rate this conversation, %{link}'
|
||||
contacts:
|
||||
online:
|
||||
delete: "%{contact_name} is Online, please try again later"
|
||||
delete: '%{contact_name} is Online, please try again later'
|
||||
integration_apps:
|
||||
dyte:
|
||||
name: 'Dyte'
|
||||
description: 'Dyte is tool that helps you to add live audio & video to your application with just a few lines of code. This integration allows you to give an option to your agents to have a video or voice call with your customers from without leaving Chatwoot.'
|
||||
slack:
|
||||
name: "Slack"
|
||||
description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack."
|
||||
name: 'Slack'
|
||||
description: 'Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack.'
|
||||
webhooks:
|
||||
name: "Webhooks"
|
||||
name: 'Webhooks'
|
||||
description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks."
|
||||
dialogflow:
|
||||
name: "Dialogflow"
|
||||
description: "Build chatbots using Dialogflow and connect them to your inbox quickly. Let the bots handle the queries before handing them off to a customer service agent."
|
||||
name: 'Dialogflow'
|
||||
description: 'Build chatbots using Dialogflow and connect them to your inbox quickly. Let the bots handle the queries before handing them off to a customer service agent.'
|
||||
fullcontact:
|
||||
name: "Fullcontact"
|
||||
description: "FullContact integration helps to enrich visitor profiles. Identify the users as soon as they share their email address and offer them tailored customer service. Connect your FullContact to your account by sharing the FullContact API Key."
|
||||
name: 'Fullcontact'
|
||||
description: 'FullContact integration helps to enrich visitor profiles. Identify the users as soon as they share their email address and offer them tailored customer service. Connect your FullContact to your account by sharing the FullContact API Key.'
|
||||
public_portal:
|
||||
search:
|
||||
search_placeholder: Search for article by title or body...
|
||||
|
|
|
@ -158,6 +158,12 @@ Rails.application.routes.draw do
|
|||
resources :apps, only: [:index, :show]
|
||||
resources :hooks, only: [:create, :update, :destroy]
|
||||
resource :slack, only: [:create, :update, :destroy], controller: 'slack'
|
||||
resource :dyte, controller: 'dyte' do
|
||||
collection do
|
||||
post :create_a_meeting
|
||||
post :add_participant_to_meeting
|
||||
end
|
||||
end
|
||||
end
|
||||
resources :working_hours, only: [:update]
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class CsmlEngine
|
|||
def process_response(response)
|
||||
return response.parsed_response if response.success?
|
||||
|
||||
{ error: response.parsed_response, status: response.code }
|
||||
{ error: response.parsed_response, status: response.code }.to_h
|
||||
end
|
||||
|
||||
def post(path, payload)
|
||||
|
|
56
lib/dyte.rb
Normal file
56
lib/dyte.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
class Dyte
|
||||
BASE_URL = 'https://api.cluster.dyte.in/v1'.freeze
|
||||
API_KEY_HEADER = 'Authorization'.freeze
|
||||
|
||||
def initialize(api_key, organization_id)
|
||||
@api_key = api_key
|
||||
@organization_id = organization_id
|
||||
end
|
||||
|
||||
def create_a_meeting(title)
|
||||
payload = {
|
||||
'title': title,
|
||||
'presetName': 'AV_Participant',
|
||||
'authorization': {
|
||||
'waitingRoom': false,
|
||||
'closed': false
|
||||
},
|
||||
'recordOnStart': false,
|
||||
'liveStreamOnStart': false
|
||||
}
|
||||
path = "organizations/#{@organization_id}/meeting"
|
||||
response = post(path, payload)
|
||||
process_response(response)
|
||||
end
|
||||
|
||||
def add_participant_to_meeting(meeting_id, client_id, name, avatar_url)
|
||||
payload = {
|
||||
'clientSpecificId': client_id,
|
||||
'userDetails': {
|
||||
'name': name,
|
||||
'picture': avatar_url
|
||||
},
|
||||
'presetName': 'AV_Participant'
|
||||
}
|
||||
path = "organizations/#{@organization_id}/meetings/#{meeting_id}/participant"
|
||||
response = post(path, payload)
|
||||
process_response(response)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_response(response)
|
||||
return response.parsed_response if response.success?
|
||||
|
||||
{ error: response.parsed_response, status: response.code }
|
||||
end
|
||||
|
||||
def post(path, payload)
|
||||
HTTParty.post(
|
||||
"#{BASE_URL}/#{path}", {
|
||||
headers: { API_KEY_HEADER => @api_key, 'Content-Type' => 'application/json' },
|
||||
body: payload.to_json
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
39
lib/integrations/dyte/processor_service.rb
Normal file
39
lib/integrations/dyte/processor_service.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
class Integrations::Dyte::ProcessorService
|
||||
pattr_initialize [:account!, :conversation!]
|
||||
|
||||
def create_a_meeting(agent)
|
||||
response = dyte_client.create_a_meeting("Meeting with #{agent.available_name}")
|
||||
|
||||
return if response[:error].present?
|
||||
|
||||
meeting = response['data']['meeting']
|
||||
@conversation.messages.create(
|
||||
{
|
||||
account_id: conversation.account_id,
|
||||
inbox_id: conversation.inbox_id,
|
||||
message_type: :outgoing,
|
||||
content_type: :integrations,
|
||||
content_attributes: {
|
||||
type: 'dyte',
|
||||
data: { meeting_id: meeting['id'] }
|
||||
},
|
||||
sender: agent
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def add_participant_to_meeting(meeting_id, user)
|
||||
dyte_client.add_participant_to_meeting(meeting_id, user.id, user.name, user.avatar_url)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dyte_hook
|
||||
@dyte_hook ||= account.hooks.find_by!(app_id: 'dyte')
|
||||
end
|
||||
|
||||
def dyte_client
|
||||
credentials = dyte_hook.settings
|
||||
@dyte_client ||= Dyte.new(credentials['api_key'], credentials['organization_id'])
|
||||
end
|
||||
end
|
BIN
public/dashboard/images/integrations/dyte.png
Normal file
BIN
public/dashboard/images/integrations/dyte.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
Loading…
Reference in a new issue