Finalização da Implementaçao do dialogflow
parent
9bf1850405
commit
1bdd4adaba
|
@ -15,6 +15,9 @@
|
|||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@google-cloud/dialogflow": "^4.6.0",
|
||||
"actions-on-google": "^3.0.0",
|
||||
"axios": "^0.27.2",
|
||||
"@sentry/node": "^5.29.2",
|
||||
"@types/pino": "^6.3.4",
|
||||
"bcryptjs": "^2.4.3",
|
||||
|
|
|
@ -13,10 +13,12 @@ import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact";
|
|||
import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl";
|
||||
import AppError from "../errors/AppError";
|
||||
|
||||
|
||||
import { searchContactCache } from '../helpers/ContactsCache'
|
||||
import { off } from "process";
|
||||
|
||||
import GetContactService from "../services/ContactServices/GetContactService";
|
||||
import ToggleUseQueuesContactService from "../services/ContactServices/ToggleUseQueuesContactService";
|
||||
import ToggleUseDialogflowContactService from "../services/ContactServices/ToggleUseDialogflowContactService";
|
||||
|
||||
|
||||
type IndexQuery = {
|
||||
|
@ -32,9 +34,15 @@ interface ContactData {
|
|||
name: string;
|
||||
number: string;
|
||||
email?: string;
|
||||
useDialogflow: boolean;
|
||||
extraInfo?: ExtraInfo[];
|
||||
}
|
||||
|
||||
type IndexGetContactQuery = {
|
||||
name: string;
|
||||
number: string;
|
||||
};
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
let { searchParam, pageNumber } = req.query as IndexQuery;
|
||||
|
||||
|
@ -68,6 +76,17 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
|||
return res.json({ contacts, count, hasMore });
|
||||
};
|
||||
|
||||
export const getContact = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { name, number } = req.body as IndexGetContactQuery;
|
||||
|
||||
const contact = await GetContactService({
|
||||
name,
|
||||
number
|
||||
});
|
||||
|
||||
return res.status(200).json(contact);
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const newContact: ContactData = req.body;
|
||||
newContact.number = newContact.number.replace("-", "").replace(" ", "");
|
||||
|
@ -94,11 +113,13 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
|||
let number = validNumber
|
||||
let email = newContact.email
|
||||
let extraInfo = newContact.extraInfo
|
||||
let useDialogflow = newContact.useDialogflow
|
||||
|
||||
const contact = await CreateContactService({
|
||||
name,
|
||||
number,
|
||||
email,
|
||||
useDialogflow,
|
||||
extraInfo,
|
||||
profilePicUrl
|
||||
});
|
||||
|
@ -173,6 +194,40 @@ export const remove = async (
|
|||
};
|
||||
|
||||
|
||||
export const toggleUseQueue = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { contactId } = req.params;
|
||||
|
||||
const contact = await ToggleUseQueuesContactService({ contactId });
|
||||
|
||||
const io = getIO();
|
||||
io.emit("contact", {
|
||||
action: "update",
|
||||
contact
|
||||
});
|
||||
|
||||
return res.status(200).json(contact);
|
||||
};
|
||||
|
||||
export const toggleUseDialogflow = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { contactId } = req.params;
|
||||
|
||||
const contact = await ToggleUseDialogflowContactService({ contactId });
|
||||
|
||||
const io = getIO();
|
||||
io.emit("contact", {
|
||||
action: "update",
|
||||
contact
|
||||
});
|
||||
|
||||
return res.status(200).json(contact);
|
||||
};
|
||||
|
||||
|
||||
export const contacsBulkInsertOnQueue = async (req: Request, res: Response): Promise<Response> => {
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
import { Request, Response } from "express";
|
||||
import { getIO } from "../libs/socket";
|
||||
import CreateDialogflowService from "../services/DialogflowServices/CreateDialogflowService";
|
||||
import DeleteDialogflowService from "../services/DialogflowServices/DeleteDialogflowService";
|
||||
import ListDialogflowsService from "../services/DialogflowServices/ListDialogflowService";
|
||||
import ShowDialogflowService from "../services/DialogflowServices/ShowDialogflowService";
|
||||
import TestSessionDialogflowService from "../services/DialogflowServices/TestSessionDialogflowService";
|
||||
import UpdateDialogflowService from "../services/DialogflowServices/UpdateDialogflowService";
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const dialogflows = await ListDialogflowsService();
|
||||
|
||||
return res.status(200).json(dialogflows);
|
||||
};
|
||||
|
||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { name, projectName, jsonContent, language } = req.body;
|
||||
|
||||
const dialogflow = await CreateDialogflowService({ name, projectName, jsonContent, language });
|
||||
|
||||
const io = getIO();
|
||||
io.emit("dialogflow", {
|
||||
action: "update",
|
||||
dialogflow
|
||||
});
|
||||
|
||||
return res.status(200).json(dialogflow);
|
||||
};
|
||||
|
||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { dialogflowId } = req.params;
|
||||
|
||||
const dialogflow = await ShowDialogflowService(dialogflowId);
|
||||
|
||||
return res.status(200).json(dialogflow);
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { dialogflowId } = req.params;
|
||||
const dialogflowData = req.body;
|
||||
|
||||
const dialogflow = await UpdateDialogflowService({dialogflowData, dialogflowId });
|
||||
|
||||
const io = getIO();
|
||||
io.emit("dialogflow", {
|
||||
action: "update",
|
||||
dialogflow
|
||||
});
|
||||
|
||||
return res.status(201).json(dialogflow);
|
||||
};
|
||||
|
||||
export const remove = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<Response> => {
|
||||
const { dialogflowId } = req.params;
|
||||
|
||||
await DeleteDialogflowService(dialogflowId);
|
||||
|
||||
const io = getIO();
|
||||
io.emit("dialogflow", {
|
||||
action: "delete",
|
||||
dialogflowId: +dialogflowId
|
||||
});
|
||||
|
||||
return res.status(200).send();
|
||||
};
|
||||
|
||||
export const testSession = async (req: Request, res: Response): Promise<Response> => {
|
||||
const { projectName, jsonContent, language } = req.body;
|
||||
|
||||
const response = await TestSessionDialogflowService({ projectName, jsonContent, language });
|
||||
|
||||
const io = getIO();
|
||||
io.emit("dialogflow", {
|
||||
action: "testSession",
|
||||
response
|
||||
});
|
||||
|
||||
return res.status(200).json(response);
|
||||
};
|
|
@ -14,6 +14,8 @@ import QuickAnswer from "../models/QuickAnswer";
|
|||
import SchedulingNotify from "../models/SchedulingNotify";
|
||||
import StatusChatEnd from "../models/StatusChatEnd";
|
||||
import UserOnlineTime from "../models/UserOnlineTime";
|
||||
|
||||
import Dialogflow from "../models/Dialogflow";
|
||||
// eslint-disable-next-line
|
||||
const dbConfig = require("../config/database");
|
||||
// import dbConfig from "../config/database";
|
||||
|
@ -36,6 +38,7 @@ const models = [
|
|||
SchedulingNotify,
|
||||
StatusChatEnd,
|
||||
UserOnlineTime,
|
||||
Dialogflow,
|
||||
];
|
||||
|
||||
sequelize.addModels(models);
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.createTable("Dialogflows", {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
projectName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
jsonContent: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: false,
|
||||
},
|
||||
language: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.dropTable("Dialogflows");
|
||||
}
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Queues", "dialogflowId", {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "Dialogflows", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "SET NULL"
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Queues", "dialogflowId");
|
||||
}
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Contacts", "useDialogflow", {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Contacts", "useDialogflow");
|
||||
}
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Contacts", "useQueues", {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Contacts", "useQueues");
|
||||
}
|
||||
};
|
|
@ -1,16 +1,44 @@
|
|||
const fsPromises = require("fs/promises");
|
||||
const fs = require('fs')
|
||||
import dir from 'path';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
|
||||
import ListUsersService from "../services/UserServices/ListUsersService"
|
||||
|
||||
const _botIsOnQueue = async (botName: string) => {
|
||||
|
||||
const { users, count, hasMore } = await ListUsersService({searchParam:`${botName}`,pageNumber:1});
|
||||
const botInfoFile = dir.join(os.tmpdir(), `botInfo.json`);
|
||||
|
||||
console.log('The bot botInfoFile: ', botInfoFile)
|
||||
|
||||
|
||||
try {
|
||||
|
||||
if (fs.existsSync(botInfoFile)) {
|
||||
|
||||
console.log('botInfo.json file exists');
|
||||
|
||||
const botInfo = fs.readFileSync(botInfoFile, {encoding:'utf8', flag:'r'});
|
||||
|
||||
return JSON.parse(botInfo)
|
||||
|
||||
} else {
|
||||
console.log('botInfo.json file not found!');
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.log('There was an error on try to read the botInfo.json file: ',error)
|
||||
}
|
||||
|
||||
console.log('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ')
|
||||
|
||||
const { users, count, hasMore } = await ListUsersService({ searchParam: `${botName}`, pageNumber: 1 });
|
||||
let botIsOnQueue = false
|
||||
let userIdBot = null
|
||||
let queueId = null
|
||||
|
||||
if(users.length > 0){
|
||||
if (users.length > 0) {
|
||||
|
||||
try {
|
||||
|
||||
|
@ -19,19 +47,24 @@ const _botIsOnQueue = async (botName: string) => {
|
|||
userIdBot = Object(users)[0].id
|
||||
botIsOnQueue = true
|
||||
|
||||
}catch(err){
|
||||
fs.writeFileSync(botInfoFile, JSON.stringify({ userIdBot: userIdBot, botQueueId: queueId, isOnQueue: botIsOnQueue }), "utf8");
|
||||
|
||||
console.log('O usuário botqueue não está em nenhuma fila err: ',err)
|
||||
} catch (err) {
|
||||
|
||||
console.log('O usuário botqueue não está em nenhuma fila err: ', err)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
else {
|
||||
console.log('Usuário botqueue não existe!')
|
||||
|
||||
fs.writeFileSync(botInfoFile, JSON.stringify({ isOnQueue: false, botQueueId: 0, userIdBot: 0 }), "utf8");
|
||||
|
||||
}
|
||||
|
||||
return { userIdBot: userIdBot, botQueueId: queueId, isOnQueue: botIsOnQueue }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default _botIsOnQueue;
|
||||
export default _botIsOnQueue;
|
|
@ -0,0 +1,33 @@
|
|||
const fsPromises = require("fs/promises");
|
||||
const fs = require('fs')
|
||||
import axios from 'axios';
|
||||
import * as https from "https";
|
||||
|
||||
const endPointQuery = async (url: string) => {
|
||||
|
||||
let response:any = null
|
||||
|
||||
try {
|
||||
|
||||
const httpsAgent = new https.Agent({ rejectUnauthorized: false, });
|
||||
|
||||
// const url = 'https://sos.espacolaser.com.br/api/whatsapp/ticket/R32656'
|
||||
|
||||
response = await axios.get(url, {
|
||||
httpsAgent,
|
||||
headers: {
|
||||
'x-access-token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOnsiaWQiOjEsInJvbGUiOiJjbGllbnQiLCJob3N0Ijoic29zLmVzcGFjb2xhc2VyLmNvbS5iciIsInRlbmFudCI6ImVzcGFjb2xhc2VyIiwibmFtZSI6IlNFTlNSLklUIiwiY29tcGFueSI6eyJpZCI6NDR9fSwiZGF0ZSI6MTY2MTI2MjY0MywiaWF0IjoxNjYxMjYyNjQzLCJleHAiOjE3NDc2NjI2NDN9.zf91OmRs4_C7B8OlVpLLrQMiRBYc7edP4qAdH_hqxpk',
|
||||
'Origin': 'espacolaser'
|
||||
}
|
||||
});
|
||||
console.log(`TEST URL CLIENT GET ROUTE: ${url} | STATUS CODE: ${response.status}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
}
|
||||
|
||||
export default endPointQuery;
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
import dir from 'path';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
|
||||
const deleteFileFromTMP = (name_ext: string) =>{
|
||||
|
||||
const botInfoFile = dir.join(os.tmpdir(), name_ext);
|
||||
|
||||
try {
|
||||
|
||||
fs.unlinkSync(botInfoFile);
|
||||
console.log(`${name_ext} file deleted!`);
|
||||
|
||||
} catch (error) {
|
||||
console.log(`Can't delete ${name_ext} file: `,error)
|
||||
}
|
||||
}
|
||||
|
||||
export default deleteFileFromTMP;
|
|
@ -41,6 +41,14 @@ class Contact extends Model<Contact> {
|
|||
@Column
|
||||
isGroup: boolean;
|
||||
|
||||
@Default(true)
|
||||
@Column
|
||||
useQueues: boolean;
|
||||
|
||||
@Default(true)
|
||||
@Column
|
||||
useDialogflow: boolean;
|
||||
|
||||
@CreatedAt
|
||||
createdAt: Date;
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import {
|
||||
Table,
|
||||
Column,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
Model,
|
||||
DataType,
|
||||
PrimaryKey,
|
||||
HasMany,
|
||||
AutoIncrement
|
||||
} from "sequelize-typescript";
|
||||
import Queue from "./Queue";
|
||||
|
||||
@Table
|
||||
class Dialogflow extends Model<Dialogflow> {
|
||||
@PrimaryKey
|
||||
@AutoIncrement
|
||||
@Column
|
||||
id: number;
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
name: string;
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
projectName: string;
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
jsonContent: string;
|
||||
|
||||
@Column(DataType.TEXT)
|
||||
language: string;
|
||||
|
||||
@CreatedAt
|
||||
@Column(DataType.DATE(6))
|
||||
createdAt: Date;
|
||||
|
||||
@UpdatedAt
|
||||
@Column(DataType.DATE(6))
|
||||
updatedAt: Date;
|
||||
|
||||
@HasMany(() => Queue)
|
||||
queues: Queue[]
|
||||
}
|
||||
|
||||
export default Dialogflow;
|
|
@ -8,13 +8,16 @@ import {
|
|||
AutoIncrement,
|
||||
AllowNull,
|
||||
Unique,
|
||||
BelongsToMany
|
||||
BelongsToMany,
|
||||
BelongsTo,
|
||||
ForeignKey
|
||||
} from "sequelize-typescript";
|
||||
import User from "./User";
|
||||
import UserQueue from "./UserQueue";
|
||||
|
||||
import Whatsapp from "./Whatsapp";
|
||||
import WhatsappQueue from "./WhatsappQueue";
|
||||
import Dialogflow from "./Dialogflow";
|
||||
|
||||
@Table
|
||||
class Queue extends Model<Queue> {
|
||||
|
@ -36,6 +39,13 @@ class Queue extends Model<Queue> {
|
|||
@Column
|
||||
greetingMessage: string;
|
||||
|
||||
@ForeignKey(() => Dialogflow)
|
||||
@Column
|
||||
dialogflowId: number;
|
||||
|
||||
@BelongsTo(() => Dialogflow)
|
||||
dialogflow: Dialogflow;
|
||||
|
||||
@CreatedAt
|
||||
createdAt: Date;
|
||||
|
||||
|
|
|
@ -18,6 +18,10 @@ contactRoutes.post("/contacts", isAuth, ContactController.store);
|
|||
|
||||
contactRoutes.put("/contacts/:contactId", isAuth, ContactController.update);
|
||||
|
||||
contactRoutes.put("/contacts/toggleUseQueues/:contactId", isAuth, ContactController.toggleUseQueue);
|
||||
|
||||
contactRoutes.put("/contacts/toggleUseDialogflow/:contactId", isAuth, ContactController.toggleUseDialogflow);
|
||||
|
||||
contactRoutes.delete("/contacts/:contactId", isAuth, ContactController.remove);
|
||||
|
||||
export default contactRoutes;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { Router } from "express";
|
||||
import isAuth from "../middleware/isAuth";
|
||||
|
||||
import * as DialogflowController from "../controllers/DialogflowController";
|
||||
|
||||
const dialogflowRoutes = Router();
|
||||
|
||||
dialogflowRoutes.get("/dialogflow", isAuth, DialogflowController.index);
|
||||
|
||||
dialogflowRoutes.post("/dialogflow", isAuth, DialogflowController.store);
|
||||
|
||||
dialogflowRoutes.get("/dialogflow/:dialogflowId", isAuth, DialogflowController.show);
|
||||
|
||||
dialogflowRoutes.put("/dialogflow/:dialogflowId", isAuth, DialogflowController.update);
|
||||
|
||||
dialogflowRoutes.delete("/dialogflow/:dialogflowId", isAuth, DialogflowController.remove);
|
||||
|
||||
dialogflowRoutes.post("/dialogflow/testsession", isAuth, DialogflowController.testSession);
|
||||
|
||||
export default dialogflowRoutes;
|
|
@ -13,6 +13,7 @@ import quickAnswerRoutes from "./quickAnswerRoutes";
|
|||
import reportRoutes from "./reportRoutes";
|
||||
import schedulingNotifiyRoutes from "./SchedulingNotifyRoutes";
|
||||
import statusChatEndRoutes from "./statusChatEndRoutes";
|
||||
import dialogflowRoutes from "./dialogflowRoutes";
|
||||
|
||||
const routes = Router();
|
||||
|
||||
|
@ -31,5 +32,6 @@ routes.use(quickAnswerRoutes);
|
|||
routes.use(schedulingNotifiyRoutes);
|
||||
routes.use(reportRoutes);
|
||||
routes.use(statusChatEndRoutes);
|
||||
routes.use(dialogflowRoutes);
|
||||
|
||||
export default routes;
|
||||
|
|
|
@ -3,6 +3,7 @@ import Contact from "../../models/Contact";
|
|||
|
||||
import { createOrUpdateContactCache } from '../../helpers/ContactsCache'
|
||||
|
||||
|
||||
interface ExtraInfo {
|
||||
name: string;
|
||||
value: string;
|
||||
|
@ -12,6 +13,7 @@ interface Request {
|
|||
name: string;
|
||||
number: string;
|
||||
email?: string;
|
||||
useDialogflow?: boolean;
|
||||
profilePicUrl?: string;
|
||||
extraInfo?: ExtraInfo[];
|
||||
}
|
||||
|
@ -20,6 +22,7 @@ const CreateContactService = async ({
|
|||
name,
|
||||
number,
|
||||
email = "",
|
||||
useDialogflow,
|
||||
extraInfo = []
|
||||
}: Request): Promise<Contact> => {
|
||||
const numberExists = await Contact.findOne({
|
||||
|
@ -35,6 +38,7 @@ const CreateContactService = async ({
|
|||
name,
|
||||
number,
|
||||
email,
|
||||
useDialogflow,
|
||||
extraInfo
|
||||
},
|
||||
{
|
||||
|
@ -47,7 +51,6 @@ const CreateContactService = async ({
|
|||
await createOrUpdateContactCache(`contact:${contact.id}`, {id: contact.id, name, number, profilePicUrl:'', isGroup:'false', extraInfo, email })
|
||||
//
|
||||
|
||||
|
||||
return contact;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import AppError from "../../errors/AppError";
|
||||
import Contact from "../../models/Contact";
|
||||
import CreateContactService from "./CreateContactService";
|
||||
|
||||
interface ExtraInfo {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface Request {
|
||||
name: string;
|
||||
number: string;
|
||||
email?: string;
|
||||
profilePicUrl?: string;
|
||||
extraInfo?: ExtraInfo[];
|
||||
}
|
||||
|
||||
const GetContactService = async ({ name, number }: Request): Promise<Contact> => {
|
||||
const numberExists = await Contact.findOne({
|
||||
where: { number }
|
||||
});
|
||||
|
||||
if (!numberExists) {
|
||||
const contact = await CreateContactService({
|
||||
name,
|
||||
number,
|
||||
})
|
||||
|
||||
if (contact == null)
|
||||
throw new AppError("CONTACT_NOT_FOUND")
|
||||
else
|
||||
return contact
|
||||
}
|
||||
|
||||
return numberExists
|
||||
};
|
||||
|
||||
export default GetContactService;
|
|
@ -0,0 +1,34 @@
|
|||
import AppError from "../../errors/AppError";
|
||||
import Contact from "../../models/Contact";
|
||||
|
||||
interface Request {
|
||||
contactId: string;
|
||||
}
|
||||
|
||||
const ToggleUseDialogflowContactService = async ({
|
||||
contactId
|
||||
}: Request): Promise<Contact> => {
|
||||
const contact = await Contact.findOne({
|
||||
where: { id: contactId },
|
||||
attributes: ["id", "useDialogflow"]
|
||||
});
|
||||
|
||||
if (!contact) {
|
||||
throw new AppError("ERR_NO_CONTACT_FOUND", 404);
|
||||
}
|
||||
|
||||
const useDialogflow = contact.useDialogflow ? false : true;
|
||||
|
||||
await contact.update({
|
||||
useDialogflow
|
||||
});
|
||||
|
||||
await contact.reload({
|
||||
attributes: ["id", "name", "number", "email", "profilePicUrl", "useQueues", "useDialogflow"],
|
||||
include: ["extraInfo"]
|
||||
});
|
||||
|
||||
return contact;
|
||||
};
|
||||
|
||||
export default ToggleUseDialogflowContactService;
|
|
@ -0,0 +1,34 @@
|
|||
import AppError from "../../errors/AppError";
|
||||
import Contact from "../../models/Contact";
|
||||
|
||||
interface Request {
|
||||
contactId: string;
|
||||
}
|
||||
|
||||
const ToggleUseQueuesContactService = async ({
|
||||
contactId
|
||||
}: Request): Promise<Contact> => {
|
||||
const contact = await Contact.findOne({
|
||||
where: { id: contactId },
|
||||
attributes: ["id", "useQueues"]
|
||||
});
|
||||
|
||||
if (!contact) {
|
||||
throw new AppError("ERR_NO_CONTACT_FOUND", 404);
|
||||
}
|
||||
|
||||
const useQueues = contact.useQueues ? false : true;
|
||||
|
||||
await contact.update({
|
||||
useQueues
|
||||
});
|
||||
|
||||
await contact.reload({
|
||||
attributes: ["id", "name", "number", "email", "profilePicUrl", "useQueues", "useDialogflow"],
|
||||
include: ["extraInfo"]
|
||||
});
|
||||
|
||||
return contact;
|
||||
};
|
||||
|
||||
export default ToggleUseQueuesContactService;
|
|
@ -0,0 +1,76 @@
|
|||
import * as Yup from "yup";
|
||||
|
||||
import AppError from "../../errors/AppError";
|
||||
import Dialogflow from "../../models/Dialogflow";
|
||||
|
||||
|
||||
interface Request {
|
||||
name: string;
|
||||
projectName: string;
|
||||
jsonContent: string;
|
||||
language: string;
|
||||
}
|
||||
|
||||
const CreateDialogflowService = async ({
|
||||
name,
|
||||
projectName,
|
||||
jsonContent,
|
||||
language
|
||||
}: Request): Promise<Dialogflow> => {
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.required()
|
||||
.min(2)
|
||||
.test(
|
||||
"Check-name",
|
||||
"This DialogFlow name is already used.",
|
||||
async value => {
|
||||
if (!value) return false;
|
||||
const nameExists = await Dialogflow.findOne({
|
||||
where: { name: value }
|
||||
});
|
||||
return !nameExists;
|
||||
}
|
||||
),
|
||||
projectName: Yup.string()
|
||||
.required()
|
||||
.min(2)
|
||||
.test(
|
||||
"Check-name",
|
||||
"This DialogFlow projectName is already used.",
|
||||
async value => {
|
||||
if (!value) return false;
|
||||
const nameExists = await Dialogflow.findOne({
|
||||
where: { projectName: value }
|
||||
});
|
||||
return !nameExists;
|
||||
}
|
||||
),
|
||||
jsonContent: Yup.string()
|
||||
.required()
|
||||
,
|
||||
language: Yup.string()
|
||||
.required()
|
||||
.min(2)
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate({ name, projectName, jsonContent, language });
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
|
||||
const dialogflow = await Dialogflow.create(
|
||||
{
|
||||
name,
|
||||
projectName,
|
||||
jsonContent,
|
||||
language
|
||||
}
|
||||
);
|
||||
|
||||
return dialogflow;
|
||||
};
|
||||
|
||||
export default CreateDialogflowService;
|
|
@ -0,0 +1,33 @@
|
|||
import { SessionsClient } from "@google-cloud/dialogflow";
|
||||
import Dialogflow from "../../models/Dialogflow";
|
||||
import dir from 'path';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import { logger } from "../../utils/logger";
|
||||
|
||||
const sessions : Map<number, SessionsClient> = new Map<number, SessionsClient>();
|
||||
|
||||
const createDialogflowSession = async (id:number, projectName:string, jsonContent:string) : Promise<SessionsClient | undefined> => {
|
||||
if(sessions.has(id)) {
|
||||
return sessions.get(id);
|
||||
}
|
||||
|
||||
const keyFilename = dir.join(os.tmpdir(), `whaticket_${id}.json`);
|
||||
|
||||
console.log('keyFilename: ',keyFilename)
|
||||
|
||||
logger.info(`Openig new dialogflow session #${projectName} in '${keyFilename}'`)
|
||||
|
||||
await fs.writeFileSync(keyFilename, jsonContent);
|
||||
const session = new SessionsClient({ keyFilename });
|
||||
|
||||
sessions.set(id, session);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
const createDialogflowSessionWithModel = async (model: Dialogflow) : Promise<SessionsClient | undefined> => {
|
||||
return createDialogflowSession(model.id, model.projectName, model.jsonContent);
|
||||
}
|
||||
|
||||
export { createDialogflowSession, createDialogflowSessionWithModel };
|
|
@ -0,0 +1,16 @@
|
|||
import Dialogflow from "../../models/Dialogflow";
|
||||
import AppError from "../../errors/AppError";
|
||||
|
||||
const DeleteDialogflowService = async (id: string): Promise<void> => {
|
||||
const dialogflow = await Dialogflow.findOne({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (!dialogflow) {
|
||||
throw new AppError("ERR_NO_DIALOG_FOUND", 404);
|
||||
}
|
||||
|
||||
await dialogflow.destroy();
|
||||
};
|
||||
|
||||
export default DeleteDialogflowService;
|
|
@ -0,0 +1,9 @@
|
|||
import DialogFLow from "../../models/Dialogflow";
|
||||
|
||||
const ListDialogflowService = async (): Promise<DialogFLow[]> => {
|
||||
const dialogFLows = await DialogFLow.findAll();
|
||||
|
||||
return dialogFLows;
|
||||
};
|
||||
|
||||
export default ListDialogflowService;
|
|
@ -0,0 +1,64 @@
|
|||
import * as Sentry from "@sentry/node";
|
||||
import { SessionsClient } from "@google-cloud/dialogflow";
|
||||
import { logger } from "../../utils/logger";
|
||||
|
||||
async function detectIntent(
|
||||
sessionClient:SessionsClient,
|
||||
projectId:string,
|
||||
sessionId:string,
|
||||
query:string,
|
||||
languageCode:string
|
||||
) {
|
||||
const sessionPath = sessionClient.projectAgentSessionPath(
|
||||
projectId,
|
||||
sessionId
|
||||
);
|
||||
|
||||
const request = {
|
||||
session: sessionPath,
|
||||
queryInput: {
|
||||
text: {
|
||||
text: query,
|
||||
languageCode: languageCode,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const responses = await sessionClient.detectIntent(request);
|
||||
return responses[0];
|
||||
}
|
||||
|
||||
async function queryDialogFlow(
|
||||
sessionClient:SessionsClient,
|
||||
projectId:string,
|
||||
sessionId:string,
|
||||
query:string,
|
||||
languageCode:string
|
||||
) : Promise<any | null> {
|
||||
let intentResponse;
|
||||
|
||||
try {
|
||||
intentResponse = await detectIntent(
|
||||
sessionClient,
|
||||
projectId,
|
||||
sessionId,
|
||||
query,
|
||||
languageCode
|
||||
);
|
||||
|
||||
const responses = intentResponse?.queryResult?.fulfillmentMessages;
|
||||
|
||||
if (responses?.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
return responses;
|
||||
}
|
||||
} catch (error) {
|
||||
Sentry.captureException(error);
|
||||
logger.error(`Error handling whatsapp message: Err: ${error}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export {queryDialogFlow}
|
|
@ -0,0 +1,15 @@
|
|||
import Dialogflow from "../../models/Dialogflow";
|
||||
import AppError from "../../errors/AppError";
|
||||
|
||||
|
||||
const ShowDialogflowService = async (id: string | number): Promise<Dialogflow> => {
|
||||
const dialogflow = await Dialogflow.findByPk(id);
|
||||
|
||||
if (!dialogflow) {
|
||||
throw new AppError("ERR_NO_DIALOG_FOUND", 404);
|
||||
}
|
||||
|
||||
return dialogflow;
|
||||
};
|
||||
|
||||
export default ShowDialogflowService;
|
|
@ -0,0 +1,70 @@
|
|||
import * as Yup from "yup";
|
||||
|
||||
import AppError from "../../errors/AppError";
|
||||
|
||||
import { queryDialogFlow } from "./QueryDialogflow";
|
||||
import { createDialogflowSession } from "./CreateSessionDialogflow";
|
||||
|
||||
|
||||
interface Request {
|
||||
projectName: string;
|
||||
jsonContent: string;
|
||||
language: string;
|
||||
}
|
||||
|
||||
interface Response {
|
||||
messages: string[];
|
||||
}
|
||||
|
||||
|
||||
const TestDialogflowSession = async ({
|
||||
projectName,
|
||||
jsonContent,
|
||||
language
|
||||
}: Request): Promise<Response | null> => {
|
||||
const schema = Yup.object().shape({
|
||||
projectName: Yup.string()
|
||||
.required()
|
||||
.min(2),
|
||||
jsonContent: Yup.string()
|
||||
.required(),
|
||||
language: Yup.string()
|
||||
.required()
|
||||
.min(2)
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate({ projectName, jsonContent, language });
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const session = await createDialogflowSession(999, projectName, jsonContent);
|
||||
|
||||
if (!session) {
|
||||
throw new AppError("ERR_TEST_SESSION_DIALOG", 400);
|
||||
}
|
||||
|
||||
let dialogFlowReply = await queryDialogFlow(
|
||||
session,
|
||||
projectName,
|
||||
"TestSeesion",
|
||||
"Ola",
|
||||
language,
|
||||
);
|
||||
|
||||
await session.close();
|
||||
|
||||
if (!dialogFlowReply) {
|
||||
throw new AppError("ERR_TEST_REPLY_DIALOG", 400);
|
||||
}
|
||||
|
||||
const messages = [];
|
||||
for (let message of dialogFlowReply) {
|
||||
messages.push(message.text.text[0]);
|
||||
}
|
||||
|
||||
return { messages };
|
||||
}
|
||||
|
||||
export default TestDialogflowSession;
|
|
@ -0,0 +1,55 @@
|
|||
import * as Yup from "yup";
|
||||
|
||||
import AppError from "../../errors/AppError";
|
||||
import Dialogflow from "../../models/Dialogflow";
|
||||
import ShowDialogflowService from "./ShowDialogflowService";
|
||||
|
||||
interface DialogflowData {
|
||||
name?: string;
|
||||
projectName?: string;
|
||||
jsonContent?: string;
|
||||
language?: string;
|
||||
}
|
||||
|
||||
interface Request {
|
||||
dialogflowData: DialogflowData;
|
||||
dialogflowId: string;
|
||||
}
|
||||
|
||||
const UpdateDialogflowService = async ({
|
||||
dialogflowData,
|
||||
dialogflowId
|
||||
}: Request): Promise<Dialogflow> => {
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().min(2),
|
||||
projectName: Yup.string().min(2),
|
||||
jsonContent: Yup.string().min(2),
|
||||
language: Yup.string().min(2)
|
||||
});
|
||||
|
||||
const {
|
||||
name,
|
||||
projectName,
|
||||
jsonContent,
|
||||
language
|
||||
} = dialogflowData;
|
||||
|
||||
try {
|
||||
await schema.validate({ name, projectName, jsonContent, language });
|
||||
} catch (err) {
|
||||
throw new AppError(err.message);
|
||||
}
|
||||
|
||||
const dialogflow = await ShowDialogflowService(dialogflowId);
|
||||
|
||||
await dialogflow.update({
|
||||
name,
|
||||
projectName,
|
||||
jsonContent,
|
||||
language
|
||||
});
|
||||
|
||||
return dialogflow;
|
||||
};
|
||||
|
||||
export default UpdateDialogflowService;
|
|
@ -1,5 +1,6 @@
|
|||
import * as Yup from "yup";
|
||||
import AppError from "../../errors/AppError";
|
||||
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||
import Queue from "../../models/Queue";
|
||||
|
||||
interface QueueData {
|
||||
|
@ -61,6 +62,8 @@ const CreateQueueService = async (queueData: QueueData): Promise<Queue> => {
|
|||
|
||||
const queue = await Queue.create(queueData);
|
||||
|
||||
deleteFileFromTMP(`botInfo.json`)
|
||||
|
||||
return queue;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import UserQueue from "../../models/UserQueue";
|
|||
import ListTicketsServiceCache from "../TicketServices/ListTicketServiceCache";
|
||||
|
||||
import { deleteTicketsFieldsCache } from '../../helpers/TicketCache'
|
||||
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||
|
||||
const DeleteQueueService = async (queueId: number | string): Promise<void> => {
|
||||
|
||||
|
@ -29,6 +30,8 @@ const DeleteQueueService = async (queueId: number | string): Promise<void> => {
|
|||
}
|
||||
|
||||
await queue.destroy();
|
||||
|
||||
deleteFileFromTMP(`botInfo.json`)
|
||||
};
|
||||
|
||||
export default DeleteQueueService;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Op } from "sequelize";
|
||||
import * as Yup from "yup";
|
||||
import AppError from "../../errors/AppError";
|
||||
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||
import Queue from "../../models/Queue";
|
||||
import ShowQueueService from "./ShowQueueService";
|
||||
|
||||
|
@ -67,6 +68,8 @@ const UpdateQueueService = async (
|
|||
|
||||
await queue.update(queueData);
|
||||
|
||||
deleteFileFromTMP(`botInfo.json`)
|
||||
|
||||
return queue;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ const FindOrCreateTicketService = async (
|
|||
|
||||
|
||||
//Habilitar esse caso queira usar o bot
|
||||
// const botInfo = await BotIsOnQueue('botqueue')
|
||||
const botInfo = { isOnQueue: false }
|
||||
const botInfo = await BotIsOnQueue('botqueue')
|
||||
// const botInfo = { isOnQueue: false }
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ const ShowTicketService = async (id: string | number): Promise<Ticket> => {
|
|||
{
|
||||
model: Contact,
|
||||
as: "contact",
|
||||
attributes: ["id", "name", "number", "profilePicUrl"],
|
||||
attributes: ["id", "name", "number", "profilePicUrl", "useDialogflow", "useQueues"],
|
||||
include: ["extraInfo"]
|
||||
},
|
||||
{
|
||||
|
@ -21,7 +21,8 @@ const ShowTicketService = async (id: string | number): Promise<Ticket> => {
|
|||
{
|
||||
model: Queue,
|
||||
as: "queue",
|
||||
attributes: ["id", "name", "color"]
|
||||
attributes: ["id", "name", "color"],
|
||||
include: ["dialogflow"]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -4,6 +4,8 @@ import AppError from "../../errors/AppError";
|
|||
import { SerializeUser } from "../../helpers/SerializeUser";
|
||||
import User from "../../models/User";
|
||||
|
||||
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||
|
||||
interface Request {
|
||||
email: string;
|
||||
password: string;
|
||||
|
@ -78,6 +80,8 @@ const CreateUserService = async ({
|
|||
|
||||
const serializedUser = SerializeUser(user);
|
||||
|
||||
deleteFileFromTMP(`botInfo.json`)
|
||||
|
||||
return serializedUser;
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import AppError from "../../errors/AppError";
|
|||
import Ticket from "../../models/Ticket";
|
||||
import UpdateDeletedUserOpenTicketsStatus from "../../helpers/UpdateDeletedUserOpenTicketsStatus";
|
||||
|
||||
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||
|
||||
const DeleteUserService = async (id: string | number): Promise<void> => {
|
||||
const user = await User.findOne({
|
||||
|
@ -24,6 +25,8 @@ const DeleteUserService = async (id: string | number): Promise<void> => {
|
|||
|
||||
await user.destroy();
|
||||
|
||||
deleteFileFromTMP(`botInfo.json`)
|
||||
|
||||
};
|
||||
|
||||
export default DeleteUserService;
|
||||
|
|
|
@ -3,6 +3,8 @@ import * as Yup from "yup";
|
|||
import AppError from "../../errors/AppError";
|
||||
import ShowUserService from "./ShowUserService";
|
||||
|
||||
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||
|
||||
interface UserData {
|
||||
email?: string;
|
||||
password?: string;
|
||||
|
@ -67,6 +69,8 @@ const UpdateUserService = async ({
|
|||
queues: user.queues
|
||||
};
|
||||
|
||||
deleteFileFromTMP(`botInfo.json`)
|
||||
|
||||
return serializedUser;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
const data:any[] = [
|
||||
{
|
||||
"id":"1",
|
||||
"action":"transfer_to_attendant",
|
||||
},
|
||||
{
|
||||
"id":"2",
|
||||
"action":"request_endpoint",
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
|
||||
export default data;
|
|
@ -16,7 +16,9 @@ import {
|
|||
Contact as WbotContact,
|
||||
Message as WbotMessage,
|
||||
MessageAck,
|
||||
Client
|
||||
Client,
|
||||
Chat,
|
||||
MessageMedia
|
||||
} from "whatsapp-web.js";
|
||||
|
||||
import Contact from "../../models/Contact";
|
||||
|
@ -44,17 +46,23 @@ import { StartWhatsAppSession } from "../../services/WbotServices/StartWhatsAppS
|
|||
import { removeWbot } from '../../libs/wbot'
|
||||
import { restartWhatsSession } from "../../helpers/RestartWhatsSession";
|
||||
|
||||
// test del
|
||||
|
||||
import data_ura from './ura'
|
||||
import msg_client_transfer from './ura_msg_transfer'
|
||||
import final_message from "./ura_final_message";
|
||||
import SendWhatsAppMessage from "./SendWhatsAppMessage";
|
||||
import Whatsapp from "../../models/Whatsapp";
|
||||
import { splitDateTime } from "../../helpers/SplitDateTime";
|
||||
//
|
||||
|
||||
import { queryDialogFlow } from "../DialogflowServices/QueryDialogflow";
|
||||
import { createDialogflowSessionWithModel } from "../DialogflowServices/CreateSessionDialogflow";
|
||||
import bot_actions from './BotActions'
|
||||
import ShowTicketService from "../TicketServices/ShowTicketService";
|
||||
|
||||
import { updateTicketCacheByTicketId } from '../../helpers/TicketCache'
|
||||
|
||||
import endPointQuery from "../../helpers/EndpointQuery";
|
||||
|
||||
|
||||
|
||||
interface Session extends Client {
|
||||
|
@ -171,6 +179,171 @@ const verifyMessage = async (
|
|||
};
|
||||
|
||||
|
||||
async function sendDelayedMessages(wbot: Session, ticket: Ticket, contact: Contact, message: string) {
|
||||
const body = message.replace(/\\n/g, '\n');
|
||||
|
||||
if (body.search('dialog_actions') != -1) {
|
||||
|
||||
let msgAction = botMsgActions(body)
|
||||
|
||||
console.log('gggggggggggggggggggggggggggggggggg msgAction: ', msgAction)
|
||||
|
||||
if (msgAction.actions[0] == 'request_endpoint') {
|
||||
|
||||
const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody);
|
||||
await verifyMessage(sentMessage, ticket, contact);
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
|
||||
// const url = 'https://sos.espacolaser.com.br/api/whatsapps/ticket/R32656'
|
||||
const endPointResponse = await endPointQuery(msgAction.actions[1])
|
||||
|
||||
|
||||
if (endPointResponse) {
|
||||
|
||||
const response = Object.entries(endPointResponse.data);
|
||||
let msg_endpoint_response = ''
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
|
||||
msg_endpoint_response += `*${response[i][0]}*: ${response[i][1]}\n`
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (endPointResponse.data.status == 'EM ATENDIMENTO') {
|
||||
|
||||
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `Seu chamado está em atendimento pelo analista ${endPointResponse.data.tecnico} + Última informação do CHAT
|
||||
// Verificar pelo “ID do solicitante” se existe chamado na HIT -> Se houver, informar status (Vamos alinhar os detalhes)`);
|
||||
|
||||
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n Seu chamado está em atendimento\n\n *Analista:* ${endPointResponse.data.tecnico}\n *Chat:* ${endPointResponse.data.chat ? endPointResponse.data.chat : ""}\n\n_Digite *0* para voltar ao menu principal._`);
|
||||
const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n Seu chamado está em atendimento\n\n${msg_endpoint_response}\n_Digite *0* para voltar ao menu principal._`);
|
||||
await verifyMessage(msg, ticket, contact);
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
|
||||
}
|
||||
else if (endPointResponse.data.categoria == 'ELOS' || (endPointResponse.data.subcategoria == 'VENDA' || endPointResponse.data.subcategoria == 'INDISPONIBILIDADE')) {
|
||||
|
||||
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n Vi que está com um problema no ELOS\n\n *Status:* ${endPointResponse.data.status}\n\nSe seu caso for urgente para concluir uma venda, digite “URGENTE”\n_Digite *0* para voltar ao menu principal._`);
|
||||
const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n Vi que está com um problema no ELOS\n\n${msg_endpoint_response}\nSe seu caso for urgente para concluir uma venda, digite “URGENTE”\n_Digite *0* para voltar ao menu principal._`);
|
||||
await verifyMessage(msg, ticket, contact);
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
|
||||
}
|
||||
else if ((endPointResponse.data.categoria == 'INFRAESTRUTURA' || endPointResponse.data.subcategoria == 'INTERNET' ||
|
||||
endPointResponse.data.terceiro_nivel == 'QUEDA TOTAL' || endPointResponse.data.terceiro_nivel == 'PROBLEMA DE LENTIDÃO') ||
|
||||
(endPointResponse.data.terceiro_nivel == 'PROBLEMA DE LENTIDÃO' || endPointResponse.data.terceiro_nivel == 'ABERTO')) {
|
||||
|
||||
const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n${msg_endpoint_response}\n Estamos direcionando seu atendimento para o Suporte. Em breve você será atendido por um de nossos atendentes!`);
|
||||
await transferTicket(0, wbot, ticket, contact)
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n *Status:* ${endPointResponse.data.status}\n *Data:* ${endPointResponse.data.data_chat ? endPointResponse.data.data_chat : ""}\n *Hora:* ${endPointResponse.data.hora_chat ? endPointResponse.data.hora_chat : ""} \n\n Por favor, aguarde atendimento e acompanhe sua solicitação no SOS.\n_Digite *0* para voltar ao menu principal._`);
|
||||
const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n${msg_endpoint_response}\n Por favor, aguarde atendimento e acompanhe sua solicitação no SOS.\n_Digite *0* para voltar ao menu principal._`);
|
||||
await verifyMessage(msg, ticket, contact);
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
botSendMessage(ticket, contact, wbot, `Desculpe, nao foi possível realizar a consulta!\n _Digite *0* para voltar ao menu principal._`)
|
||||
}
|
||||
|
||||
} else if (msgAction.actions[0] == 'queue_transfer') {
|
||||
|
||||
console.log('>>>>>>>>>>>>>>> msgAction: ', msgAction, ' | msgAction.actions[1]: ', msgAction.actions[1])
|
||||
|
||||
const msg = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody);
|
||||
await verifyMessage(msg, ticket, contact);
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
|
||||
await transferTicket(+msgAction.actions[1], wbot, ticket, contact)
|
||||
}
|
||||
else if (msgAction.actions[0] == 'send_file'){
|
||||
|
||||
const sourcePath = path.join(__dirname,`../../../public/bot`)
|
||||
|
||||
const msg = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody);
|
||||
await verifyMessage(msg, ticket, contact);
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
|
||||
await botSendMedia(ticket,contact,wbot,sourcePath, msgAction.actions[1])
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// const linesOfBody = body.split('\n');
|
||||
const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, body);
|
||||
await verifyMessage(sentMessage, ticket, contact);
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
|
||||
// for(let message of linesOfBody) {
|
||||
// const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, message);
|
||||
// await verifyMessage(sentMessage, ticket, contact);
|
||||
// await new Promise(f => setTimeout(f, 1000));
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
const extractCallCode = (str: string) => {
|
||||
if (str.includes('*') && str.indexOf('*') < str.lastIndexOf('*')) {
|
||||
return (str.substring(str.indexOf('*'), str.lastIndexOf('*') + 1)).split('*').join('')
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
const sendDialogflowAwswer = async (
|
||||
wbot: Session,
|
||||
ticket: Ticket,
|
||||
msg: WbotMessage,
|
||||
contact: Contact,
|
||||
chat: Chat
|
||||
) => {
|
||||
const session = await createDialogflowSessionWithModel(ticket.queue.dialogflow);
|
||||
if (session === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
wbot.sendPresenceAvailable();
|
||||
|
||||
// console.log('typeof(msg.type): ', typeof (msg.type), ' | msg.type: ', msg.type)
|
||||
|
||||
if (msg.type != 'chat') {
|
||||
botSendMessage(ticket, contact, wbot, `Desculpe, nao compreendi!\nEnvie apenas texto quando estiver interagindo com o bot!\n _Digite *0* para voltar ao menu principal._`)
|
||||
return
|
||||
}
|
||||
|
||||
if (msg.type == 'chat' && String(msg.body).length > 120) {
|
||||
botSendMessage(ticket, contact, wbot, `Desculpe, nao compreendi!\nTexto acima de 120 caracteres!\n _Digite *0* para voltar ao menu principal._`)
|
||||
return
|
||||
}
|
||||
|
||||
let dialogFlowReply = await queryDialogFlow(
|
||||
session,
|
||||
ticket.queue.dialogflow.projectName,
|
||||
msg.from,
|
||||
msg.body,
|
||||
ticket.queue.dialogflow.language
|
||||
);
|
||||
if (dialogFlowReply === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
chat.sendStateTyping();
|
||||
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
|
||||
for (let message of dialogFlowReply) {
|
||||
await sendDelayedMessages(wbot, ticket, contact, message.text.text[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const verifyQueue = async (
|
||||
wbot: Session,
|
||||
|
@ -195,8 +368,8 @@ const verifyQueue = async (
|
|||
let choosenQueue = null
|
||||
|
||||
//Habilitar esse caso queira usar o bot
|
||||
// const botInfo = await BotIsOnQueue('botqueue')
|
||||
const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 }
|
||||
const botInfo = await BotIsOnQueue('botqueue')
|
||||
// const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 }
|
||||
|
||||
if (botInfo.isOnQueue) {
|
||||
|
||||
|
@ -247,7 +420,12 @@ const verifyQueue = async (
|
|||
ticketId: ticket.id
|
||||
});
|
||||
|
||||
data_ura.forEach((s, index) => { botOptions += `*${index + 1}* - ${s.option}\n` });
|
||||
const _ticket = await ShowTicketService(ticket.id);
|
||||
const chat = await msg.getChat();
|
||||
await sendDialogflowAwswer(wbot, _ticket, msg, contact, chat);
|
||||
return
|
||||
|
||||
|
||||
}
|
||||
//
|
||||
|
||||
|
@ -302,6 +480,27 @@ const verifyQueue = async (
|
|||
}
|
||||
};
|
||||
|
||||
const transferTicket = async (queueIndex: number, wbot: Session, ticket: Ticket, contact: Contact) => {
|
||||
|
||||
const botInfo = await BotIsOnQueue('botqueue')
|
||||
|
||||
const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId)
|
||||
|
||||
const queues = queuesWhatsGreetingMessage.queues
|
||||
|
||||
await botTransferTicket(queues[queueIndex], ticket, contact, wbot)
|
||||
|
||||
}
|
||||
|
||||
const botMsgActions = (params: string) => {
|
||||
|
||||
let lstActions = params.split('dialog_actions=')
|
||||
let bodyMsg = lstActions[0].trim()
|
||||
let actions = lstActions[1].split("=")
|
||||
|
||||
return { msgBody: bodyMsg, 'actions': [actions[0].trim(), actions[1].trim()] };
|
||||
}
|
||||
|
||||
const isValidMsg = (msg: WbotMessage): boolean => {
|
||||
if (msg.from === "status@broadcast") return false;
|
||||
if (
|
||||
|
@ -341,6 +540,37 @@ const botTransferTicket = async (queues: Queue, ticket: Ticket, contact: Contact
|
|||
|
||||
}
|
||||
|
||||
const botSendMedia = async (ticket: Ticket, contact: Contact, wbot: Session, mediaPath: string, fileNameExtension: string) => {
|
||||
|
||||
const debouncedSentMessage = debounce(
|
||||
|
||||
async () => {
|
||||
|
||||
// const sentMessage = await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`, newMedia, { sendAudioAsVoice: true });
|
||||
|
||||
const newMedia = MessageMedia.fromFilePath(`${mediaPath}/${fileNameExtension}`);
|
||||
|
||||
const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, newMedia, {caption: 'this is my caption'});
|
||||
|
||||
|
||||
|
||||
// client.sendMessage("xxxxxxxx@c.us", media, {caption: "some caption"}); })();
|
||||
|
||||
// client.sendMessage(msg.from, attachmentData, { caption: 'Here\'s your requested media.' });
|
||||
|
||||
await ticket.update({ lastMessage: fileNameExtension });
|
||||
|
||||
verifyMessage(sentMessage, ticket, contact);
|
||||
|
||||
},
|
||||
3000,
|
||||
ticket.id
|
||||
);
|
||||
|
||||
debouncedSentMessage();
|
||||
|
||||
}
|
||||
|
||||
|
||||
const botSendMessage = (ticket: Ticket, contact: Contact, wbot: Session, msg: string) => {
|
||||
|
||||
|
@ -469,307 +699,14 @@ const handleMessage = async (
|
|||
// O bot interage com o cliente e encaminha o atendimento para fila de atendende quando o usuário escolhe a opção falar com atendente
|
||||
|
||||
//Habilitar esse caso queira usar o bot
|
||||
// const botInfo = await BotIsOnQueue('botqueue')
|
||||
const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 }
|
||||
|
||||
const botInfo = await BotIsOnQueue('botqueue')
|
||||
// const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 }
|
||||
if (botInfo.isOnQueue && !msg.fromMe && ticket.userId == botInfo.userIdBot) {
|
||||
|
||||
|
||||
if (msg.body === '0') {
|
||||
|
||||
const queue = await ShowQueueService(ticket.queue.id);
|
||||
|
||||
const greetingMessage = `\u200e${queue.greetingMessage}`;
|
||||
|
||||
let options = "";
|
||||
|
||||
data_ura.forEach((s, index) => { options += `*${index + 1}* - ${s.option}\n` });
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `${greetingMessage}\n\n${options}\n${final_message.msg}`)
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
// Pega as ultimas 9 opções numericas digitadas pelo cliente em orde DESC
|
||||
// Consulta apenas mensagens do usuári
|
||||
|
||||
|
||||
let lastOption = ''
|
||||
|
||||
let ura_length = data_ura.length
|
||||
|
||||
let indexAttendant = data_ura.findIndex((u) => u.atendente)
|
||||
|
||||
let opt_user_attendant = '-1'
|
||||
|
||||
if (indexAttendant != -1) {
|
||||
opt_user_attendant = data_ura[indexAttendant].id
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
let ticket_message = await ShowTicketMessage(ticket.id, true, ura_length, `^[0-${ura_length}}]$`);
|
||||
|
||||
if (ticket_message.length > 1) {
|
||||
|
||||
lastOption = ticket_message[1].body
|
||||
|
||||
const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId)
|
||||
|
||||
const queues = queuesWhatsGreetingMessage.queues
|
||||
|
||||
if (queues.length > 1) {
|
||||
|
||||
const index_opt_user_attendant = ticket_message.findIndex((q) => q.body == opt_user_attendant)
|
||||
const index0 = ticket_message.findIndex((q) => q.body == '0')
|
||||
|
||||
if (index_opt_user_attendant != -1) {
|
||||
|
||||
if (index0 > -1 && index0 < index_opt_user_attendant) {
|
||||
lastOption = ''
|
||||
}
|
||||
else {
|
||||
lastOption = opt_user_attendant
|
||||
}
|
||||
}
|
||||
await sendDialogflowAwswer(wbot, ticket, msg, contact, chat);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
// È numero
|
||||
if (!Number.isNaN(Number(msg.body.trim())) && (+msg.body >= 0 && +msg.body <= data_ura.length)) {
|
||||
|
||||
const indexUra = data_ura.findIndex((ura) => ura.id == msg.body.trim())
|
||||
|
||||
|
||||
|
||||
if (indexUra != -1) {
|
||||
|
||||
if (data_ura[indexUra].id != opt_user_attendant && lastOption != opt_user_attendant) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// test del
|
||||
let next = true
|
||||
|
||||
let indexAux = ticket_message.findIndex((e) => e.body == '0')
|
||||
|
||||
let listMessage = null
|
||||
|
||||
if (indexAux != -1) {
|
||||
|
||||
listMessage = ticket_message.slice(0, indexAux)
|
||||
}
|
||||
else {
|
||||
|
||||
listMessage = ticket_message
|
||||
|
||||
}
|
||||
|
||||
let id = ''
|
||||
let subUra = null
|
||||
|
||||
if (listMessage.length > 1) {
|
||||
|
||||
id = listMessage[listMessage.length - 1].body
|
||||
subUra = data_ura.filter((e) => e.id == id)[0]
|
||||
|
||||
if (subUra && (!subUra.subOptions || subUra.subOptions.length == 0)) {
|
||||
|
||||
listMessage.pop()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (listMessage.length > 1) {
|
||||
|
||||
id = listMessage[listMessage.length - 1].body
|
||||
subUra = data_ura.filter((e) => e.id == id)[0]
|
||||
|
||||
if (subUra.subOptions && subUra.subOptions.length > 0) {
|
||||
|
||||
if (!Number.isNaN(Number(msg.body.trim())) && (+msg.body >= 0 && +msg.body <= subUra.subOptions?.length) && subUra.subOptions) {
|
||||
|
||||
|
||||
if (subUra.subOptions[+msg.body - 1].responseToClient) {
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `*${subUra.option}*\n\n${subUra.subOptions[+msg.body - 1].responseToClient}`)
|
||||
|
||||
}
|
||||
else {
|
||||
botSendMessage(ticket, contact, wbot, `*${subUra.option}*\n\n${subUra.subOptions[+msg.body - 1].subOpt}`)
|
||||
}
|
||||
|
||||
const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId)
|
||||
|
||||
const queues = queuesWhatsGreetingMessage.queues
|
||||
|
||||
if (queues.length > 0) {
|
||||
await botTransferTicket(queues[0], ticket, contact, wbot)
|
||||
}
|
||||
else {
|
||||
console.log('NO QUEUE!')
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
let options = "";
|
||||
let subOptions: any[] = subUra.subOptions
|
||||
|
||||
subOptions?.forEach((s, index) => { options += `*${index + 1}* - ${s.subOpt}\n` });
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `*${subUra.option}*\n\nDigite um número válido disponível no menu de opções de atendimento abaixo: \n${options}\n\n*0* - Voltar ao menu principal`)
|
||||
|
||||
}
|
||||
|
||||
next = false
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
if (next) {
|
||||
if (data_ura[indexUra].subOptions && data_ura[indexUra].subOptions.length > 0) {
|
||||
|
||||
let options = "";
|
||||
let option = data_ura[indexUra].option
|
||||
let subOptions: any[] = data_ura[indexUra].subOptions
|
||||
let description = data_ura[indexUra].description
|
||||
|
||||
subOptions?.forEach((s, index) => { options += `*${index + 1}* - ${s.subOpt}\n` });
|
||||
|
||||
const body = `\u200e${description}:\n${options}`
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `*${option}*\n\n${body}\n\n *0* - Voltar ao menu principal`)
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
//test del deletar isso (Usar somente na hit)
|
||||
if (data_ura[indexUra].closeChat) {
|
||||
|
||||
|
||||
const { ticket: res } = await UpdateTicketService({
|
||||
ticketData: { 'status': 'closed', 'userId': botInfo.userIdBot }, ticketId: ticket.id
|
||||
});
|
||||
|
||||
///////////////////////////////
|
||||
const whatsapp = await ShowWhatsAppService(ticket.whatsappId);
|
||||
|
||||
const { farewellMessage } = whatsapp;
|
||||
|
||||
if (farewellMessage) {
|
||||
await SendWhatsAppMessage({ body: farewellMessage, ticket: res });
|
||||
}
|
||||
///////////////////////////////
|
||||
|
||||
}
|
||||
else {
|
||||
botSendMessage(ticket, contact, wbot, `${data_ura[indexUra].description}\n\n *0* - Voltar ao menu principal`)
|
||||
}
|
||||
//
|
||||
|
||||
|
||||
// botSendMessage(ticket, contact, wbot, `${data_ura[indexUra].description}\n\n *0* - Voltar ao menu principal`)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (data_ura[indexUra].id == opt_user_attendant) {
|
||||
|
||||
const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId)
|
||||
|
||||
const queues = queuesWhatsGreetingMessage.queues
|
||||
|
||||
// Se fila for maior que 1 exibi as opções fila para atendimento humano
|
||||
if (queues.length > 1) {
|
||||
|
||||
let options = "";
|
||||
|
||||
queues.forEach((queue, index) => {
|
||||
|
||||
options += `*${index + 1}* - ${queue.name}\n`;
|
||||
|
||||
});
|
||||
|
||||
const body = `\u200eSelecione uma das opções de atendimento abaixo:\n${options}`;
|
||||
|
||||
botSendMessage(ticket, contact, wbot, body)
|
||||
|
||||
} // Para situações onde há apenas uma fila com exclusão da fila do bot, já direciona o cliente para essa fila de atendimento humano
|
||||
else if (queues.length == 1) {
|
||||
|
||||
await botTransferTicket(queues[0], ticket, contact, wbot)
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `${msg_client_transfer.msg}`)
|
||||
|
||||
}
|
||||
}
|
||||
else if (lastOption == opt_user_attendant) {
|
||||
|
||||
const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId)
|
||||
|
||||
const queues = queuesWhatsGreetingMessage.queues
|
||||
|
||||
// É numero
|
||||
if (!Number.isNaN(Number(msg.body.trim())) && (+msg.body >= 0 && +msg.body <= queues.length)) {
|
||||
|
||||
await botTransferTicket(queues[+msg.body - 1], ticket, contact, wbot)
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `${msg_client_transfer.msg}`)
|
||||
}
|
||||
else {
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `Digite um número válido disponível no menu de opções de atendimento\n\n*0* - Voltar ao menu principal`)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// É numero
|
||||
if (!Number.isNaN(Number(msg.body.trim()))) {
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `Opção numérica inválida!\nDigite um dos números mostrados no menu de opções\n\n*0* - Voltar ao menu principal`)
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
botSendMessage(ticket, contact, wbot, `Digite um número válido disponível no menu de opções\n\n*0* - Voltar ao menu principal`)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
//
|
||||
|
||||
|
||||
// test del
|
||||
|
||||
// if (msg.body.trim() == 'broken') {
|
||||
// throw new Error('Throw makes it go boom!')
|
||||
|
|
|
@ -73,6 +73,7 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => {
|
|||
name: "",
|
||||
number: "",
|
||||
email: "",
|
||||
useDialogflow: true,
|
||||
};
|
||||
|
||||
const [contact, setContact] = useState(initialState);
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import * as Yup from "yup";
|
||||
import { Formik, Form, Field } from "formik";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
CircularProgress,
|
||||
Select,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
TextField,
|
||||
} from '@material-ui/core'
|
||||
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { green } from "@material-ui/core/colors";
|
||||
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
|
||||
import api from "../../services/api";
|
||||
import toastError from "../../errors/toastError";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
textField: {
|
||||
marginRight: theme.spacing(1),
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
btnWrapper: {
|
||||
position: "relative",
|
||||
},
|
||||
|
||||
buttonProgress: {
|
||||
color: green[500],
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
marginTop: -12,
|
||||
marginLeft: -12,
|
||||
},
|
||||
btnLeft: {
|
||||
display: "flex",
|
||||
marginRight: "auto",
|
||||
marginLeft: 12,
|
||||
},
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 240,
|
||||
},
|
||||
colorAdorment: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
}));
|
||||
|
||||
const DialogflowSchema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.min(2, "Too Short!")
|
||||
.max(50, "Too Long!")
|
||||
.required("Required"),
|
||||
projectName: Yup.string().min(3, "Too Short!").max(100, "Too Long!").required(),
|
||||
jsonContent: Yup.string().min(3, "Too Short!").required(),
|
||||
language: Yup.string().min(2, "Too Short!").max(50, "Too Long!").required(),
|
||||
|
||||
});
|
||||
|
||||
const DialogflowModal = ({ open, onClose, dialogflowId }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const initialState = {
|
||||
name: "",
|
||||
projectName: "",
|
||||
jsonContent: "",
|
||||
language: "",
|
||||
};
|
||||
|
||||
const [dialogflow, setDialogflow] = useState(initialState);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (!dialogflowId) return;
|
||||
try {
|
||||
const { data } = await api.get(`/dialogflow/${dialogflowId}`);
|
||||
setDialogflow(prevState => {
|
||||
return { ...prevState, ...data };
|
||||
});
|
||||
} catch (err) {
|
||||
toastError(err);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
setDialogflow({
|
||||
name: "",
|
||||
projectName: "",
|
||||
jsonContent: "",
|
||||
language: "",
|
||||
});
|
||||
};
|
||||
}, [dialogflowId, open]);
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setDialogflow(initialState);
|
||||
};
|
||||
|
||||
const handleTestSession = async (event, values) => {
|
||||
try {
|
||||
const {projectName, jsonContent, language } = values
|
||||
|
||||
await api.post(`/dialogflow/testSession`, {projectName, jsonContent, language });
|
||||
|
||||
toast.success( i18n.t("dialogflowModal.messages.testSuccess") );
|
||||
} catch (err) {
|
||||
toastError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveDialogflow = async values => {
|
||||
try {
|
||||
console.log(values)
|
||||
if (dialogflowId) {
|
||||
await api.put(`/dialogflow/${dialogflowId}`, values);
|
||||
toast.success( i18n.t("dialogflowModal.messages.editSuccess") );
|
||||
} else {
|
||||
await api.post("/dialogflow", values);
|
||||
toast.success( i18n.t("dialogflowModal.messages.addSuccess") );
|
||||
}
|
||||
handleClose();
|
||||
} catch (err) {
|
||||
toastError(err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
|
||||
<Dialog open={open} onClose={handleClose} scroll="paper">
|
||||
<DialogTitle>
|
||||
{dialogflowId
|
||||
? `${i18n.t("dialogflowModal.title.edit")}`
|
||||
: `${i18n.t("dialogflowModal.title.add")}`}
|
||||
</DialogTitle>
|
||||
<Formik
|
||||
initialValues={dialogflow}
|
||||
enableReinitialize={true}
|
||||
validationSchema={DialogflowSchema}
|
||||
onSubmit={(values, actions, event) => {
|
||||
setTimeout(() => {
|
||||
console.log(actions);
|
||||
handleSaveDialogflow(values);
|
||||
actions.setSubmitting(false);
|
||||
}, 400);
|
||||
}}
|
||||
>
|
||||
{({ touched, errors, isSubmitting, values }) => (
|
||||
<Form>
|
||||
<DialogContent dividers>
|
||||
<Field
|
||||
as={TextField}
|
||||
label={i18n.t("dialogflowModal.form.name")}
|
||||
autoFocus
|
||||
name="name"
|
||||
error={touched.name && Boolean(errors.name)}
|
||||
helperText={touched.name && errors.name}
|
||||
variant="outlined"
|
||||
margin="dense"
|
||||
className={classes.textField}
|
||||
/>
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={classes.formControl}
|
||||
margin="dense"
|
||||
>
|
||||
<InputLabel id="language-selection-input-label">
|
||||
{i18n.t("dialogflowModal.form.language")}
|
||||
</InputLabel>
|
||||
|
||||
<Field
|
||||
as={Select}
|
||||
label={i18n.t("dialogflowModal.form.language")}
|
||||
name="language"
|
||||
labelId="profile-selection-label"
|
||||
error={touched.language && Boolean(errors.language)}
|
||||
helperText={touched.language && errors.language}
|
||||
id="language-selection"
|
||||
required
|
||||
>
|
||||
<MenuItem value="pt-BR">Portugues</MenuItem>
|
||||
<MenuItem value="en">Inglês</MenuItem>
|
||||
<MenuItem value="es">Espanhol</MenuItem>
|
||||
</Field>
|
||||
</FormControl>
|
||||
<div>
|
||||
<Field
|
||||
as={TextField}
|
||||
label={i18n.t("dialogflowModal.form.projectName")}
|
||||
name="projectName"
|
||||
error={touched.projectName && Boolean(errors.projectName)}
|
||||
helperText={touched.projectName && errors.projectName}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
margin="dense"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Field
|
||||
as={TextField}
|
||||
label={i18n.t("dialogflowModal.form.jsonContent")}
|
||||
type="jsonContent"
|
||||
multiline
|
||||
//inputRef={greetingRef}
|
||||
rows={5}
|
||||
fullWidth
|
||||
name="jsonContent"
|
||||
error={
|
||||
touched.jsonContent && Boolean(errors.jsonContent)
|
||||
}
|
||||
helperText={
|
||||
touched.jsonContent && errors.jsonContent
|
||||
}
|
||||
variant="outlined"
|
||||
margin="dense"
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button
|
||||
//type="submit"
|
||||
onClick={(e) => handleTestSession(e, values)}
|
||||
color="inherit"
|
||||
disabled={isSubmitting}
|
||||
name="testSession"
|
||||
variant="outlined"
|
||||
className={classes.btnLeft}
|
||||
>
|
||||
{i18n.t("dialogflowModal.buttons.test")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleClose}
|
||||
color="secondary"
|
||||
disabled={isSubmitting}
|
||||
variant="outlined"
|
||||
>
|
||||
{i18n.t("dialogflowModal.buttons.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
disabled={isSubmitting}
|
||||
variant="contained"
|
||||
className={classes.btnWrapper}
|
||||
>
|
||||
{dialogflowId
|
||||
? `${i18n.t("dialogflowModal.buttons.okEdit")}`
|
||||
: `${i18n.t("dialogflowModal.buttons.okAdd")}`}
|
||||
{isSubmitting && (
|
||||
<CircularProgress
|
||||
size={24}
|
||||
className={classes.buttonProgress}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DialogflowModal;
|
|
@ -13,14 +13,23 @@ import DialogActions from "@material-ui/core/DialogActions";
|
|||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||
import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
IconButton,
|
||||
InputAdornment
|
||||
} from "@material-ui/core"
|
||||
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
|
||||
import api from "../../services/api";
|
||||
import toastError from "../../errors/toastError";
|
||||
import ColorPicker from "../ColorPicker";
|
||||
import { IconButton, InputAdornment } from "@material-ui/core";
|
||||
import { Colorize } from "@material-ui/icons";
|
||||
import useDialogflows from "../../hooks/useDialogflows";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
|
@ -45,8 +54,8 @@ const useStyles = makeStyles(theme => ({
|
|||
marginLeft: -12,
|
||||
},
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 120,
|
||||
marginRight: theme.spacing(1),
|
||||
minWidth: 200,
|
||||
},
|
||||
colorAdorment: {
|
||||
width: 20,
|
||||
|
@ -61,6 +70,7 @@ const QueueSchema = Yup.object().shape({
|
|||
.required("Required"),
|
||||
color: Yup.string().min(3, "Too Short!").max(9, "Too Long!").required(),
|
||||
greetingMessage: Yup.string(),
|
||||
dialogflowId: Yup.number(),
|
||||
});
|
||||
|
||||
const QueueModal = ({ open, onClose, queueId }) => {
|
||||
|
@ -70,10 +80,14 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
|||
name: "",
|
||||
color: "",
|
||||
greetingMessage: "",
|
||||
dialogflowId: "",
|
||||
};
|
||||
|
||||
const [colorPickerModalOpen, setColorPickerModalOpen] = useState(false);
|
||||
const [queue, setQueue] = useState(initialState);
|
||||
const [dialogflows, setDialogflows] = useState([]);
|
||||
const { findAll: findAllDialogflows } = useDialogflows();
|
||||
|
||||
const greetingRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -81,6 +95,10 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
|||
if (!queueId) return;
|
||||
try {
|
||||
const { data } = await api.get(`/queue/${queueId}`);
|
||||
if(data.dialogflowId === null) {
|
||||
data.dialogflowId = "";
|
||||
}
|
||||
|
||||
setQueue(prevState => {
|
||||
return { ...prevState, ...data };
|
||||
});
|
||||
|
@ -94,10 +112,20 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
|||
name: "",
|
||||
color: "",
|
||||
greetingMessage: "",
|
||||
dialogflowId: "",
|
||||
});
|
||||
};
|
||||
}, [queueId, open]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadDialogflows = async () => {
|
||||
const list = await findAllDialogflows();
|
||||
setDialogflows(list);
|
||||
}
|
||||
loadDialogflows();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setQueue(initialState);
|
||||
|
@ -105,6 +133,10 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
|||
|
||||
const handleSaveQueue = async values => {
|
||||
try {
|
||||
if(values.dialogflowId === "") {
|
||||
values.dialogflowId = null;
|
||||
}
|
||||
|
||||
if (queueId) {
|
||||
await api.put(`/queue/${queueId}`, values);
|
||||
} else {
|
||||
|
@ -213,6 +245,27 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
|||
margin="dense"
|
||||
/>
|
||||
</div>
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={classes.formControl}
|
||||
margin="dense"
|
||||
>
|
||||
<InputLabel id="dialogflow-selection-input-label">
|
||||
{i18n.t("queueModal.form.dialogflow")}
|
||||
</InputLabel>
|
||||
<Field
|
||||
as={Select}
|
||||
label={i18n.t("queueModal.form.dialogflow")}
|
||||
name="dialogflowId"
|
||||
labelId="dialogflow-selection-label"
|
||||
id="dialogflow-selection"
|
||||
>
|
||||
<MenuItem value={''}> </MenuItem>
|
||||
{dialogflows.map((dialogflow) => (
|
||||
<MenuItem key={dialogflow.id} value={dialogflow.id}>{dialogflow.name}</MenuItem>
|
||||
))}
|
||||
</Field>
|
||||
</FormControl>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
|
|
|
@ -34,6 +34,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
const history = useHistory();
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [useDialogflow, setUseDialogflow] = useState(ticket.contact.useDialogflow);
|
||||
const ticketOptionsMenuOpen = Boolean(anchorEl);
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
|
@ -50,9 +51,9 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
|
||||
const chatEndVal = (data) => {
|
||||
|
||||
if(data){
|
||||
if (data) {
|
||||
|
||||
data = {...data, 'ticketId': ticket.id}
|
||||
data = { ...data, 'ticketId': ticket.id }
|
||||
|
||||
|
||||
|
||||
|
@ -75,12 +76,12 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
};
|
||||
|
||||
|
||||
const handleUpdateTicketStatus = async (e, status, userId, schedulingData={}) => {
|
||||
const handleUpdateTicketStatus = async (e, status, userId, schedulingData = {}) => {
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
|
||||
if(status==='closed'){
|
||||
if (status === 'closed') {
|
||||
|
||||
if (tabOption === 'search') {
|
||||
setTabOption('open')
|
||||
|
@ -93,7 +94,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
});
|
||||
|
||||
}
|
||||
else{
|
||||
else {
|
||||
|
||||
if (tabOption === 'search') {
|
||||
setTabOption('open')
|
||||
|
@ -119,11 +120,21 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
toastError(err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
const handleContactToggleUseDialogflow = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const contact = await api.put(`/contacts/toggleUseDialogflow/${ticket.contact.id}`);
|
||||
setUseDialogflow(contact.data.useDialogflow);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
toastError(err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className={classes.actionButtons}>
|
||||
{ticket.status === "closed" && (
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
function getConfig(name, defaultValue=null) {
|
||||
// If inside a docker container, use window.ENV
|
||||
if( window.ENV !== undefined ) {
|
||||
return window.ENV[name] || defaultValue;
|
||||
}
|
||||
|
||||
return process.env[name] || defaultValue;
|
||||
}
|
||||
|
||||
export function getBackendUrl() {
|
||||
return getConfig('REACT_APP_BACKEND_URL');
|
||||
}
|
||||
|
||||
export function getHoursCloseTicketsAuto() {
|
||||
return getConfig('REACT_APP_HOURS_CLOSE_TICKETS_AUTO');
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import api from "../../services/api";
|
||||
|
||||
const useDialogflows = () => {
|
||||
const findAll = async () => {
|
||||
const { data } = await api.get("/dialogflow");
|
||||
return data;
|
||||
}
|
||||
|
||||
return { findAll };
|
||||
};
|
||||
|
||||
export default useDialogflows;
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import DeviceHubOutlined from "@material-ui/icons/DeviceHubOutlined"
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
|
@ -130,11 +131,18 @@ const MainListItems = (props) => {
|
|||
role={user.profile}
|
||||
perform="settings-view:show"
|
||||
yes={() => (
|
||||
<>
|
||||
<ListItemLink
|
||||
to="/settings"
|
||||
primary={i18n.t("mainDrawer.listItems.settings")}
|
||||
icon={<SettingsOutlinedIcon />}
|
||||
/>
|
||||
<ListItemLink
|
||||
to="/Dialogflows"
|
||||
primary={i18n.t("mainDrawer.listItems.dialogflow")}
|
||||
icon={<DeviceHubOutlined />}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
import { format, parseISO } from "date-fns";
|
||||
import React, { useReducer, useState } from "react";
|
||||
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
makeStyles,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow
|
||||
} from "@material-ui/core";
|
||||
|
||||
import { DeleteOutline, Edit } from "@material-ui/icons";
|
||||
import { toast } from "react-toastify";
|
||||
import ConfirmationModal from "../../components/ConfirmationModal";
|
||||
import DialogflowModal from "../../components/DialogflowModal";
|
||||
import MainContainer from "../../components/MainContainer";
|
||||
import MainHeader from "../../components/MainHeader";
|
||||
import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper";
|
||||
import TableRowSkeleton from "../../components/TableRowSkeleton";
|
||||
import Title from "../../components/Title";
|
||||
import toastError from "../../errors/toastError";
|
||||
import api from "../../services/api";
|
||||
import { i18n } from "../../translate/i18n";
|
||||
import useLoadData from "../Queues/useLoadData.js";
|
||||
import useSocket from "../Queues/useSocket";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
mainPaper: {
|
||||
flex: 1,
|
||||
padding: theme.spacing(1),
|
||||
overflowY: "scroll",
|
||||
...theme.scrollbarStyles,
|
||||
},
|
||||
customTableCell: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
const reducer = (state, action) => {
|
||||
if (action.type === "LOAD_DIALOGFLOWS") {
|
||||
const dialoflows = action.payload;
|
||||
const newDialogflows = [];
|
||||
|
||||
dialoflows.forEach((dialogflow) => {
|
||||
const dialogflowIndex = state.findIndex((q) => q.id === dialogflow.id);
|
||||
if (dialogflowIndex !== -1) {
|
||||
state[dialogflowIndex] = dialogflow;
|
||||
} else {
|
||||
newDialogflows.push(dialogflow);
|
||||
}
|
||||
});
|
||||
|
||||
return [...state, ...newDialogflows];
|
||||
}
|
||||
|
||||
if (action.type === "UPDATE_DIALOGFLOWS") {
|
||||
const dialogflow = action.payload;
|
||||
const dialogflowIndex = state.findIndex((u) => u.id === dialogflow.id);
|
||||
|
||||
if (dialogflowIndex !== -1) {
|
||||
state[dialogflowIndex] = dialogflow;
|
||||
return [...state];
|
||||
} else {
|
||||
return [dialogflow, ...state];
|
||||
}
|
||||
}
|
||||
|
||||
if (action.type === "DELETE_DIALOGFLOW") {
|
||||
const dialogflowId = action.payload;
|
||||
const dialogflowIndex = state.findIndex((q) => q.id === dialogflowId);
|
||||
if (dialogflowIndex !== -1) {
|
||||
state.splice(dialogflowIndex, 1);
|
||||
}
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "RESET") {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const Dialogflows = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
const [dialogflows, dispatch] = useReducer(reducer, []);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [dialogflowModalOpen, setDialogflowModalOpen] = useState(false);
|
||||
const [selectedDialogflow, setSelectedDialogflow] = useState(null);
|
||||
const [confirmModalOpen, setConfirmModalOpen] = useState(false);
|
||||
|
||||
useLoadData(setLoading, dispatch, '/dialogflow', 'LOAD_DIALOGFLOWS');
|
||||
useSocket(dispatch, 'dialogflow', 'UPDATE_DIALOGFLOWS', 'DELETE_DIALOGFLOW', 'dialogflow', 'dialogflowId');
|
||||
|
||||
const handleOpenDialogflowModal = () => {
|
||||
setDialogflowModalOpen(true);
|
||||
setSelectedDialogflow(null);
|
||||
};
|
||||
|
||||
const handleCloseDialogflowModal = () => {
|
||||
setDialogflowModalOpen(false);
|
||||
setSelectedDialogflow(null);
|
||||
};
|
||||
|
||||
const handleEditDialogflow = (dialogflow) => {
|
||||
setSelectedDialogflow(dialogflow);
|
||||
setDialogflowModalOpen(true);
|
||||
};
|
||||
|
||||
const handleCloseConfirmationModal = () => {
|
||||
setConfirmModalOpen(false);
|
||||
setSelectedDialogflow(null);
|
||||
};
|
||||
|
||||
const handleDeleteDialogflow = async (dialogflowId) => {
|
||||
try {
|
||||
await api.delete(`/dialogflow/${dialogflowId}`);
|
||||
toast.success(i18n.t("Dialogflow deleted successfully!"));
|
||||
} catch (err) {
|
||||
toastError(err);
|
||||
}
|
||||
setSelectedDialogflow(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfirmationModal
|
||||
title={
|
||||
selectedDialogflow &&
|
||||
`${i18n.t("dialogflows.confirmationModal.deleteTitle")} ${
|
||||
selectedDialogflow.name
|
||||
}?`
|
||||
}
|
||||
open={confirmModalOpen}
|
||||
onClose={handleCloseConfirmationModal}
|
||||
onConfirm={() => handleDeleteDialogflow(selectedDialogflow.id)}
|
||||
>
|
||||
{i18n.t("dialogflows.confirmationModal.deleteMessage")}
|
||||
</ConfirmationModal>
|
||||
<DialogflowModal
|
||||
open={dialogflowModalOpen}
|
||||
onClose={handleCloseDialogflowModal}
|
||||
dialogflowId={selectedDialogflow?.id}
|
||||
/>
|
||||
<MainHeader>
|
||||
<Title>{i18n.t("dialogflows.title")}</Title>
|
||||
<MainHeaderButtonsWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleOpenDialogflowModal}
|
||||
>
|
||||
{i18n.t("dialogflows.buttons.add")}
|
||||
</Button>
|
||||
</MainHeaderButtonsWrapper>
|
||||
</MainHeader>
|
||||
<Paper className={classes.mainPaper} variant="outlined">
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="center">
|
||||
{i18n.t("dialogflows.table.name")}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{i18n.t("dialogflows.table.projectName")}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{i18n.t("dialogflows.table.language")}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{i18n.t("dialogflows.table.lastUpdate")}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{i18n.t("dialogflows.table.actions")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<>
|
||||
{dialogflows.map((dialogflow) => (
|
||||
<TableRow key={dialogflow.id}>
|
||||
<TableCell align="center">{dialogflow.name}</TableCell>
|
||||
<TableCell align="center">{ dialogflow.projectName}</TableCell>
|
||||
<TableCell align="center">{dialogflow.language}</TableCell>
|
||||
<TableCell align="center">
|
||||
{format(parseISO(dialogflow.updatedAt), "dd/MM/yy HH:mm")}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => handleEditDialogflow(dialogflow)}
|
||||
><Edit />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setSelectedDialogflow(dialogflow);
|
||||
setConfirmModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<DeleteOutline />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{loading && <TableRowSkeleton columns={4} />}
|
||||
</>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
</MainContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dialogflows;
|
|
@ -0,0 +1,22 @@
|
|||
import { useEffect } from "react";
|
||||
import toastError from "../../errors/toastError";
|
||||
import api from "../../services/api";
|
||||
|
||||
const useLoadData = (setLoading, dispatch, route, dispatchType) => {
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data } = await api.get(route);
|
||||
dispatch({ type: dispatchType, payload: data });
|
||||
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
toastError(err);
|
||||
setLoading(false);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
}
|
||||
|
||||
export default useLoadData;
|
|
@ -0,0 +1,24 @@
|
|||
import { useEffect } from "react";
|
||||
import openSocket from "../../services/socket-io";
|
||||
|
||||
const useSocket = (dispatch, socketEvent, typeUpdate, typeDelete, payloadUpdate, payloadDelete) => {
|
||||
useEffect(() => {
|
||||
const socket = openSocket();
|
||||
|
||||
socket.on(socketEvent, (data) => {
|
||||
if (data.action === "update" || data.action === "create") {
|
||||
dispatch({ type: typeUpdate, payload: data[payloadUpdate] });
|
||||
}
|
||||
|
||||
if (data.action === "delete") {
|
||||
dispatch({ type: typeDelete, payload: data[payloadDelete] });
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
|
||||
export default useSocket;
|
|
@ -21,7 +21,7 @@ import { AuthProvider } from "../context/Auth/AuthContext";
|
|||
import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext";
|
||||
import Route from "./Route";
|
||||
|
||||
|
||||
import Dialogflows from "../pages/Dialogflow/";
|
||||
|
||||
const Routes = () => {
|
||||
return (
|
||||
|
@ -47,6 +47,7 @@ const Routes = () => {
|
|||
<Route exact path="/quickAnswers" component={QuickAnswers} isPrivate />
|
||||
<Route exact path="/Settings" component={Settings} isPrivate />
|
||||
<Route exact path="/Queues" component={Queues} isPrivate />
|
||||
<Route exact path="/Dialogflows" component={Dialogflows} isPrivate />
|
||||
</LoggedInLayout>
|
||||
</WhatsAppsProvider>
|
||||
</Switch>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import openSocket from "socket.io-client";
|
||||
import { getBackendUrl } from "../config";
|
||||
|
||||
function connectToSocket() {
|
||||
return openSocket(getBackendUrl());
|
||||
}
|
||||
|
||||
export default connectToSocket;
|
|
@ -155,6 +155,7 @@ const messages = {
|
|||
email: "Email",
|
||||
extraName: "Field name",
|
||||
extraValue: "Value",
|
||||
dialogflow: "Dialogflow",
|
||||
},
|
||||
buttons: {
|
||||
addExtraInfo: "Add information",
|
||||
|
@ -188,6 +189,7 @@ const messages = {
|
|||
form: {
|
||||
name: "Name",
|
||||
color: "Color",
|
||||
dialogflow: "Dialogflow",
|
||||
greetingMessage: "Greeting Message",
|
||||
},
|
||||
buttons: {
|
||||
|
@ -196,6 +198,29 @@ const messages = {
|
|||
cancel: "Cancel",
|
||||
},
|
||||
},
|
||||
dialogflowModal: {
|
||||
title: {
|
||||
add: "Add Project",
|
||||
edit: "Edit Project",
|
||||
},
|
||||
form: {
|
||||
name: "Name",
|
||||
projectName: "Project Name",
|
||||
language: "Language",
|
||||
jsonContent: "JsonContent"
|
||||
},
|
||||
buttons: {
|
||||
okAdd: "Add",
|
||||
okEdit: "Save",
|
||||
cancel: "Cancel",
|
||||
test: "Bot Test",
|
||||
},
|
||||
messages: {
|
||||
testSuccess: "Dialogflow test successfully",
|
||||
addSuccess: "Dialogflow added successfully",
|
||||
editSuccess: "Dialogflow edited successfully",
|
||||
}
|
||||
},
|
||||
userModal: {
|
||||
title: {
|
||||
add: "Add user",
|
||||
|
@ -206,6 +231,7 @@ const messages = {
|
|||
email: "Email",
|
||||
password: "Password",
|
||||
profile: "Profile",
|
||||
whatsapp: "Default Connection",
|
||||
},
|
||||
buttons: {
|
||||
okAdd: "Add",
|
||||
|
@ -248,7 +274,9 @@ const messages = {
|
|||
title: "Transfer Ticket",
|
||||
fieldLabel: "Type to search for users",
|
||||
fieldQueueLabel: "Transfer to queue",
|
||||
fieldConnectionLabel: "Transfer to connection",
|
||||
fieldQueuePlaceholder: "Please select a queue",
|
||||
fieldConnectionPlaceholder: "Please select a connection",
|
||||
noOptions: "No user found with this name",
|
||||
buttons: {
|
||||
ok: "Transfer",
|
||||
|
@ -260,6 +288,7 @@ const messages = {
|
|||
assignedHeader: "Working on",
|
||||
noTicketsTitle: "Nothing here!",
|
||||
noTicketsMessage: "No tickets found with this status or search term.",
|
||||
connectionTitle: "Connection that is currently being used.",
|
||||
buttons: {
|
||||
accept: "Accept",
|
||||
},
|
||||
|
@ -284,6 +313,7 @@ const messages = {
|
|||
administration: "Administration",
|
||||
users: "Users",
|
||||
settings: "Settings",
|
||||
dialogflow: "Dialogflow",
|
||||
},
|
||||
appBar: {
|
||||
user: {
|
||||
|
@ -312,6 +342,24 @@ const messages = {
|
|||
"Are you sure? It cannot be reverted! Tickets in this queue will still exist, but will not have any queues assigned.",
|
||||
},
|
||||
},
|
||||
dialogflows: {
|
||||
title: "Dialogflow",
|
||||
table: {
|
||||
name: "Name",
|
||||
projectName: "Project Name",
|
||||
lamguage: "Language",
|
||||
lastUpdate:"Last Update",
|
||||
actions: "Actions",
|
||||
},
|
||||
buttons: {
|
||||
add: "Add Project",
|
||||
},
|
||||
confirmationModal: {
|
||||
deleteTitle: "Delete",
|
||||
deleteMessage:
|
||||
"Are you sure? It cannot be reverted!",
|
||||
},
|
||||
},
|
||||
queueSelect: {
|
||||
inputLabel: "Queues",
|
||||
},
|
||||
|
@ -340,6 +388,7 @@ const messages = {
|
|||
name: "Name",
|
||||
email: "Email",
|
||||
profile: "Profile",
|
||||
whatsapp: "Default Connection",
|
||||
actions: "Actions",
|
||||
},
|
||||
buttons: {
|
||||
|
@ -371,6 +420,7 @@ const messages = {
|
|||
header: {
|
||||
assignedTo: "Assigned to:",
|
||||
buttons: {
|
||||
dialogflow: "Dialogflow",
|
||||
return: "Return",
|
||||
resolve: "Resolve",
|
||||
reopen: "Reopen",
|
||||
|
@ -392,6 +442,7 @@ const messages = {
|
|||
},
|
||||
ticketOptionsMenu: {
|
||||
delete: "Delete",
|
||||
useQueues: "¿Usar colas?",
|
||||
transfer: "Transfer",
|
||||
confirmationModal: {
|
||||
title: "Delete ticket #",
|
||||
|
@ -453,6 +504,9 @@ const messages = {
|
|||
"This color is already in use, pick another one.",
|
||||
ERR_WAPP_GREETING_REQUIRED:
|
||||
"Greeting message is required if there is more than one queue.",
|
||||
ERR_NO_DIALOG_FOUND: "No Dialogflow found with this ID.",
|
||||
ERR_TEST_SESSION_DIALOG: "Error creating DialogFlow session",
|
||||
ERR_TEST_REPLY_DIALOG: "Error testing DialogFlow configuration",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -158,6 +158,7 @@ const messages = {
|
|||
email: "Correo Electrónico",
|
||||
extraName: "Nombre del Campo",
|
||||
extraValue: "Valor",
|
||||
dialogflow: "Dialogflow",
|
||||
},
|
||||
buttons: {
|
||||
addExtraInfo: "Agregar información",
|
||||
|
@ -191,6 +192,7 @@ const messages = {
|
|||
form: {
|
||||
name: "Nombre",
|
||||
color: "Color",
|
||||
dialogflow: "Dialogflow",
|
||||
greetingMessage: "Mensaje de saludo",
|
||||
},
|
||||
buttons: {
|
||||
|
@ -199,6 +201,29 @@ const messages = {
|
|||
cancel: "Cancelar",
|
||||
},
|
||||
},
|
||||
dialogflowModal: {
|
||||
title: {
|
||||
add: "Agregar cola",
|
||||
edit: "Editar cola",
|
||||
},
|
||||
form: {
|
||||
name: "Nombre",
|
||||
projectName: "Nombre del proyecto",
|
||||
language: "Idioma",
|
||||
jsonContent: "JsonContent",
|
||||
},
|
||||
buttons: {
|
||||
okAdd: "Añadir",
|
||||
okEdit: "Ahorrar",
|
||||
cancel: "Cancelar",
|
||||
test: "Testar Bot",
|
||||
},
|
||||
messages: {
|
||||
testSuccess: "Prueba Dialogflow con éxito",
|
||||
addSuccess: "Dialogflow agregado con éxito",
|
||||
editSuccess: "Dialogflow editado con éxito",
|
||||
}
|
||||
},
|
||||
userModal: {
|
||||
title: {
|
||||
add: "Agregar usuario",
|
||||
|
@ -209,6 +234,7 @@ const messages = {
|
|||
email: "Correo Electrónico",
|
||||
password: "Contraseña",
|
||||
profile: "Perfil",
|
||||
whatsapp: "Conexión estándar",
|
||||
},
|
||||
buttons: {
|
||||
okAdd: "Agregar",
|
||||
|
@ -251,7 +277,9 @@ const messages = {
|
|||
title: "Transferir Ticket",
|
||||
fieldLabel: "Escriba para buscar usuarios",
|
||||
fieldQueueLabel: "Transferir a la cola",
|
||||
fieldConnectionLabel: "Transferir to conexión",
|
||||
fieldQueuePlaceholder: "Seleccione una cola",
|
||||
fieldConnectionPlaceholder: "Seleccione una conexión",
|
||||
noOptions: "No se encontraron usuarios con ese nombre",
|
||||
buttons: {
|
||||
ok: "Transferir",
|
||||
|
@ -262,6 +290,7 @@ const messages = {
|
|||
pendingHeader: "Cola",
|
||||
assignedHeader: "Trabajando en",
|
||||
noTicketsTitle: "¡Nada acá!",
|
||||
connectionTitle: "Conexión que se está utilizando actualmente.",
|
||||
noTicketsMessage:
|
||||
"No se encontraron tickets con este estado o término de búsqueda",
|
||||
buttons: {
|
||||
|
@ -280,6 +309,7 @@ const messages = {
|
|||
mainDrawer: {
|
||||
listItems: {
|
||||
dashboard: "Dashboard",
|
||||
|
||||
connections: "Conexiones",
|
||||
tickets: "Tickets",
|
||||
contacts: "Contactos",
|
||||
|
@ -288,6 +318,7 @@ const messages = {
|
|||
administration: "Administración",
|
||||
users: "Usuarios",
|
||||
settings: "Configuración",
|
||||
dialogflow: "Dialogflow",
|
||||
},
|
||||
appBar: {
|
||||
user: {
|
||||
|
@ -316,6 +347,24 @@ const messages = {
|
|||
"¿Estás seguro? ¡Esta acción no se puede revertir! Los tickets en esa cola seguirán existiendo, pero ya no tendrán ninguna cola asignada.",
|
||||
},
|
||||
},
|
||||
dialoflows: {
|
||||
title: "Dialogflow",
|
||||
table: {
|
||||
name: "Nombre",
|
||||
projectName: "Nombre del proyecto",
|
||||
language: "Idioma",
|
||||
lastUpdate: "Última Actualización",
|
||||
actions: "Comportamiento",
|
||||
},
|
||||
buttons: {
|
||||
add: "Agregar proyecto",
|
||||
},
|
||||
confirmationModal: {
|
||||
deleteTitle: "Eliminar",
|
||||
deleteMessage:
|
||||
"¿Estás seguro? ¡Esta acción no se puede revertir!",
|
||||
},
|
||||
},
|
||||
queueSelect: {
|
||||
inputLabel: "Linhas",
|
||||
},
|
||||
|
@ -345,6 +394,7 @@ const messages = {
|
|||
name: "Nombre",
|
||||
email: "Correo Electrónico",
|
||||
profile: "Perfil",
|
||||
whatsapp: "Conexión estándar",
|
||||
actions: "Acciones",
|
||||
},
|
||||
buttons: {
|
||||
|
@ -376,6 +426,7 @@ const messages = {
|
|||
header: {
|
||||
assignedTo: "Asignado a:",
|
||||
buttons: {
|
||||
dialogflow: "Dialogflow",
|
||||
return: "Devolver",
|
||||
resolve: "Resolver",
|
||||
reopen: "Reabrir",
|
||||
|
@ -398,6 +449,7 @@ const messages = {
|
|||
},
|
||||
ticketOptionsMenu: {
|
||||
delete: "Borrar",
|
||||
useQueues: "Use Queues",
|
||||
transfer: "Transferir",
|
||||
confirmationModal: {
|
||||
title: "¿Borrar ticket #",
|
||||
|
@ -460,6 +512,9 @@ const messages = {
|
|||
"Este color ya está en uso, elija otro.",
|
||||
ERR_WAPP_GREETING_REQUIRED:
|
||||
"El mensaje de saludo es obligatorio cuando hay más de una cola.",
|
||||
ERR_NO_DIALOG_FOUND: "No se encontró Dialogflow con este ID.",
|
||||
ERR_TEST_SESSION_DIALOG: "Error al crear la sesión de dialogflow",
|
||||
ERR_TEST_REPLY_DIALOG: "Error al probar la configuración de DialogFlow",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -80,8 +80,7 @@ const messages = {
|
|||
qrcode: {
|
||||
title: "Esperando leitura do QR Code",
|
||||
content:
|
||||
// "Clique no botão 'QR CODE' e leia o QR Code com o seu celular para iniciar a sessão",
|
||||
"Entre em contato com o suporte para realizar a leitura do QR code",
|
||||
"Clique no botão 'QR CODE' e leia o QR Code com o seu celular para iniciar a sessão",
|
||||
},
|
||||
connected: {
|
||||
title: "Conexão estabelecida!",
|
||||
|
@ -158,6 +157,7 @@ const messages = {
|
|||
email: "Email",
|
||||
extraName: "Nome do campo",
|
||||
extraValue: "Valor",
|
||||
dialogflow: "Dialogflow",
|
||||
},
|
||||
buttons: {
|
||||
addExtraInfo: "Adicionar informação",
|
||||
|
@ -191,6 +191,7 @@ const messages = {
|
|||
form: {
|
||||
name: "Nome",
|
||||
color: "Cor",
|
||||
dialogflow: "Dialogflow",
|
||||
greetingMessage: "Mensagem de saudação",
|
||||
},
|
||||
buttons: {
|
||||
|
@ -199,6 +200,29 @@ const messages = {
|
|||
cancel: "Cancelar",
|
||||
},
|
||||
},
|
||||
dialogflowModal: {
|
||||
title: {
|
||||
add: "Adicionar projeto",
|
||||
edit: "Editar projeto",
|
||||
},
|
||||
form: {
|
||||
name: "Nome",
|
||||
projectName: "Nome do Projeto",
|
||||
language: "Linguagem",
|
||||
jsonContent: "JsonContent",
|
||||
},
|
||||
buttons: {
|
||||
okAdd: "Adicionar",
|
||||
okEdit: "Salvar",
|
||||
cancel: "Cancelar",
|
||||
test: "Testar Bot",
|
||||
},
|
||||
messages: {
|
||||
testSuccess: "Dialogflow testado com sucesso!",
|
||||
addSuccess: "Dialogflow adicionado com sucesso.",
|
||||
editSuccess: "Dialogflow editado com sucesso.",
|
||||
}
|
||||
},
|
||||
userModal: {
|
||||
title: {
|
||||
add: "Adicionar usuário",
|
||||
|
@ -209,6 +233,7 @@ const messages = {
|
|||
email: "Email",
|
||||
password: "Senha",
|
||||
profile: "Perfil",
|
||||
whatsapp: "Conexão Padrão",
|
||||
},
|
||||
buttons: {
|
||||
okAdd: "Adicionar",
|
||||
|
@ -241,7 +266,7 @@ const messages = {
|
|||
search: { title: "Busca" },
|
||||
},
|
||||
search: {
|
||||
placeholder: "Busca telefone/nome",
|
||||
placeholder: "Buscar tickets e mensagens",
|
||||
},
|
||||
buttons: {
|
||||
showAll: "Todos",
|
||||
|
@ -251,7 +276,9 @@ const messages = {
|
|||
title: "Transferir Ticket",
|
||||
fieldLabel: "Digite para buscar usuários",
|
||||
fieldQueueLabel: "Transferir para fila",
|
||||
fieldConnectionLabel: "Transferir para conexão",
|
||||
fieldQueuePlaceholder: "Selecione uma fila",
|
||||
fieldConnectionPlaceholder: "Selecione uma conexão",
|
||||
noOptions: "Nenhum usuário encontrado com esse nome",
|
||||
buttons: {
|
||||
ok: "Transferir",
|
||||
|
@ -264,6 +291,7 @@ const messages = {
|
|||
noTicketsTitle: "Nada aqui!",
|
||||
noTicketsMessage:
|
||||
"Nenhum ticket encontrado com esse status ou termo pesquisado",
|
||||
connectionTitle: "Conexão que está sendo utilizada atualmente.",
|
||||
buttons: {
|
||||
accept: "Aceitar",
|
||||
},
|
||||
|
@ -288,6 +316,7 @@ const messages = {
|
|||
administration: "Administração",
|
||||
users: "Usuários",
|
||||
settings: "Configurações",
|
||||
dialogflow: "Dialogflow",
|
||||
},
|
||||
appBar: {
|
||||
user: {
|
||||
|
@ -316,6 +345,24 @@ const messages = {
|
|||
"Você tem certeza? Essa ação não pode ser revertida! Os tickets dessa fila continuarão existindo, mas não terão mais nenhuma fila atribuída.",
|
||||
},
|
||||
},
|
||||
dialogflows: {
|
||||
title: "Dialogflow",
|
||||
table: {
|
||||
name: "Nome",
|
||||
projectName: "Nome do Projeto",
|
||||
language: "Linguagem",
|
||||
lastUpdate: "Ultima atualização",
|
||||
actions: "Ações",
|
||||
},
|
||||
buttons: {
|
||||
add: "Adicionar Projeto",
|
||||
},
|
||||
confirmationModal: {
|
||||
deleteTitle: "Excluir",
|
||||
deleteMessage:
|
||||
"Você tem certeza? Essa ação não pode ser revertida! e será removida das filas e conexões vinculadas",
|
||||
},
|
||||
},
|
||||
queueSelect: {
|
||||
inputLabel: "Filas",
|
||||
},
|
||||
|
@ -345,6 +392,7 @@ const messages = {
|
|||
name: "Nome",
|
||||
email: "Email",
|
||||
profile: "Perfil",
|
||||
whatsapp: "Conexão Padrão",
|
||||
actions: "Ações",
|
||||
},
|
||||
buttons: {
|
||||
|
@ -376,6 +424,7 @@ const messages = {
|
|||
header: {
|
||||
assignedTo: "Atribuído à:",
|
||||
buttons: {
|
||||
dialogflow: "Dialogflow",
|
||||
return: "Retornar",
|
||||
resolve: "Resolver",
|
||||
reopen: "Reabrir",
|
||||
|
@ -398,6 +447,7 @@ const messages = {
|
|||
},
|
||||
ticketOptionsMenu: {
|
||||
delete: "Deletar",
|
||||
useQueues: "Usar fila?",
|
||||
transfer: "Transferir",
|
||||
confirmationModal: {
|
||||
title: "Deletar o ticket do contato",
|
||||
|
@ -458,6 +508,9 @@ const messages = {
|
|||
"Esta cor já está em uso, escolha outra.",
|
||||
ERR_WAPP_GREETING_REQUIRED:
|
||||
"A mensagem de saudação é obrigatório quando há mais de uma fila.",
|
||||
ERR_NO_DIALOG_FOUND: "Nenhuma Dialogflow encontrado com este ID",
|
||||
ERR_TEST_SESSION_DIALOG: "Erro ao criar sessão do DialogFlow",
|
||||
ERR_TEST_REPLY_DIALOG: "Erro ao testar configuração do DialogFlow",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue