Failed Conditions
Push — master ( 711045...1671e4 )
by Yo
01:54
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
            .catch(error => {
56
                const newError = new NestedError('Alfred::listen error', error);
57
                this.logError(newError);
58
59
                const throwError = () => {
60
                    return Promise.reject(newError);
61
                };
62
63
                const tts = 'Impossible de vous écouter';
64
                return this.speak(tts)
65
                    .catch(error => {
66
                        logger.warning(`skip previous speak("${tts}") error !`);
67
68
                        return throwError();
69
                    })
70
                    .then(throwError);
71
            })
72
        ;
73
    }
74
75
    /**
76
     * @public
77
     *
78
     * @returns {Promise<null|Error>}
79
     */
80
    stopListening() {
81
        return sarahClient({'listen': false})
82
            .catch(error => {
83
                const newError = new NestedError('Alfred::stopListening error', error);
84
                this.logError(newError);
85
86
                const throwError = () => {
87
                    return Promise.reject(newError);
88
                };
89
                const tts = 'je n\'arrive pas à ne rien faire ! J\'ai besoin de votre aide !';
90
91
                return this.speak(tts)
92
                    .catch(error => {
93
                        logger.warning(`skip previous speak("${tts}") error !`);
94
95
                        return throwError();
96
                    })
97
                    .then(throwError);
98
            })
99
            .then(() => this.speak('A votre service !'))
100
        ;
101
    }
102
103
    /**
104
     * @public
105
     * Politely wake up Alfred before asking him something.
106
     *
107
     * He will wake up each of his leprechauns
108
     *
109
     * @return {Promise}
110
     */
111
    init() {
112
        let haveInvalidPlugins = false;
113
        return pAllAnyway(
114
            this.pluginList.map(plugin => plugin.init(this))
115
        )
116
            .then(({resolvedList, rejectedList}) => {
117
                haveInvalidPlugins = rejectedList.length > 0;
118
119
                return this.cleanPluginList(resolvedList.length ? resolvedList.keys() : []);
120
            })
121
            .then(() => this.splitPluginListByRole())
122
            .then(() => {this.initialized = true;})
123
            .then(() => {
124
                if (haveInvalidPlugins) {
125
                    return this.speak('Certain lutins semble malades. Ils sont au repos pour le moment.');
126
                }
127
            })
128
            .then(() => this.listen())
129
            .then(() => this.speak('A votre écoute'))
130
        ;
131
    }
132
133
    /**
134
     * @public
135
     *
136
     * @param {Plugin[]} pluginList
137
     */
138
    constructor(pluginList = []) {
139
        this.logger = logger;
140
        /** @type {Plugin[]} */
141
        this.pluginList = pluginList;
142
        /** @type {DecoratorPlugin[]} */
143
        this.decoratorList = [];
144
        /** @type {ActorPlugin[]} */
145
        this.actorList = [];
146
        /** @type {Plugin[]} */
147
        this.invalidPluginList = [];
148
    }
149
150
    /**
151
     * @private
152
     *
153
     * @param {Promise<string>} textToSpeech
154
     */
155
    decorateTts(textToSpeech) {
156
        return pMapNormalizeAnyway(
157
            textToSpeech,
158
            this.decoratorList
159
                // Return a callback that accept the value to normalize
160
                .map(decorator => decorator.normalizeTts)
161
        )
162
    }
163
164
    /**
165
     * @private
166
     *
167
     * @return {Promise<undefined|Error>}
168
     */
169
    cleanPluginList(validPluginIdList) {
170
        // Override plugins list with only valid ones
171
        const backupPluginList = this.pluginList;
172
        this.pluginList = [];
173
174
        return new Promise((resolve, reject) => {
175
            try {
176
                this.pluginList = validPluginIdList.map(index => {
177
                    return backupPluginList[index];
178
                });
179
                resolve();
180
            } catch (e) {
181
                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...
182
            }
183
        });
184
    }
185
186
    /**
187
     * @private
188
     *
189
     * @return {Promise<undefined|Error>}
190
     */
191
    splitPluginListByRole() {
192
        return Promise.all([
193
            () => {
194
                this.decoratorList = this.pluginList
195
                    .map(plugin => plugin instanceof DecoratorPlugin);
196
            },
197
            () => {
198
                this.actorList = this.pluginList
199
                    .map(plugin => plugin instanceof ActorPlugin);
200
            }
201
        ])
202
            .catch(error => Promise.reject(new NestedError('Error during plugins roles split', error)));
203
    }
204
205
    /**
206
     * @private
207
     * @param {Error} error
208
     *
209
     * @returns {Error}
210
     */
211
    logError(error) {
212
        this.logger.error(`Alfred error => ${error.message}`);
213
214
        return error;
215
    }
216
}
217
218
module.exports = Alfred;
219