@tiledesk/tiledesk-server 2.18.4 → 2.18.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/app.js +2 -1
- package/event/kbEvent.js +12 -0
- package/event/webhookEvent.js +9 -0
- package/jobs.js +12 -1
- package/lib/analyticsClient.js +60 -0
- package/package.json +2 -2
- package/pubmodules/analytics-publisher/index.js +437 -0
- package/pubmodules/pubModulesManager.js +14 -0
- package/routes/answered.js +73 -0
- package/routes/kb.js +22 -2
- package/routes/project_user.js +51 -20
- package/routes/public-request.js +305 -239
- package/routes/request.js +11 -1
- package/routes/unanswered.js +71 -0
- package/routes/urlPreview.js +57 -0
- package/routes/webhook.js +2 -0
- package/services/Scheduler.js +13 -0
- package/services/aiManager.js +30 -0
- package/services/urlPreviewService.js +50 -0
- package/utils/jobs-worker-queue-manager/JobManagerV2.js +23 -12
- package/utils/jobs-worker-queue-manager/queueManagerClassV2.js +270 -270
- package/utils/transcriptTimezone.js +101 -0
- package/views/messages-layout.jade +130 -0
- package/views/messages.jade +23 -22
- package/views/messages_old.jade +11 -4
- package/.env.sample +0 -141
package/routes/public-request.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
var express = require('express');
|
|
2
2
|
var router = express.Router();
|
|
3
|
+
var csvExpress = require('csv-express');
|
|
3
4
|
var Message = require("../models/message");
|
|
4
5
|
var Request = require("../models/request");
|
|
5
6
|
var User = require("../models/user");
|
|
6
7
|
var winston = require('../config/winston');
|
|
8
|
+
var transcriptTz = require('../utils/transcriptTimezone');
|
|
7
9
|
|
|
8
10
|
var fonts = {
|
|
9
11
|
Roboto: {
|
|
@@ -18,339 +20,403 @@ var PdfPrinter = require('pdfmake');
|
|
|
18
20
|
var printer = new PdfPrinter(fonts);
|
|
19
21
|
// var fs = require('fs');
|
|
20
22
|
|
|
23
|
+
router.get('/:requestid/test/error', async (req, res) => {
|
|
24
|
+
|
|
25
|
+
return res.status(500).render('error', {
|
|
26
|
+
title: 'Tiledesk',
|
|
27
|
+
error: "An error occurred while getting the request"
|
|
28
|
+
});
|
|
21
29
|
|
|
30
|
+
});
|
|
22
31
|
|
|
32
|
+
router.get('/:requestid/messages', function(req, res) {
|
|
23
33
|
|
|
34
|
+
winston.debug(req.params);
|
|
35
|
+
winston.debug("here");
|
|
36
|
+
return Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec(function(err, messages) {
|
|
37
|
+
if (err) {
|
|
38
|
+
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
39
|
+
}
|
|
24
40
|
|
|
41
|
+
if(!messages){
|
|
42
|
+
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
43
|
+
}
|
|
25
44
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
winston.debug(req.params);
|
|
29
|
-
winston.debug("here");
|
|
30
|
-
return Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec(function(err, messages) {
|
|
31
|
-
if (err) {
|
|
32
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if(!messages){
|
|
36
|
-
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
37
|
-
}
|
|
45
|
+
return res.json(messages);
|
|
46
|
+
});
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
});
|
|
48
|
+
});
|
|
41
49
|
|
|
42
|
-
});
|
|
43
50
|
|
|
51
|
+
router.get('/:requestid/messages.html', async (req, res) => {
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
winston.debug(req.params);
|
|
54
|
+
winston.debug("here");
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec(function(err, messages) {
|
|
50
|
-
if (err) {
|
|
51
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
52
|
-
}
|
|
56
|
+
try {
|
|
57
|
+
let messages = await Message.find({ "recipient": req.params.requestid }).sort({createdAt: 'asc'}).exec();
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
56
|
-
}
|
|
59
|
+
let filteredMessages = await filterMessages(messages);
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
brandName: process.env.BRAND_NAME || null,
|
|
62
|
-
brandLogo: process.env.BRAND_LOGO || null
|
|
63
|
-
});
|
|
64
|
-
});
|
|
61
|
+
let idProject = filteredMessages[0] && filteredMessages[0].id_project ? String(filteredMessages[0].id_project) : undefined;
|
|
62
|
+
let tz = await transcriptTz.resolveTranscriptTimezone(req, idProject);
|
|
63
|
+
let messagesForView = attachTranscriptDisplayTimes(filteredMessages, tz);
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
return res.render('messages',
|
|
66
|
+
{
|
|
67
|
+
title: 'Tiledesk',
|
|
68
|
+
messages: messagesForView,
|
|
69
|
+
brandName: process.env.BRAND_NAME || null,
|
|
70
|
+
brandLogo: process.env.BRAND_LOGO || null
|
|
71
|
+
});
|
|
67
72
|
|
|
73
|
+
} catch (error) {
|
|
74
|
+
winston.error("Error getting messages: ", error);
|
|
75
|
+
|
|
76
|
+
return res.status(500).render('error', {
|
|
77
|
+
title: 'Tiledesk',
|
|
78
|
+
error: "An error occurred while getting the messages"
|
|
79
|
+
});
|
|
68
80
|
|
|
69
|
-
|
|
81
|
+
}
|
|
70
82
|
|
|
71
|
-
|
|
72
|
-
winston.debug("here");
|
|
73
|
-
return Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).lean().exec(function(err, messages) {
|
|
74
|
-
if (err) {
|
|
75
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
76
|
-
}
|
|
83
|
+
});
|
|
77
84
|
|
|
78
|
-
if(!messages){
|
|
79
|
-
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
80
|
-
}
|
|
81
85
|
|
|
82
|
-
messages.forEach(function(element) {
|
|
83
86
|
|
|
84
|
-
var channel_name = "";
|
|
85
|
-
if (element.channel && element.channel.name) {
|
|
86
|
-
channel_name = element.channel.name;
|
|
87
|
-
}
|
|
88
|
-
delete element.channel;
|
|
89
|
-
element.channel_name = channel_name;
|
|
90
87
|
|
|
91
|
-
|
|
92
|
-
});
|
|
88
|
+
router.get('/:requestid/messages.csv', async function(req, res) {
|
|
93
89
|
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
var requestid = req.params.requestid;
|
|
91
|
+
var csvFilename = requestid.replace(/["\\\r\n]/g, '_') + '.csv';
|
|
96
92
|
|
|
97
|
-
|
|
98
|
-
});
|
|
93
|
+
try {
|
|
94
|
+
let messages = await Message.find({"recipient": requestid}).sort({createdAt: 'asc'}).lean().exec();
|
|
99
95
|
|
|
100
|
-
|
|
96
|
+
let filteredMessages = await filterMessages(messages);
|
|
101
97
|
|
|
98
|
+
let tz = await transcriptTz.resolveTranscriptTimezone(req, firstMessageProjectId(filteredMessages));
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if(!messages){
|
|
113
|
-
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
var text = "Chat transcript:\n" //+ req.project.name;
|
|
118
|
-
|
|
119
|
-
messages.forEach(function(element) {
|
|
120
|
-
text = text + "[ " + element.createdAt.toLocaleString('en', { timeZone: 'UTC' })+ "] " + element.senderFullname + ": " + element.text + "\n";
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
res.set({"Content-Disposition":"attachment; filename=\"transcript.txt\""});
|
|
125
|
-
res.send(text);
|
|
100
|
+
let rows = filteredMessages.map(function(m) {
|
|
101
|
+
return {
|
|
102
|
+
createdAt: transcriptTz.formatTranscriptInstant(m.createdAt, tz),
|
|
103
|
+
senderFullname: m.senderFullname != null ? String(m.senderFullname) : '',
|
|
104
|
+
text: m.text != null ? String(m.text) : ''
|
|
105
|
+
};
|
|
126
106
|
});
|
|
127
107
|
|
|
128
|
-
|
|
108
|
+
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
109
|
+
res.setHeader('Content-Disposition', 'attachment; filename="' + csvFilename + '"');
|
|
129
110
|
|
|
111
|
+
if (rows.length === 0) {
|
|
112
|
+
return res.send(['createdAt', 'senderFullname', 'text'].join(csvExpress.separator) + '\r\n');
|
|
113
|
+
}
|
|
130
114
|
|
|
115
|
+
return res.csv(rows, true);
|
|
131
116
|
|
|
132
|
-
|
|
117
|
+
} catch (err) {
|
|
118
|
+
winston.error('public-request messages.csv', err);
|
|
119
|
+
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
120
|
+
}
|
|
133
121
|
|
|
122
|
+
});
|
|
134
123
|
|
|
135
|
-
winston.debug(req.params);
|
|
136
|
-
winston.debug("here");
|
|
137
|
-
return Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec(function(err, messages) {
|
|
138
|
-
if (err) {
|
|
139
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
140
|
-
}
|
|
141
124
|
|
|
125
|
+
router.get('/:requestid/messages.txt', function(req, res) {
|
|
142
126
|
|
|
143
|
-
|
|
127
|
+
winston.debug(req.params);
|
|
128
|
+
winston.debug("here");
|
|
129
|
+
Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec()
|
|
130
|
+
.then(function (messages) {
|
|
131
|
+
if (!messages) {
|
|
144
132
|
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
145
133
|
}
|
|
134
|
+
return transcriptTz.resolveTranscriptTimezone(req, firstMessageProjectId(messages))
|
|
135
|
+
.then(function (tz) {
|
|
136
|
+
var text = "Chat transcript:\n";
|
|
137
|
+
messages.forEach(function(element) {
|
|
138
|
+
text = text + "[ " + transcriptTz.formatTranscriptInstant(element.createdAt, tz) + "] " + element.senderFullname + ": " + element.text + "\n";
|
|
139
|
+
});
|
|
140
|
+
res.set({"Content-Disposition":"attachment; filename=\"transcript.txt\""});
|
|
141
|
+
res.send(text);
|
|
142
|
+
});
|
|
143
|
+
})
|
|
144
|
+
.catch(function (err) {
|
|
145
|
+
winston.error('public-request messages.txt', err);
|
|
146
|
+
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
147
|
+
});
|
|
146
148
|
|
|
149
|
+
});
|
|
147
150
|
|
|
148
|
-
var docDefinition = {
|
|
149
|
-
content: [
|
|
150
|
-
{ text: 'Chat Transcript', style: 'header' },
|
|
151
|
-
{
|
|
152
|
-
ul: [
|
|
153
|
-
// 'item 1',
|
|
154
|
-
// 'item 2',
|
|
155
|
-
// 'item 3'
|
|
156
|
-
]
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
],
|
|
160
|
-
styles: {
|
|
161
|
-
header: {
|
|
162
|
-
bold: true,
|
|
163
|
-
fontSize: 15
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
defaultStyle: {
|
|
167
|
-
fontSize: 12
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
151
|
|
|
171
|
-
|
|
172
152
|
|
|
173
|
-
|
|
174
|
-
docDefinition.content[1].ul.push("[ " + element.createdAt.toLocaleString('en', { timeZone: 'UTC' })+ "] " + element.senderFullname + ": " + element.text );
|
|
175
|
-
});
|
|
153
|
+
router.get('/:requestid/messages.pdf', async function(req, res) {
|
|
176
154
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
155
|
+
var requestid = req.params.requestid;
|
|
156
|
+
var pdfFilename = requestid.replace(/["\\\r\n]/g, '_') + '.pdf';
|
|
157
|
+
|
|
158
|
+
winston.debug(req.params);
|
|
159
|
+
winston.debug("here");
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
let messages = await Message.find({"recipient": requestid}).sort({createdAt: 'asc'}).exec();
|
|
163
|
+
|
|
164
|
+
let filteredMessages = await filterMessages(messages);
|
|
165
|
+
|
|
166
|
+
let tz = await transcriptTz.resolveTranscriptTimezone(req, firstMessageProjectId(filteredMessages));
|
|
167
|
+
let docDefinition = buildTranscriptPdfDocDefinition(filteredMessages, tz);
|
|
168
|
+
let pdfDoc = printer.createPdfKitDocument(docDefinition);
|
|
181
169
|
|
|
182
170
|
res.setHeader('Content-Type', 'application/pdf');
|
|
183
|
-
res.setHeader('Content-Disposition', 'attachment; filename=
|
|
184
|
-
|
|
171
|
+
res.setHeader('Content-Disposition', 'attachment; filename="' + pdfFilename + '"');
|
|
185
172
|
|
|
186
173
|
pdfDoc.pipe(res);
|
|
187
174
|
pdfDoc.end();
|
|
175
|
+
} catch (err) {
|
|
176
|
+
winston.error('public-request messages.pdf', err);
|
|
177
|
+
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
178
|
+
}
|
|
188
179
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
});
|
|
180
|
+
});
|
|
194
181
|
|
|
195
182
|
|
|
196
183
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
winston.debug(req.params);
|
|
200
|
-
winston.debug("here");
|
|
201
|
-
return Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec(function(err, messages) {
|
|
202
|
-
if (err) {
|
|
203
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
204
|
-
}
|
|
184
|
+
router.get('/:requestid/messages-user.html', async function(req, res) {
|
|
205
185
|
|
|
206
|
-
|
|
186
|
+
winston.debug(req.params);
|
|
187
|
+
winston.debug("here");
|
|
188
|
+
try {
|
|
189
|
+
let messages = await Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec();
|
|
190
|
+
messages = messages.filter(m => m.sender != "system" );
|
|
207
191
|
|
|
192
|
+
if (!messages || messages.length === 0) {
|
|
193
|
+
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
194
|
+
}
|
|
208
195
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
196
|
+
var idProject = messages[0] && messages[0].id_project ? String(messages[0].id_project) : undefined;
|
|
197
|
+
var tz = await transcriptTz.resolveTranscriptTimezone(req, idProject);
|
|
198
|
+
var messagesForView = attachTranscriptDisplayTimes(messages, tz);
|
|
213
199
|
|
|
214
|
-
|
|
200
|
+
return res.render('messages', { title: 'Tiledesk', messages: messagesForView,
|
|
201
|
+
brandName: process.env.BRAND_NAME || null,
|
|
202
|
+
brandLogo: process.env.BRAND_LOGO || null
|
|
215
203
|
});
|
|
204
|
+
} catch (err) {
|
|
205
|
+
winston.error('public-request messages-user.html', err);
|
|
206
|
+
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
207
|
+
}
|
|
216
208
|
|
|
217
|
-
|
|
209
|
+
});
|
|
218
210
|
|
|
219
211
|
|
|
220
212
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
winston.debug(req.params);
|
|
224
|
-
winston.debug("here");
|
|
225
|
-
return Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec(function(err, messages) {
|
|
226
|
-
if (err) {
|
|
227
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
228
|
-
}
|
|
213
|
+
router.get('/:requestid/messages-user.txt', function(req, res) {
|
|
229
214
|
|
|
230
|
-
|
|
215
|
+
winston.debug(req.params);
|
|
216
|
+
winston.debug("here");
|
|
217
|
+
Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec()
|
|
218
|
+
.then(function (messages) {
|
|
219
|
+
if (!messages || messages.length === 0) {
|
|
231
220
|
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
232
221
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
222
|
+
messages = messages.filter(m => m.sender != "system" );
|
|
223
|
+
if (messages.length === 0) {
|
|
224
|
+
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
225
|
+
}
|
|
226
|
+
return transcriptTz.resolveTranscriptTimezone(req, firstMessageProjectId(messages))
|
|
227
|
+
.then(function (tz) {
|
|
228
|
+
var text = "Chat transcript:\n";
|
|
229
|
+
messages.forEach(function(element) {
|
|
230
|
+
text = text + "[ " + transcriptTz.formatTranscriptInstant(element.createdAt, tz) + "] " + element.senderFullname + ": " + element.text + "\n";
|
|
231
|
+
});
|
|
232
|
+
res.set({"Content-Disposition":"attachment; filename=\"transcript.txt\""});
|
|
233
|
+
res.send(text);
|
|
234
|
+
});
|
|
235
|
+
})
|
|
236
|
+
.catch(function (err) {
|
|
237
|
+
winston.error('public-request messages-user.txt', err);
|
|
238
|
+
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
246
239
|
});
|
|
247
240
|
|
|
248
|
-
|
|
241
|
+
});
|
|
249
242
|
|
|
250
243
|
|
|
251
|
-
|
|
244
|
+
router.get('/:requestid/messages-user.pdf', async function(req, res) {
|
|
252
245
|
|
|
246
|
+
var requestid = req.params.requestid;
|
|
247
|
+
var pdfFilename = requestid.replace(/["\\\r\n]/g, '_') + '.pdf';
|
|
253
248
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return Message.find({"recipient": req.params.requestid}).sort({createdAt: 'asc'}).exec(function(err, messages) {
|
|
257
|
-
if (err) {
|
|
258
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
259
|
-
}
|
|
249
|
+
winston.debug(req.params);
|
|
250
|
+
winston.debug("here");
|
|
260
251
|
|
|
261
|
-
|
|
252
|
+
try {
|
|
253
|
+
let messages = await Message.find({"recipient": requestid}).sort({createdAt: 'asc'}).exec();
|
|
262
254
|
|
|
255
|
+
if (!messages || messages.length === 0) {
|
|
256
|
+
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
257
|
+
}
|
|
263
258
|
|
|
264
|
-
|
|
265
|
-
if(!messages){
|
|
266
|
-
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
267
|
-
}
|
|
259
|
+
messages = messages.filter(m => m.sender != "system" );
|
|
268
260
|
|
|
261
|
+
if (messages.length === 0) {
|
|
262
|
+
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
263
|
+
}
|
|
269
264
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
{
|
|
274
|
-
ul: [
|
|
275
|
-
// 'item 1',
|
|
276
|
-
// 'item 2',
|
|
277
|
-
// 'item 3'
|
|
278
|
-
]
|
|
279
|
-
},
|
|
280
|
-
|
|
281
|
-
],
|
|
282
|
-
styles: {
|
|
283
|
-
header: {
|
|
284
|
-
bold: true,
|
|
285
|
-
fontSize: 15
|
|
286
|
-
}
|
|
287
|
-
},
|
|
288
|
-
defaultStyle: {
|
|
289
|
-
fontSize: 12
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
messages.forEach(function(element) {
|
|
296
|
-
docDefinition.content[1].ul.push("[ " + element.createdAt.toLocaleString('en', { timeZone: 'UTC' })+ "] " + element.senderFullname + ": " + element.text );
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
console.log(docDefinition);
|
|
300
|
-
|
|
301
|
-
var pdfDoc = printer.createPdfKitDocument(docDefinition);
|
|
302
|
-
// pdfDoc.pipe(fs.createWriteStream('lists.pdf'));
|
|
265
|
+
let tz = await transcriptTz.resolveTranscriptTimezone(req, firstMessageProjectId(messages));
|
|
266
|
+
let docDefinition = buildTranscriptPdfDocDefinition(messages, tz);
|
|
267
|
+
let pdfDoc = printer.createPdfKitDocument(docDefinition);
|
|
303
268
|
|
|
304
269
|
res.setHeader('Content-Type', 'application/pdf');
|
|
305
|
-
res.setHeader('Content-Disposition', 'attachment; filename=
|
|
306
|
-
|
|
270
|
+
res.setHeader('Content-Disposition', 'attachment; filename="' + pdfFilename + '"');
|
|
271
|
+
|
|
307
272
|
pdfDoc.pipe(res);
|
|
308
273
|
pdfDoc.end();
|
|
274
|
+
} catch (err) {
|
|
275
|
+
winston.error('public-request messages-user.pdf', err);
|
|
276
|
+
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
277
|
+
}
|
|
309
278
|
|
|
279
|
+
});
|
|
310
280
|
|
|
311
|
-
|
|
312
|
-
});
|
|
313
281
|
|
|
314
|
-
|
|
282
|
+
router.get('/:requestid/messages-user.csv', function(req, res) {
|
|
315
283
|
|
|
284
|
+
var requestid = req.params.requestid;
|
|
285
|
+
var csvFilename = requestid.replace(/["\\\r\n]/g, '_') + '.csv';
|
|
316
286
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
287
|
+
winston.debug(req.params);
|
|
288
|
+
winston.debug("here");
|
|
289
|
+
Message.find({"recipient": requestid}).sort({createdAt: 'asc'}).lean().exec()
|
|
290
|
+
.then(function (messages) {
|
|
291
|
+
if (!messages || messages.length === 0) {
|
|
292
|
+
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
324
293
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
//skip info message
|
|
330
|
-
if(!messages){
|
|
294
|
+
messages = messages.filter(m => m.sender != "system" );
|
|
295
|
+
if (messages.length === 0) {
|
|
331
296
|
return res.status(404).send({success: false, msg: 'Object not found.'});
|
|
332
297
|
}
|
|
298
|
+
return transcriptTz.resolveTranscriptTimezone(req, firstMessageProjectId(messages))
|
|
299
|
+
.then(function (tz) {
|
|
300
|
+
var rows = messages.map(function(m) {
|
|
301
|
+
return {
|
|
302
|
+
createdAt: transcriptTz.formatTranscriptInstant(m.createdAt, tz),
|
|
303
|
+
senderFullname: m.senderFullname != null ? String(m.senderFullname) : '',
|
|
304
|
+
text: m.text != null ? String(m.text) : ''
|
|
305
|
+
};
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
309
|
+
res.setHeader('Content-Disposition', 'attachment; filename="' + csvFilename + '"');
|
|
310
|
+
|
|
311
|
+
if (rows.length === 0) {
|
|
312
|
+
return res.send(['createdAt', 'senderFullname', 'text'].join(csvExpress.separator) + '\r\n');
|
|
313
|
+
}
|
|
333
314
|
|
|
315
|
+
return res.csv(rows, true);
|
|
316
|
+
});
|
|
317
|
+
})
|
|
318
|
+
.catch(function (err) {
|
|
319
|
+
winston.error('public-request messages-user.csv', err);
|
|
320
|
+
return res.status(500).send({success: false, msg: 'Error getting object.'});
|
|
321
|
+
});
|
|
334
322
|
|
|
335
|
-
|
|
323
|
+
});
|
|
336
324
|
|
|
337
|
-
|
|
338
|
-
if (element.channel && element.channel.name) {
|
|
339
|
-
channel_name = element.channel.name;
|
|
340
|
-
}
|
|
341
|
-
delete element.channel;
|
|
342
|
-
element.channel_name = channel_name;
|
|
325
|
+
function filterMessages(messages) {
|
|
343
326
|
|
|
344
|
-
|
|
345
|
-
|
|
327
|
+
if (messages[0].text === "welcome" || messages[0].text === "/start") {
|
|
328
|
+
messages = messages.slice(1);
|
|
329
|
+
}
|
|
330
|
+
return messages.filter(m => m.sender !== "system" );
|
|
346
331
|
|
|
332
|
+
}
|
|
347
333
|
|
|
348
|
-
|
|
349
|
-
|
|
334
|
+
function firstMessageProjectId(messages) {
|
|
335
|
+
return messages && messages[0] && messages[0].id_project ? String(messages[0].id_project) : undefined;
|
|
336
|
+
}
|
|
350
337
|
|
|
351
|
-
|
|
338
|
+
function attachTranscriptDisplayTimes(messages, timeZone) {
|
|
339
|
+
return messages.map(function (m) {
|
|
340
|
+
var o = typeof m.toObject === 'function' ? m.toObject() : m;
|
|
341
|
+
return Object.assign({}, o, {
|
|
342
|
+
transcriptDisplayTime: transcriptTz.formatTranscriptInstant(m.createdAt, timeZone)
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** Footer line aligned with views/messages.jade (Powered by …). */
|
|
348
|
+
function transcriptPdfFooterLine() {
|
|
349
|
+
var brand = process.env.BRAND_NAME;
|
|
350
|
+
if (brand) {
|
|
351
|
+
return 'Powered by ' + brand;
|
|
352
|
+
}
|
|
353
|
+
return 'Powered by Tiledesk';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* pdfmake document for a chat transcript: title, message blocks (sender, body, time), footer.
|
|
358
|
+
* Simplified layout inspired by messages.jade / messages-layout.jade.
|
|
359
|
+
*/
|
|
360
|
+
function buildTranscriptPdfDocDefinition(messages, tz) {
|
|
361
|
+
var innerWidthPt = 515;
|
|
362
|
+
var content = [
|
|
363
|
+
{ text: 'Chat transcript', style: 'pdfTitle', alignment: 'center', margin: [0, 0, 0, 18] }
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
messages.forEach(function (element, index) {
|
|
367
|
+
if (index > 0) {
|
|
368
|
+
content.push({
|
|
369
|
+
canvas: [
|
|
370
|
+
{ type: 'line', x1: 0, y1: 0, x2: innerWidthPt, y2: 0, lineWidth: 0.5, lineColor: '#e2e8f0' }
|
|
371
|
+
],
|
|
372
|
+
margin: [0, 4, 0, 12]
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
var sender = element.senderFullname || 'Unknown';
|
|
377
|
+
var body = element.text != null ? String(element.text) : '';
|
|
378
|
+
var timeStr = transcriptTz.formatTranscriptInstant(element.createdAt, tz);
|
|
379
|
+
|
|
380
|
+
content.push({
|
|
381
|
+
stack: [
|
|
382
|
+
{ text: sender, style: 'pdfSender' },
|
|
383
|
+
{ text: body, style: 'pdfBody', margin: [0, 6, 0, 2] },
|
|
384
|
+
{
|
|
385
|
+
columns: [
|
|
386
|
+
{ width: '*', text: '' },
|
|
387
|
+
{ width: 'auto', text: timeStr, style: 'pdfMeta' }
|
|
388
|
+
],
|
|
389
|
+
columnGap: 0
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
margin: [0, 0, 0, 2]
|
|
352
393
|
});
|
|
394
|
+
});
|
|
353
395
|
|
|
396
|
+
content.push({
|
|
397
|
+
text: transcriptPdfFooterLine(),
|
|
398
|
+
style: 'pdfFooter',
|
|
399
|
+
alignment: 'center',
|
|
400
|
+
margin: [0, 24, 0, 0]
|
|
354
401
|
});
|
|
355
402
|
|
|
403
|
+
return {
|
|
404
|
+
pageSize: 'A4',
|
|
405
|
+
pageMargins: [40, 44, 40, 44],
|
|
406
|
+
content: content,
|
|
407
|
+
styles: {
|
|
408
|
+
pdfTitle: { fontSize: 20, bold: true, color: '#0f172a' },
|
|
409
|
+
pdfSender: { fontSize: 11, bold: true, color: '#0f172a' },
|
|
410
|
+
pdfBody: { fontSize: 10, color: '#334155' },
|
|
411
|
+
pdfMeta: { fontSize: 8, color: '#64748b', alignment: 'right' },
|
|
412
|
+
pdfFooter: { fontSize: 9, color: '#64748b' }
|
|
413
|
+
},
|
|
414
|
+
defaultStyle: {
|
|
415
|
+
font: 'Roboto',
|
|
416
|
+
fontSize: 10,
|
|
417
|
+
lineHeight: 1.4
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
356
422
|
module.exports = router;
|
package/routes/request.js
CHANGED
|
@@ -314,6 +314,11 @@ router.patch('/:requestid', function (req, res) {
|
|
|
314
314
|
requestEvent.emit("request.update", request);
|
|
315
315
|
requestEvent.emit("request.update.comment", { comment: "PATCH", request: request }); //Deprecated
|
|
316
316
|
requestEvent.emit("request.updated", { comment: "PATCH", request: request, patch: update });
|
|
317
|
+
|
|
318
|
+
if (update.rating != null) {
|
|
319
|
+
requestEvent.emit("request.satisfaction", { request: request, patch: update });
|
|
320
|
+
}
|
|
321
|
+
|
|
317
322
|
return res.json(request);
|
|
318
323
|
});
|
|
319
324
|
|
|
@@ -1009,8 +1014,13 @@ router.put('/:requestid/tag', async (req, res) => {
|
|
|
1009
1014
|
.execPopulate();
|
|
1010
1015
|
|
|
1011
1016
|
requestEvent.emit("request.update", populatedRequest)
|
|
1017
|
+
|
|
1018
|
+
if (adding_tags.length > 0) {
|
|
1019
|
+
requestEvent.emit("request.tag.update", { request: populatedRequest, tags: adding_tags });
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1012
1022
|
res.status(200).send(updatedRequest)
|
|
1013
|
-
|
|
1023
|
+
|
|
1014
1024
|
if (process.env.NODE_ENV !== 'test') {
|
|
1015
1025
|
scheduleTags(id_project, adding_tags);
|
|
1016
1026
|
}
|