-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.js
167 lines (146 loc) · 5.36 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* jslint node: true */
'use strict';
const fs = require('fs');
const EventEmitter = require('events');
const express = require('express');
const app = express();
const circuit = require('./circuit');
const mailer = require('./mailer');
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const jsonfile = require('jsonfile');
const port = process.env.PORT || 3100;
const config = require('./config.json');
const emitter = new EventEmitter();
const db = './complaints.json';
let supportUserIds;
// Serve pages
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/node_modules'));
// Create complaints.json file if not exits
const exists = fs.existsSync(db);
!exists && fs.writeFileSync(db, '[]');
const complaints = jsonfile.readFileSync(db);
// Lookup complaint and verify its for the public thread and
// sent by the support team
function getComplaint(data) {
const complaint = complaints.find(c => c.convId === data.convId);
if (!complaint) {
console.error(`No complaint found in DB for convId: ${data.convId}`);
return;
}
if (complaint.thread !== data.thread) {
console.error('Message on a internal thread. Skip it.');
return;
}
if (data.fromCustomer) {
// Complaint page is already updated locally in browser
// Might want to send an email to customer letting him/her know
// that reply was recevied.
return;
}
return complaint;
}
// Initialize Circuit API
circuit.init(emitter)
.then(() => circuit.getUsersByEmail(config.supportUsers))
.then(users =>
supportUserIds = users.map(user => user.userId))
.catch(console.error);
// Email support message to customer
emitter.on('message-received', async data => {
let complaint = getComplaint(data);
if (complaint) {
// Send email to customer notifying of update
mailer.sendUpdate(complaint, data.message, complaint.complaintId);
}
});
// Client socket.io connections
io.on('connection', async socket => {
let query = socket.handshake.query;
console.log('socket connected', socket.id);
// New complaint from form
socket.on('new-complaint', async data => {
console.log('new-complaint', data);
try {
// Create group conversation
let conv = await circuit.createConversation(supportUserIds, data.name);
// Create complaint in database
let newComplaintId = complaints.length ? complaints[complaints.length - 1].complaintId + 1 : config.complaintIdStart;
let newComplaint = {
convId: conv.convId,
complaintId: newComplaintId,
customer: {
name: data.name,
email: data.email,
topic: data.topic
}
}
// Post initial message which creates the thread for customer communication
await circuit.sendMessage(conv.convId, {
subject: `New complaint: ${newComplaintId}`,
content: `Name: ${data.name}<br>Email: <a href="${data.email}">${data.email}</a><br>Topic: ${data.topic}<br>`
});
// Post initial complaint message. This will become the communication thread with the customer
const customerThread = await circuit.sendMessage(conv.convId, {
subject: '*** CUSTOMER COMMUNICATION THREAD ***',
content: data.message
});
// Save threadId in db. Subsequent messages to/from customer go into this thread
newComplaint.thread = customerThread.itemId;
complaints.push(newComplaint)
jsonfile.writeFile(db, complaints, { spaces: 2 }, err => {
if (err) {
console.error('Error updating the complaint database', err);
return;
}
});
socket.emit('complaint-created', newComplaintId);
// Send email to customer
mailer.sendInitialMsg(newComplaint);
} catch (err) {
console.error('Error creating new complaint', err);
}
});
// Get complaint from DB and return to browser
socket.on('get-complaint', async id => {
const complaint = complaints.find(c => c.complaintId === id);
if (!complaint) {
console.error(`No complaint found in DB for complaint: ${id}`);
return;
}
const messages = await circuit.getMessages(complaint.convId, complaint.thread);
socket.emit('get-complaint-response', {
complaint: complaint,
messages: messages,
});
});
// New message from customer. Post it in public communication thread
socket.on('new-message', async data => {
const complaint = complaints.find(c => c.complaintId === data.complaintId);
if (!complaint) {
console.error(`No complaint found in DB for complaint: ${data.complaintId}`);
return;
}
await circuit.sendMessage(complaint.convId, {
parentId: complaint.thread,
content: data.message
});
socket.emit('new-message-response');
});
// Look for messages from back office support etc., i.e. for messages in
// the public communication thread not sent by the customer (bot user)
// Then update the UI (in case its page it open) and send email to customer.
emitter.on('message-received', async data => {
if (getComplaint(data)) {
// Send an event to the UI with the new message and append the new message
socket.emit('new-support-message', data);
}
});
emitter.on('thread-updated', async data => {
if (getComplaint(data)) {
socket.emit('thread-updated');
}
});
});
server.listen(port, _ => console.log(`Server listening at port ${port}`));