Skip to content

Commit dac5e3e

Browse files
authored
fix(hitl): add support for all types of messages (#5736)
* fix(hitl): add support for all types of messages * use csstype v2 * fix card display and agent messages * removed some temp hack + refactoring
1 parent 8c6adff commit dac5e3e

File tree

6 files changed

+401
-164
lines changed

6 files changed

+401
-164
lines changed

modules/hitl/assets/default.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@
243243
}
244244

245245
.bph-chat-bubble {
246-
max-width: 60%;
246+
max-width: 50%;
247247

248248
border: 1px solid #f1f1f1;
249249
background-color: rgba(255, 255, 255, 0.9);
@@ -254,6 +254,14 @@
254254
word-break: break-word;
255255
}
256256

257+
.bph-chat-bubble.card {
258+
min-width: 50%;
259+
}
260+
261+
.bph-chat-bubble p {
262+
margin: 0px;
263+
}
264+
257265
.bph-chat-bubble img {
258266
max-height: 240px;
259267
max-width: 100%;

modules/hitl/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@
2626
"@types/react-bootstrap": "^0.32.19"
2727
},
2828
"dependencies": {
29+
"@botpress/messaging-components": "^0.0.8",
2930
"bluebird": "^3.5.3",
3031
"classnames": "^2.2.6",
32+
"csstype": "^2.2.0",
3133
"lodash": "^4.17.19",
32-
"mime": "^2.4.4",
33-
"moment": "^2.24.0",
34-
"react-audio-player": "^0.11.0"
34+
"moment": "^2.24.0"
3535
},
3636
"resolutions": {
3737
"fstream": ">=1.0.12",

modules/hitl/src/backend/db.ts

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -159,42 +159,6 @@ export default class HitlDb {
159159
}
160160
}
161161

