]> git.r.bdr.sh - rbdr/dead-drop/blame - lib/controllers/recordings.js
Update readme and changelog
[rbdr/dead-drop] / lib / controllers / recordings.js
CommitLineData
7404eac9
RBR
1'use strict';
2
3const Joi = require('joi');
4const Pify = require('pify');
5const Redis = require('redis');
6const Twilio = require('twilio');
7
8const internals = {};
9
10internals.kContentType = 'application/xml'; // The content type used to respond
11internals.kLanguage = 'es-mx'; // the language to use
12internals.kMaxMessageLength = 30; // max message length in seconds
7404eac9 13internals.kRecordingsSet = 'recordings';
e5fdb2d4
RBR
14internals.kRecordMessage = 'Graba tu mensaje despues del bip y ' +
15 'presiona cualquier tecla para finalizar. '; // the recording message
7404eac9
RBR
16internals.kConfirmationMessage = 'Gracias. Tu mensaje es el número: ';
17internals.kNotFoundMessage = 'Mensaje no encontrado. Adiós!';
18
19internals.kRecordingSchema = Joi.object().keys({
20 url: Joi.string().required()
21});
22
23/**
24 * Handles the HTTP requests for the recording menu
25 *
26 * @class RecordingsController
27 * @param {DeadDrop.tConfiguration} config The configuration to
28 * initialize.
29 */
30module.exports = internals.RecordingsController = class RecordingsController {
31 constructor(config) {
32
33 this._redis = Redis.createClient(config.redis);
34
35 // Log an error if it happens.
36 this._redis.on('error', (err) => {
37
38 console.error(err);
39 });
40 }
41
42 /**
43 * Start recording process
44 *
45 * @function startRecording
46 * @memberof RecordingsController
47 * @instance
48 * @return {generator} a koa compatible handler generator function
49 */
50 startRecording() {
51
52 return function * () {
53
54 const response = new Twilio.TwimlResponse();
55
56 // the action will default to post in the same URL, so no change
57 // required there.
58 response.say(internals.kRecordMessage, { language: internals.kLanguage })
59 .record({
60 maxLength: internals.kMaxMessageLength
61 });
62
63 this.type = internals.kContentType;
64 this.body = response.toString();
65 };
66 }
67
68 /**
69 * Saves the recording for later use
70 *
71 * @function saveRecording
72 * @memberof RecordingsController
73 * @instance
74 * @return {generator} a koa compatible handler generator function
75 */
76 saveRecording() {
77
78 const self = this;
79
80 return function * () {
81
82 const zadd = Pify(self._redis.zadd.bind(self._redis));
83
84 const response = new Twilio.TwimlResponse();
85
86 const id = Date.now().toString().substr(2,10);
87 const url = this.request.body.RecordingUrl;
88 const separatedId = id.split('').join('. ');
89 const recording = {
90 url
91 };
92
93 yield self._validate(recording).catch((err) => {
94
95 this.throw(err.message, 422);
96 });
97
98 // Add to ordered set for quick fetches, and set for random fetches
99 yield zadd(internals.kRecordingsSet, parseInt(id), url);
100
101 response.say(`${internals.kConfirmationMessage}${separatedId}`, { language: internals.kLanguage });
102
103 this.type = internals.kContentType;
104 this.body = response.toString();
105 };
106 }
107
108 /**
109 * Gets a recording.
110 *
111 * @function getRecording
112 * @memberof RecordingsController
113 * @instance
114 * @return {generator} a koa compatible handler generator function
115 */
116 getRecording() {
117
118 const self = this;
119
120 return function * (id) {
121
122 id = parseInt(id);
123
124 const zcard = Pify(self._redis.zcard.bind(self._redis));
125 const zrange = Pify(self._redis.zrange.bind(self._redis));
126 const zscore = Pify(self._redis.zscore.bind(self._redis));
127 const zrangebyscore = Pify(self._redis.zrangebyscore.bind(self._redis));
128
129 const response = new Twilio.TwimlResponse();
130 let location = null;
131
132 if (!id) {
133 const maxNumber = yield zcard(internals.kRecordingsSet);
134 const index = Math.floor(Math.random() * maxNumber); // get random between 0 and cardinality
135 location = yield zrange(internals.kRecordingsSet, index, index);
136 }
137 else {
138 location = yield zrangebyscore(internals.kRecordingsSet, id, id);
139 }
140
141 if (location && location.length > 0) {
142 if (!id) {
143 id = yield zscore(internals.kRecordingsSet, location[0]);
144 }
145
146 const separatedId = id.toString().split('').join('. ');
147 response.play(location[0]).say(separatedId, { language: internals.kLanguage });
148 }
149 else {
150 response.say(internals.kNotFoundMessage, { language: internals.kLanguage });
151 }
152
153 this.type = internals.kContentType;
154 this.body = response.toString();
155 };
156 }
157
158 // Validates the post schema
159
160 _validate(post) {
161
162 const validate = Pify(Joi.validate.bind(Joi));
163 return validate(post, internals.kRecordingSchema);
164 }
165};