Failed Conditions
Push — master ( f5e7b3...711045 )
by Yo
01:30
created

lib/Alfred.js (1 issue)

Labels
Severity
1
"use strict";
2
3
const sarahClient = require('./client/sarahClient');
4
const pMapNormalizeAnyway = require('./plugins/promise/mapNormalizeAnyway');
5
const pAllAnyway = require('./plugins/promise/allAnyway');
6
const logger = require('./logger');
7
const DecoratorPlugin = require('./model/DecoratorPlugin');
8
const NestedError = require('nested-error-stacks');
9
const ActorPlugin = require('./model/ActorPlugin');
10
11
12
/**
13
 * Represent the bot which can speak and listen to you
14
 */
15
class Alfred {
16
    /**
17
     * @public
18
     *
19
     * @param {string} textToSpeech
20
     *
21
     * @returns {Promise<null|Error>}
22
     */
23
    speak(textToSpeech) {
24
25
        return this.decorateTts(textToSpeech)
26
            .then(textToSpeech => {
27
                if (!textToSpeech) {
28
                    return Promise.resolve();
29
                }
30
31
                this.logger.debug(`Alfred will say "${textToSpeech}"`);
32
33
                return sarahClient({
34
                    tts: textToSpeech,
35
                    sync: true
36
                })
37
                    .then(() => null)
38
                    .catch(error => {
39
                        const newError = new NestedError('Alfred::speak error', error);
40
                        this.logError(newError);
41
42
                        return Promise.reject(newError);
43
                    });
44
            })
45
        ;
46
    }
47
48
    /**
49
     * @public
50
     *
51
     * @returns {Promise<null|Error>}
52
     */
53
    listen() {
54
        return sarahClient({'listen': true})
55
            .then(() => null)
56
            .catch(error => {
57
                const newError = new NestedError('Alfred::listen error', error);
58
                this.logError(newError);
59
60
                const throwError = () => {
61
                    return Promise.reject(newError);
62
                };
63
64
                const tts = 'Impossible de vous écouter';
65
                return this.speak(tts)
66
                    .catch(error => {
67
                        logger.warning(`skip previous speak("${tts}") error !`);
68
69
                        return throwError();
70
                    })
71
                    .then(throwError);
72
            })
73
        ;
74
    }
75
76
    /**
77
     * @public
78
     *
79
     * @returns {Promise<boolean>} true if action have been taken in account else false
80
     */
81
    stopListening() {
82
        return sarahClient({'listen': false})
83
            .then(() => true)
84
            .catch(error => {
85
                const newError = new NestedError('Alfred::stopListening error', error);
86
                this.logError(newError);
87
88
                const throwError = () => {
89
                    return Promise.reject(newError);
90
                };
91
                const tts = 'je n\'arrive pas à ne rien faire ! J\'ai besoin de votre aide !';
92
93
                return this.speak(tts)
94
                    .catch(error => {
95
                        logger.warning(`skip previous speak("${tts}") error !`);
96
97
                        return throwError();
98
                    })
99
                    .then(throwError);
100
            })
101
            .then(processed => {
102
                if (processed) {
103
                    return this.speak('A votre service !').then(() => processed);
104
                }
105
106
                return processed;
107
            })
108
        ;
109
    }
110
111
    /**
112
     * @public
113
     * Politely wake up Alfred before asking him something.
114
     *
115
     * He will wake up each of his leprechauns
116
     *
117
     * @return {Promise}
118
     */
119
    init() {
120
        let haveInvalidPlugins = false;
121
        return pAllAnyway(
122
            this.pluginList.map(plugin => plugin.init(this))
123
        )
124
            .then(({resolvedList, rejectedList}) => {
125
                haveInvalidPlugins = rejectedList.length > 0;
126
127
                return this.cleanPluginList(resolvedList.length ? resolvedList.keys() : []);
128
            })
129
            .then(() => this.splitPluginListByRole())
130
            .then(() => {this.initialized = true;})
131
            .then(() => {
132
                if (haveInvalidPlugins) {
133
                    return this.speak('Certain lutins semble malades. Ils sont au repos pour le moment.');
134
                }
135
            })
136
            .then(() => this.listen())
137
        ;
138
    }
139
140
    /**
141
     * @public
142
     *
143
     * @param {Plugin[]} pluginList
144
     */
145
    constructor(pluginList = []) {
146
        this.logger = logger;
147
        /** @type {Plugin[]} */
148
        this.pluginList = pluginList;
149
        /** @type {DecoratorPlugin[]} */
150
        this.decoratorList = [];
151
        /** @type {ActorPlugin[]} */
152
        this.actorList = [];
153
        /** @type {Plugin[]} */
154
        this.invalidPluginList = [];
155
    }
156
157
    /**
158
     * @private
159
     *
160
     * @param {Promise<string>} textToSpeech
161
     */
162
    decorateTts(textToSpeech) {
163
        return pMapNormalizeAnyway(
164
            textToSpeech,
165
            this.decoratorList
166
                // Return a callback that accept the value to normalize
167
                .map(decorator => decorator.normalizeTts)
168
        )
169
    }
170
171
    /**
172
     * @private
173
     *
174
     * @return {Promise<undefined|Error>}
175
     */
176
    cleanPluginList(validPluginIdList) {
177
        // Override plugins list with only valid ones
178
        const backupPluginList = this.pluginList;
179
        this.pluginList = [];
180
181
        return new Promise((resolve, reject) => {
182
            try {
183
                this.pluginList = validPluginIdList.map(index => {
184
                    return backupPluginList[index];
185
                });
186
                resolve();
187
            } catch (e) {
188
                reject(new NestedError('Error during plugins validity split', error));
0 ignored issues
show
The variable error seems to be never declared. If this is a global, consider adding a /** global: error */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
189
            }
190
        });
191
    }
192
193
    /**
194
     * @private
195
     *
196
     * @return {Promise<undefined|Error>}
197
     */
198
    splitPluginListByRole() {
199
        return Promise.all([
200
            () => {
201
                this.decoratorList = this.pluginList
202
                    .map(plugin => plugin instanceof DecoratorPlugin);
203
            },
204
            () => {
205
                this.actorList = this.pluginList
206
                    .map(plugin => plugin instanceof ActorPlugin);
207
            }
208
        ])
209
            .catch(error => Promise.reject(new NestedError('Error during plugins roles split', error)));
210
    }
211
212
    /**
213
     * @private
214
     * @param {Error} error
215
     *
216
     * @returns {Error}
217
     */
218
    logError(error) {
219
        this.logger.error(`Alfred error => ${error.message}`);
220
221
        return error;
222
    }
223
}
224
225
module.exports = Alfred;
226