Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webchat): builtin feedback event #358

Merged
merged 6 commits into from
Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/client/src/base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { uuid } from '@botpress/messaging-base'
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
import { ConversationStartedEvent, MessageNewEvent, UserNewEvent } from '.'
import { ConversationStartedEvent, MessageFeedbackEvent, MessageNewEvent, UserNewEvent } from '.'
import { MessagingClientAuth } from './auth'
import { Emitter } from './emitter'
import { Logger } from './logger'
Expand All @@ -10,6 +10,7 @@ export abstract class MessagingChannelBase extends Emitter<{
user: UserNewEvent
started: ConversationStartedEvent
message: MessageNewEvent
feedback: MessageFeedbackEvent
}> {
public get options() {
return this._options
Expand Down
7 changes: 7 additions & 0 deletions packages/client/src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ export class MessagingChannel extends MessagingChannelApi {
}

await this.emit('user', clientId, data)
} else if (type === 'message.feedback') {
const { error } = Schemas.MessageFeedback.validate(data)
if (error) {
return res.status(400).send(error.message)
}

await this.emit('feedback', clientId, data)
}

res.sendStatus(200)
Expand Down
3 changes: 3 additions & 0 deletions packages/client/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Conversation, HealthReport, Message, SyncRequest, SyncResult, User, uuid } from '@botpress/messaging-base'
import { AxiosRequestConfig } from 'axios'
import { Router } from 'express'
import { MessageFeedbackEvent } from '.'
import { MessagingChannel } from './channel'
import { ProtectedEmitter } from './emitter'
import { ConversationStartedEvent, MessageNewEvent, UserNewEvent } from './events'
Expand All @@ -10,6 +11,7 @@ export class MessagingClient extends ProtectedEmitter<{
user: UserNewEvent
started: ConversationStartedEvent
message: MessageNewEvent
feedback: MessageFeedbackEvent
}> {
public get options() {
return this._options
Expand Down Expand Up @@ -68,6 +70,7 @@ export class MessagingClient extends ProtectedEmitter<{
this.channel.on('user', async (_, e) => this.emit('user', e))
this.channel.on('started', async (_, e) => this.emit('started', e))
this.channel.on('message', async (_, e) => this.emit('message', e))
this.channel.on('feedback', async (_, e) => this.emit('feedback', e))

this._options = options
this.applyOptions()
Expand Down
8 changes: 8 additions & 0 deletions packages/client/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ export interface MessageNewEvent extends ChannelEvent {
export interface ConversationStartedEvent extends ChannelEvent {
channel: string
}

export interface MessageEvent extends ChannelEvent {
messageId: Message
}

export interface MessageFeedbackEvent extends MessageEvent {
feedback: number
}
27 changes: 19 additions & 8 deletions packages/client/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,47 @@ import joi from 'joi'
export const Schemas = {
UserNew: joi
.object({
userId: joi.string().guid().required()
userId: joi.string().uuid().required()
})
.required()
.options({ stripUnknown: true }) as any,

ConversationStarted: joi
.object({
userId: joi.string().guid().required(),
conversationId: joi.string().guid().required(),
userId: joi.string().uuid().required(),
conversationId: joi.string().uuid().required(),
channel: joi.string().required()
})
.required()
.options({ stripUnknown: true }) as any,

MessageNew: joi
.object({
userId: joi.string().guid().required(),
conversationId: joi.string().guid().required(),
userId: joi.string().uuid().required(),
conversationId: joi.string().uuid().required(),
channel: joi.string().required(),
collect: joi.boolean().optional(),
message: joi
.object({
id: joi.string().guid().required(),
conversationId: joi.string().guid().required(),
authorId: joi.string().guid().required(),
id: joi.string().uuid().required(),
conversationId: joi.string().uuid().required(),
authorId: joi.string().uuid().required(),
sentOn: joi.date().required(),
payload: joi.object().required()
})
.required()
})
.required()
.options({ stripUnknown: true }) as any,

MessageFeedback: joi
.object({
userId: joi.string().uuid().required(),
conversationId: joi.string().uuid().required(),
channel: joi.string().required(),
messageId: joi.string().uuid().required(),
feedback: joi.number().allow(1, -1)
})
.required()
.options({ stripUnknown: true }) as any
}
10 changes: 9 additions & 1 deletion packages/server/src/conversations/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Conversation, Emitter, uuid } from '@botpress/messaging-base'

export enum ConversationEvents {
Created,
Started
Started,
Visit
}

export interface ConversationCreatedEvent {
Expand All @@ -13,9 +14,16 @@ export interface ConversationStartedEvent {
conversationId: uuid
}

export interface ConversationVisitEvent {
conversationId: uuid
timezone: number
locale: string
}

export class ConversationEmitter extends Emitter<{
[ConversationEvents.Created]: ConversationCreatedEvent
[ConversationEvents.Started]: ConversationStartedEvent
[ConversationEvents.Visit]: ConversationVisitEvent
}> {}

export type ConversationWatcher = Omit<ConversationEmitter, 'emit'>
8 changes: 7 additions & 1 deletion packages/server/src/conversations/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ const Socket = {

Delete: Joi.object({
id: Joi.string().guid().required()
}).required()
}).required(),

Visit: Joi.object({
id: Joi.string().guid().required(),
timezone: Joi.number().required(),
locale: Joi.string().required()
})
}

export const Schema = { Api, Socket }
4 changes: 4 additions & 0 deletions packages/server/src/conversations/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ export class ConversationService extends Service {
await this.emitter.emit(ConversationEvents.Started, { conversationId: id })
}

public async visit(id: uuid, timezone: number, locale: string): Promise<void> {
await this.emitter.emit(ConversationEvents.Visit, { conversationId: id, timezone, locale })
}

public async delete(id: uuid): Promise<number> {
await this.batcher.flush()
this.cache.del(id, true)
Expand Down
21 changes: 16 additions & 5 deletions packages/server/src/conversations/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class ConversationSocket {
this.sockets.handle('conversations.get', Schema.Socket.Get, this.get.bind(this))
this.sockets.handle('conversations.list', Schema.Socket.List, this.list.bind(this))
this.sockets.handle('conversations.delete', Schema.Socket.Delete, this.delete.bind(this))
this.sockets.handle('conversations.visit', Schema.Socket.Visit, this.visit.bind(this))
}

async create(socket: SocketRequest) {
Expand All @@ -25,7 +26,7 @@ export class ConversationSocket {
const conversation = await this.conversations.fetch(id)

if (!conversation || conversation.userId !== socket.userId) {
return socket.reply(undefined)
return socket.notFound('Conversation does not exist')
}

socket.reply(conversation)
Expand All @@ -44,13 +45,23 @@ export class ConversationSocket {
const { id } = socket.data
const conversation = await this.conversations.fetch(id)

if (!conversation) {
return socket.reply(false)
} else if (conversation.userId !== socket.userId) {
return socket.forbid('Conversation does not belong to user')
if (!conversation || conversation.userId !== socket.userId) {
return socket.forbid('Conversation does not exist')
}

await this.conversations.delete(id)
socket.reply(true)
}

async visit(socket: SocketRequest) {
const { id, timezone, locale } = socket.data
const conversation = await this.conversations.fetch(id)

if (!conversation || conversation.userId !== socket.userId) {
return socket.forbid('Conversation does not exist')
}

await this.conversations.visit(id, timezone, locale)
socket.reply(true)
}
}
17 changes: 16 additions & 1 deletion packages/server/src/conversations/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { uuid } from '@botpress/messaging-base'
import { Streamer } from '../base/streamer'
import { ChannelService } from '../channels/service'
import { MappingService } from '../mapping/service'
import { ConversationCreatedEvent, ConversationEvents, ConversationStartedEvent } from './events'
import {
ConversationCreatedEvent,
ConversationEvents,
ConversationStartedEvent,
ConversationVisitEvent
} from './events'
import { ConversationService } from './service'

export class ConversationStream {
Expand All @@ -16,6 +21,7 @@ export class ConversationStream {
async setup() {
this.conversations.events.on(ConversationEvents.Created, this.handleConversationCreated.bind(this))
this.conversations.events.on(ConversationEvents.Started, this.handleConversationStarted.bind(this))
this.conversations.events.on(ConversationEvents.Visit, this.handleConversationVisit.bind(this))
}

private async handleConversationCreated({ conversation }: ConversationCreatedEvent) {
Expand All @@ -36,6 +42,15 @@ export class ConversationStream {
)
}

private async handleConversationVisit({ conversationId, timezone, locale }: ConversationVisitEvent) {
const conversation = await this.conversations.get(conversationId)
await this.streamer.stream(
'conversation.visit',
{ userId: conversation.userId, conversationId, channel: await this.getChannel(conversationId), timezone, locale },
conversation.clientId
)
}

private async getChannel(conversationId: uuid) {
const convmaps = await this.mapping.convmap.listByConversationId(conversationId)
if (convmaps.length === 1) {
Expand Down
11 changes: 9 additions & 2 deletions packages/server/src/messages/events.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { Emitter, Message } from '@botpress/messaging-base'
import { Emitter, Message, uuid } from '@botpress/messaging-base'
import { ActionSource } from '../base/source'

export enum MessageEvents {
Created
Created,
Feedback
}

export interface MessageCreatedEvent {
message: Message
source?: ActionSource
}

export interface MessageFeedbackEvent {
messageId: uuid
feedback: number
}

export class MessageEmitter extends Emitter<{
[MessageEvents.Created]: MessageCreatedEvent
[MessageEvents.Feedback]: MessageFeedbackEvent
}> {}

export type MessageWatcher = Omit<MessageEmitter, 'emit'>
5 changes: 5 additions & 0 deletions packages/server/src/messages/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ const Socket = {
List: Joi.object({
conversationId: Joi.string().guid().required(),
limit: Joi.number().required()
}).required(),

Feedback: Joi.object({
messageId: Joi.string().guid().required(),
feedback: Joi.number().allow(-1, 1)
}).required()
}

Expand Down
4 changes: 4 additions & 0 deletions packages/server/src/messages/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export class MessageService extends Service {
return message
}

public async feedback(id: uuid, feedback: number) {
await this.emitter.emit(MessageEvents.Feedback, { messageId: id, feedback })
}

public async delete(id: uuid): Promise<number> {
await this.batcher.flush()
this.cache.del(id, true)
Expand Down
18 changes: 18 additions & 0 deletions packages/server/src/messages/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class MessageSocket {
setup() {
this.sockets.handle('messages.create', Schema.Socket.Create, this.create.bind(this))
this.sockets.handle('messages.list', Schema.Socket.List, this.list.bind(this))
this.sockets.handle('messages.feedback', Schema.Socket.Feedback, this.feedback.bind(this))
}

async create(socket: SocketRequest) {
Expand Down Expand Up @@ -40,4 +41,21 @@ export class MessageSocket {
const messages = await this.messages.listByConversationId(conversationId, limit)
socket.reply(messages)
}

async feedback(socket: SocketRequest) {
const { messageId, feedback } = socket.data

const message = await this.messages.fetch(messageId)
if (!message) {
return socket.notFound('Message does not exist')
}

const conversation = await this.conversations.fetch(message.conversationId)
if (!conversation || conversation.userId !== socket.userId) {
return socket.notFound('Conversation does not exist')
}

await this.messages.feedback(messageId, feedback)
socket.reply(true)
}
}
20 changes: 19 additions & 1 deletion packages/server/src/messages/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ChannelService } from '../channels/service'
import { ConversationService } from '../conversations/service'
import { ConverseService } from '../converse/service'
import { MappingService } from '../mapping/service'
import { MessageCreatedEvent, MessageEvents } from './events'
import { MessageCreatedEvent, MessageEvents, MessageFeedbackEvent } from './events'
import { MessageService } from './service'

export class MessageStream {
Expand All @@ -19,6 +19,7 @@ export class MessageStream {

async setup() {
this.messages.events.on(MessageEvents.Created, this.handleMessageCreate.bind(this))
this.messages.events.on(MessageEvents.Feedback, this.handleMessageFeedback.bind(this))
}

private async handleMessageCreate({ message, source }: MessageCreatedEvent) {
Expand All @@ -39,6 +40,23 @@ export class MessageStream {
)
}

private async handleMessageFeedback({ messageId, feedback }: MessageFeedbackEvent) {
const message = await this.messages.get(messageId)
const conversation = await this.conversations.get(message.conversationId)

await this.streamer.stream(
'message.feedback',
{
userId: conversation.userId,
conversationId: conversation.id,
channel: await this.getChannel(conversation.id),
messageId,
feedback
},
conversation.clientId
)
}

private async getChannel(conversationId: uuid) {
const convmaps = await this.mapping.convmap.listByConversationId(conversationId)
if (convmaps.length === 1) {
Expand Down
2 changes: 1 addition & 1 deletion packages/socket/src/com.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class SocketCom {
this.socket.on('message', async (message) => {
if (this.pending[message.request]) {
if (message.data?.error) {
this.pending[message.request].reject(message.data.message)
this.pending[message.request].reject(new Error(message.data.message))
} else {
this.pending[message.request].resolve(message.data)
}
Expand Down
Loading