162-
formatMessage = event => {
163-
// Convert messenger payloads to HITL-compatible format
164-
if (event.channel === 'messenger' && event.payload.quick_replies) {
165-
return {
166-
type: 'custom',
167-
raw_message: {
168-
type: 'custom',
169-
module: 'channel-messenger',
170-
component: 'QuickReplies',
171-
quick_replies: event.payload.quick_replies,
172-
wrapped: { type: 'text', ..._.omit(event.payload, 'quick_replies') }
173-
}
174-
}
175-
} else if (event.channel === 'messenger' && _.get(event.payload, 'attachment.payload.elements')) {
176-
return {
177-
type: 'carousel',
178-
raw_message: {
179-
text: ' ',
180-
type: 'carousel',
181-
elements: _.get(event.payload, 'attachment.payload.elements').map(card => ({
182-
title: card.title,
183-
picture: card.image_url,
184-
subtitle: card.subtitle,
185-
buttons: card.buttons.map(a => ({
186-
...a,
187-
type: a.type === 'web_url' ? 'open_url' : a.type
188-
}))
189-
})),
190-
fromMessenger: true
191-
}
192-
}
193-
}
194-
195-
return { type: event.type, raw_message: event.payload }
196-
}
197-
198162
async appendMessageToSession(event: sdk.IO.Event, sessionId: string, direction: string) {
199163
const payload = event.payload || {}
200164
const text = event.preview || payload.text || (payload.wrapped && payload.wrapped.text)
@@ -214,10 +178,6 @@ export default class HitlDb {
214178
ts: new Date()
215179
}
216180

217-
const { type, raw_message } = this.formatMessage(event)
218-
message.type = type
219-
message.raw_message = raw_message
220-
221181
return Bluebird.join(
222182
this.knex(TABLE_NAME_MESSAGES).insert({
223183
...message,
Lines changed: 25 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,33 @@
1+
import ReactMessageRenderer, { defaultMessageConfig } from '@botpress/messaging-components'
12
import classnames from 'classnames'
23
import _ from 'lodash'
3-
import mimeTypes from 'mime/lite'
44
import moment from 'moment'
5-
import path from 'path'
65
import React from 'react'
7-
import ReactAudioPlayer from 'react-audio-player'
86

97
import { Message as HitlMessage } from '../../../../backend/typings'
108
import SVGIcon from '../SVGIcon'
119

12-
const validMessageTypes = [
13-
'text',
14-
'message',
15-
'image',
16-
'video',
17-
'audio',
18-
'quick_reply',
19-
'custom',
20-
'visit',
21-
'file',
22-
'carousel'
23-
]
24-
25-
const getComponent = (moduleName: string, componentName: string) =>
26-
window.botpress[moduleName] && window.botpress[moduleName][componentName]
27-
2810
export default class Message extends React.Component<{ message: HitlMessage }> {
29-
renderFile() {
30-
const { type, raw_message, text } = this.props.message
31-
32-
const url = _.get(raw_message, 'url', text)
33-
const name = _.get(raw_message, 'name', '')
34-
35-
const extension = path.extname(url)
36-
const mime = mimeTypes.getType(extension)
37-
38-
if (type === 'image' || mime.includes('image/')) {
39-
return (
40-
<a href={url} target={'_blank'}>
41-
<img src={url} title={name} />
42-
</a>
43-
)
44-
} else if (type === 'audio' || mime.includes('audio/')) {
45-
return <ReactAudioPlayer src={this.props.message.text} />
46-
} else if (type === 'video' || mime.includes('video/')) {
47-
return (
48-
<video controls>
49-
<source src={this.props.message.text} type="video/mp4" />
50-
</video>
51-
)
52-
}
53-
54-
return <p>Unsupported media</p>
55-
}
56-
57-
renderText(text?: string) {
58-
const Text = getComponent('channel-web', 'Text')
59-
return Text ? <Text markdown={true} text={text || this.props.message.text} /> : <span>{text}</span>
60-
}
61-
62-
renderDropdown() {
63-
const Dropdown = getComponent('channel-web', 'Dropdown')
64-
return Dropdown ? (
65-
<Dropdown isLastGroup={true} isLastOfGroup={true} options={this.props.message.raw_message.options} />
66-
) : null
67-
}
68-
69-
renderCarousel() {
70-
const Carousel = getComponent('channel-web', 'Carousel')
71-
return Carousel ? (
72-
<Carousel style={{ maxWidth: '400px' }} carousel={this.props.message.raw_message} />
73-
) : (
74-
<span>Could not display carousel</span>
75-
)
76-
}
77-
78-
renderQuickReply(quickReplies) {
79-
const Keyboard = getComponent('channel-web', 'Keyboard')
80-
const QuickReplies = getComponent('channel-web', 'QuickReplies')
81-
82-
return Keyboard && QuickReplies ? (
83-
<span>
84-
<div style={{ paddingBottom: 10 }}>{this.renderText()}</div>
85-
<Keyboard.Default />
86-
<QuickReplies quick_replies={quickReplies} isLastGroup={true} isLastOfGroup={true} />
87-
</span>
88-
) : (
89-
<span>Could not display quick replies</span>
90-
)
91-
}
92-
9311
renderContent() {
94-
const { type, raw_message } = this.props.message
95-
96-
if (type === 'message' || type === 'text' || type === 'quick_reply') {
97-
return this.renderText()
98-
} else if (type === 'image' || type === 'file' || type === 'video' || type === 'audio') {
99-
return this.renderFile()
100-
} else if (type === 'custom' && raw_message) {
101-
if (raw_message.component === 'Dropdown' && raw_message.options) {
102-
return this.renderDropdown()
103-
} else if (raw_message.component === 'QuickReplies' && raw_message.quick_replies) {
104-
return this.renderQuickReply(raw_message.quick_replies)
105-
}
12+
let { raw_message: payload } = this.props.message
13+
const { ts, source } = this.props.message
10614

107-
return (
108-
<span>
109-
Bot sent custom component: [Module: {raw_message.module}, Component: {raw_message.component}]
110-
</span>
111-
)
112-
} else if (type === 'carousel') {
113-
return this.renderCarousel()
15+
if (!payload.type) {
16+
payload = { ...payload, type: 'text' }
11417
}
11518

116-
return <span>Cannot display this message</span>
19+
return (
20+
<ReactMessageRenderer
21+
content={payload}
22+
config={{
23+
...defaultMessageConfig,
24+
isBotMessage: source !== 'user',
25+
isLastGroup: false,
26+
isLastOfGroup: false,
27+
sentOn: ts
28+
}}
29+
/>
30+
)
11731
}
11832

11933
renderMessage() {
@@ -122,12 +36,19 @@ export default class Message extends React.Component<{ message: HitlMessage }> {
12236

12337
let messageFrom = 'bot'
12438
if (direction === 'in') {
39+
// TODO: Visit and session reset are currently unsupported by @botpress/messaging-components
12540
if (type === 'visit') {
12641
return (
12742
<div className={classnames('bph-message', 'bph-from-system')}>
12843
<p>User visit: {date}</p>
12944
</div>
13045
)
46+
} else if (type === 'session_reset') {
47+
return (
48+
<div className={classnames('bph-message', 'bph-from-system')}>
49+
<p>Reset the conversation</p>
50+
</div>
51+
)
13152
}
13253
messageFrom = 'user'
13354
} else if (source === 'agent') {
@@ -150,18 +71,14 @@ export default class Message extends React.Component<{ message: HitlMessage }> {
15071
>
15172
{messageFrom === 'user' && avatar}
15273
<div className="bph-message-container">
153-
<div className="bph-chat-bubble">{this.renderContent()}</div>
74+
<div className={classnames('bph-chat-bubble', { ['card']: type === 'card' })}>{this.renderContent()}</div>
15475
</div>
15576
{messageFrom !== 'user' && avatar}
15677
</div>
15778
)
15879
}
15980

16081
render() {
161-
if (!validMessageTypes.includes(this.props.message.type)) {
162-
return null
163-
}
164-
16582
return this.renderMessage()
16683
}
16784
}

modules/hitl/src/views/full/components/messages/MessageList.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import _ from 'lodash'
22
import moment from 'moment'
33
import React, { FC } from 'react'
4-
import { renderPayload } from '../../../../../../../packages/ui-shared-lite/Payloads'
54

65
import { Message as HitlMessage } from '../../../../backend/typings'
76

@@ -26,7 +25,7 @@ class MessageWrapper extends React.Component<{ message: any }> {
2625
return <p className="bph-chat-error">* Cannot display message *</p>
2726
}
2827

29-
return <Message message={{ ...this.props.message, raw_message: renderPayload(this.props.message.raw_message) }} />
28+
return <Message message={this.props.message} />
3029
}
3130
}
3231

0 commit comments

Comments
 (0)