Skip to content

Commit

Permalink
fix(web): download transcript config
Browse files Browse the repository at this point in the history
  • Loading branch information
slvnperron committed Aug 3, 2018
2 parents 7a57186 + ee8ec8a commit fe1a1c4
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 7 deletions.
3 changes: 2 additions & 1 deletion docs/recipes/embedding.md
Expand Up @@ -63,6 +63,7 @@ window.botpressWebChat.init({
foregroundColor: '#0176ff', // Element background color (header, composer, button..)
textColorOnForeground: '#ffffff', // Element text color (header, composer, button..)
showUserName: false, // Whether or not to show the user's name
showUserAvatar: false // Whether or not to show the user's avatar
showUserAvatar: false, // Whether or not to show the user's avatar
enableTranscriptDownload: false // Whether or not to show the transcript download button
})
```
6 changes: 4 additions & 2 deletions packages/channels/botpress-channel-web/README.md
Expand Up @@ -248,7 +248,8 @@ const config = {
foregroundColor: '#000000',
textColorOnForeground: '#ffffff',
showUserName: false,
showUserAvatar: false
showUserAvatar: false,
enableTranscriptDownload: false
}

bp.createShortlink('chat', '/lite', {
Expand Down Expand Up @@ -287,7 +288,8 @@ window.botpressWebChat.init({
foregroundColor: '#0176ff', // Element background color (header, composer, button..)
textColorOnForeground: '#ffffff', // Element text color (header, composer, button..)
showUserName: false, // Whether or not to show the user's name
showUserAvatar: false // Whether or not to show the user's avatar
showUserAvatar: false, // Whether or not to show the user's avatar
enableTranscriptDownload: false // Whether or not to show the transcript download button
})
```

Expand Down
47 changes: 46 additions & 1 deletion packages/channels/botpress-channel-web/src/api.js
Expand Up @@ -3,6 +3,7 @@ import path from 'path'
import multer from 'multer'
import multers3 from 'multer-s3'
import aws from 'aws-sdk'
import moment from 'moment'

import injectScript from 'raw-loader!./inject.js'
import injectStyle from 'raw-loader!./inject.css'
Expand Down Expand Up @@ -75,7 +76,7 @@ module.exports = async (bp, config) => {

const { listConversations, getConversation, appendUserMessage, getOrCreateRecentConversation } = db(knex, config)

const { getOrCreateUser } = await users(bp, config)
const { getOrCreateUser, getUserProfile } = await users(bp, config)

const router = bp.getRouter('botpress-platform-webchat', { auth: false })

Expand Down Expand Up @@ -300,5 +301,49 @@ module.exports = async (bp, config) => {
}
})

const getMessageContent = message => {
switch (message.message_type) {
case 'file':
return message.message_data.url
case 'text':
return message.message_text
default:
return `Event (${message.message_type})`
}
}

const convertToTxtFile = async conversation => {
const { messages } = conversation
const user = await getUserProfile(conversation.userId)
const timeFormat = 'MM/DD/YY HH:mm'

const metadata = `Title: ${conversation.title}\r\nCreated on: ${moment(conversation.created_on).format(
timeFormat
)}\r\nUser: ${user.first_name} ${user.last_name}\r\n-----------------\r\n`

const messagesAsTxt = messages.map(message => {
if (message.message_type === 'session_reset') {
return ''
}

return `[${moment(message.sent_on).format(timeFormat)}] ${message.full_name}: ${getMessageContent(message)}\r\n`
})

return [metadata, ...messagesAsTxt].join('')
}

router.get('/conversations/:userId/:conversationId/download/txt', async (req, res) => {
const { userId, conversationId } = req.params || {}

if (!validateUserId(userId)) {
return res.status(400).send(ERR_USER_ID_REQ)
}

const conversation = await getConversation(userId, conversationId)
const txt = await convertToTxtFile(conversation)

res.send({ txt, name: `${conversation.title}.txt` })
})

return router
}
16 changes: 15 additions & 1 deletion packages/channels/botpress-channel-web/src/users.js
Expand Up @@ -26,6 +26,20 @@ module.exports = async bp => {
return getOrCreateUser(realUserId, true)
}

async function getUserProfile(userId) {
const realUserId = userId.startsWith('webchat:') ? userId.substr(8) : userId

const user = await knex('users')
.where({
platform: 'webchat',
userId: realUserId
})
.then()
.get(0)

return user
}

function createNewUser(userId) {
const [first_name, last_name] = sillyname().split(' ')

Expand All @@ -38,5 +52,5 @@ module.exports = async bp => {
})
}

return { getOrCreateUser }
return { getOrCreateUser, getUserProfile }
}
3 changes: 2 additions & 1 deletion packages/channels/botpress-channel-web/src/views/index.jsx
Expand Up @@ -26,7 +26,8 @@ export class WebBotpressUIInjection extends React.Component {
hideWidget: true,
botConvoTitle: 'Bot Emulator',
botConvoDescription: 'Test your bot live',
enableReset: true
enableReset: true,
enableTranscriptDownload: true
})

window.document.body.appendChild(script)
Expand Down
28 changes: 27 additions & 1 deletion packages/channels/botpress-channel-web/src/views/web/index.jsx
Expand Up @@ -39,7 +39,8 @@ const defaultOptions = {
enableReset: false,
showUserName: false,
showUserAvatar: false,
botConvoTitle: 'Botpress Webchat'
botConvoTitle: 'Botpress Webchat',
enableTranscriptDownload: false
}

export default class Web extends React.Component {
Expand Down Expand Up @@ -509,6 +510,30 @@ export default class Web extends React.Component {
)
}

downaloadFile(name, blob) {
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')

link.href = url
link.setAttribute('download', name)

document.body.appendChild(link)
link.click()

document.body.removeChild(link)
window.URL.revokeObjectURL(url)
}

downloadConversation = async () => {
const userId = window.__BP_VISITOR_ID
const url = `${BOT_HOSTNAME}/api/botpress-platform-webchat/conversations/${userId}/${this.state
.currentConversationId}/download/txt`
const file = (await this.props.bp.axios.get(url)).data
const blobFile = new Blob([file.txt])

this.downaloadFile(file.name, blobFile)
}

renderSide() {
return (
<Side
Expand All @@ -530,6 +555,7 @@ export default class Web extends React.Component {
onFileUploadSend={this.handleFileUploadSend}
onLoginPromptSend={this.handleLoginPrompt}
onSendData={this.handleSendData}
downloadConversation={this.downloadConversation}
/>
)
}
Expand Down
Expand Up @@ -147,6 +147,30 @@ export default class Side extends React.Component {
)
}

renderDownloadButton() {
if (!this.props.config.enableTranscriptDownload) {
return null
}

return (
<span className={style.downloadIcon}>
<i onClick={this.props.downloadConversation}>
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="17"
height="17"
className={style.downloadSVG}
viewBox="0 0 32 32"
>
<title>Download</title>
<path d="M27.414 19.414l-10 10c-0.781 0.781-2.047 0.781-2.828 0l-10-10c-0.781-0.781-0.781-2.047 0-2.828s2.047-0.781 2.828 0l6.586 6.586v-19.172c0-1.105 0.895-2 2-2s2 0.895 2 2v19.172l6.586-6.586c0.39-0.39 0.902-0.586 1.414-0.586s1.024 0.195 1.414 0.586c0.781 0.781 0.781 2.047 0 2.828z" />
</svg>
</i>
</span>
)
}

renderHeader() {
const status = (
<div className={style.status}>
Expand All @@ -166,6 +190,7 @@ export default class Side extends React.Component {
</div>
</div>
{this.renderResetButton()}
{this.renderDownloadButton()}
{this.renderConvoButton()}
{this.renderCloseButton()}
</div>
Expand Down
Expand Up @@ -152,6 +152,20 @@
fill: currentColor;
}
}

.downloadIcon {
margin-left: 22px;
}

.downloadSVG {
fill: currentColor;
cursor: pointer;
color: #bbb;

&:hover {
color: #aaa;
}
}
}

.conversation {
Expand Down

0 comments on commit fe1a1c4

Please sign in to comment.