Passed
Pull Request — master (#5)
by Apocist
01:21
created

nodeVBulletinAPI.js (8 issues)

1
"use strict";
2
const md5 = require('js-md5'),
3
    os = require('os'),
4
    request = require('request'),
5
    _ = require('underscore'),
6
    Forum = require('./Forum'),
7
    Member = require('./Member'),
8
    //Post = require('./Post'),
9
    Thread = require('./Thread'),
10
    {version} = require('./package.json');
11
12
/**
13
 *
14
 */
15
class VBApi {
16
    /**
17
     * Initialize a vb api connection .This needs to be called for the first time
18
     * @param {string} apiUrl
19
     * @param {string} apiKey
20
     * @param {string} platformName
21
     * @param {string} platformVersion
22
     * @param {object=} options - A fallback to the old style config
23
     * @param {string=} options.apiUrl
24
     * @param {string=} options.apiKey
25
     * @param {string=} options.platformName
26
     * @param {string=} options.platformVersion
27
     */
28
    constructor(apiUrl, apiKey, platformName, platformVersion, options) {
29
        this.defaultVars = {
30
            baseUrl: '', //Needed for cookie related commands
31
            apiUrl: '',
32
            apiKey: '',
33
            clientName: 'nodeVBulletinAPI',
34
            clientVersion: version,
35
            uniqueId: ''
36
        };
37
38
        this.clientSessionVars = {
39
            apiVersion: '',
40
            apiAccessToken: '',
41
            sessionHash: '', // Unused?
42
            apiClientId: '',
43
            secret: '',
44
            inited: false,
45
            error: null
46
        };
47
48
        /**
49
         * @typedef UserVars
50
         * @property {string} dbsessionhash
51
         * @property {number} userid
52
         * @property {string} username
53
         * @property {boolean} loggedIn
54
         * @type {UserVars}
55
         */
56
        this.userSessionVars = {
57
            dbsessionhash: '',
58
            username: '',
59
            userid: 0,
60
            loggedIn: false
61
        };
62
63
        /** @private */
64
        this.__waitingForInitializationCallback = function () {
65
        }; // A blank callback to be filled in
66
67
        options = options || {};
68
        if (!_.isEmpty(apiUrl) || !_.isEmpty(options.apiUrl)) {
69
            options.apiUrl = apiUrl || options.apiUrl || {};
70
        }
71
        if (!_.isEmpty(apiKey) || !_.isEmpty(options.apiKey)) {
72
            options.apiKey = apiKey || options.apiKey || {};
73
        }
74
        if (!_.isEmpty(platformName) || !_.isEmpty(options.platformName)) {
75
            options.platformName = platformName || options.platformName || {};
76
        }
77
        if (!_.isEmpty(platformVersion) || !_.isEmpty(options.platformVersion)) {
78
            options.platformVersion = platformVersion || options.platformVersion || {};
79
        }
80
81
        this.__initialize(options)
82
83
    }
84
85
    /**
86
     * Initialize a vb api connection. This needs to be called for the first time
87
     * @param {object} options
88
     * @param {string} options.apiUrl
89
     * @param {string} options.apiKey
90
     * @param {string} options.platformName
91
     * @param {string} options.platformVersion
92
     * @private
93
     */
94
    __initialize(options) {
95
        let that = this;
96
        //return new Promise(async function (resolve, reject) {
97
        // Run itself as a self invoked promise that is awaited by nothing. callMethod shall wait until this is finished
98
        (async function __initialize_self() {
99
            if (
100
                !options.hasOwnProperty('apiUrl')
101
                || !options.hasOwnProperty('apiKey')
102
                || !options.hasOwnProperty('platformName')
103
                || !options.hasOwnProperty('platformVersion')
104
                || options.platformName === ''
105
                || options.platformVersion === ''
106
            ) {
107
                that.clientSessionVars.error = 'apiInit(): Initialization requires a `apiUrl`, `apiKey`, `platformName`, and `platformVersion`';
108
                that.__waitingForInitializationCallback(false);
109
                // reject(that.clientSessionVars.error);
110
                return that.clientSessionVars.error;
111
            } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
112
113
                let regex_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
114
                let url_parts = regex_url.exec(options.apiUrl);
115
                that.defaultVars.baseUrl = that.defaultVars.baseUrl || url_parts[1] + ':' + url_parts[2] + url_parts[3] + '/';
116
                that.defaultVars.apiUrl = that.defaultVars.apiUrl || options.apiUrl;
117
                that.defaultVars.apiKey = that.defaultVars.apiKey || options.apiKey;
118
                that.defaultVars.uniqueId = that.defaultVars.uniqueId || md5(that.defaultVars.clientName + that.defaultVars.clientVersion + options.platformName + options.platformVersion + that.constructor.getMacAddress() + new Date().getTime());
119
120
                try {
121
                    /**
122
                     *
123
                     * @type {{}}
124
                     * @property {string} apiversion
125
                     * @property {string} apiaccesstoken
126
                     * @property {string} sessionhash
127
                     * @property {string} apiclientid
128
                     * @property {string} secret
129
                     */
130
                    let response = await that.callMethod({
131
                        method: 'api_init',
132
                        params: {
133
                            clientname: that.defaultVars.clientName,
134
                            clientversion: that.defaultVars.clientVersion,
135
                            platformname: options.platformName,
136
                            platformversion: options.platformVersion,
137
                            uniqueid: that.defaultVars.uniqueId
138
                        }
139
                    });
140
141
                    that.clientSessionVars.apiVersion = '';
142
                    that.clientSessionVars.apiAccessToken = '';
143
                    that.clientSessionVars.sessionHash = '';
144
                    that.clientSessionVars.apiClientId = '';
145
                    that.clientSessionVars.secret = '';
146
                    that.clientSessionVars.inited = false;
147
                    if (
148
                        response.apiversion
149
                        && response.apiaccesstoken
150
                        && response.sessionhash
151
                        && response.apiclientid
152
                        && response.secret
153
                    ) {
154
                        that.clientSessionVars.apiVersion = response.apiversion;
155
                        that.clientSessionVars.apiAccessToken = response.apiaccesstoken;
156
                        that.clientSessionVars.sessionHash = response.sessionhash;
157
                        that.clientSessionVars.apiClientId = response.apiclientid;
158
                        that.clientSessionVars.secret = response.secret;
159
                        that.clientSessionVars.inited = true;
160
161
                        that.__waitingForInitializationCallback(true);
162
                        // resolve(that);
163
                        return that;
164
                    } else {
165
                        that.clientSessionVars.error = that.constructor.parseErrorMessage(response) || 'TODO ERROR (api connection did not return a session)';
166
                        that.__waitingForInitializationCallback(false);
167
                        // reject(that.clientSessionVars.error);
168
                        return that.clientSessionVars.error;
169
                    }
170
                } catch (e) {
171
                    that.clientSessionVars.error = e;
172
                    that.__waitingForInitializationCallback(false);
173
                    // reject(e);
174
                    return e;
175
                }
176
            }
177
        }());
178
        //});
179
    }
180
181
    /**
182
     * Will return after #initialize() is complete. Otherwise may reject() after 15 second timeout
183
     * @param {number=15} waitTime
184
     * @returns {Promise<void>}
185
     * @fulfill {void}
186
     * @reject {string} - Error Reason
187
     */
188
    async waitForInitialization(waitTime) {
189
        let that = this;
190
        waitTime = waitTime || 15;
191
        return new Promise(async function (resolve, reject) {
192
            if (that.clientSessionVars.inited === true) {
193
                resolve();
194
            } else if (that.clientSessionVars.error !== null) {
195
                reject(that.clientSessionVars.error);
196
            }
197
198
            /**
199
             * @type {number}
200
             * @private
201
             */
202
            that.__waitingForInitializationTimeout = setTimeout(
203
                function () {
204
                    that.__waitingForInitializationCallback = function () {
205
                    }; // Set back to a blank function
206
                    if (that.clientSessionVars.inited === true) {
207
                        resolve();
208
                    } else {
209
                        reject('Connection could not be achieved due to timed out', that.clientSessionVars.error);
210
                    }
211
212
                },
213
                waitTime * 1000 // 1 minute timeout
214
            );
215
            /**
216
             * @param {boolean=true} success
217
             * @private
218
             */
219
            that.__waitingForInitializationCallback = function (success) {
220
                if (that.__waitingForInitializationTimeout) {
221
                    clearTimeout(that.__waitingForInitializationTimeout);
222
                }
223
                if (success === false) {
224
                    reject(that.clientSessionVars.error);
225
                } else {
226
                    resolve();
227
                }
228
            };
229
        })
230
    }
231
232
    /**
233
     *
234
     * @param {object} options
235
     * @param {string} options.method - Required action to take
236
     * @param {object<string,string>} [options.params={}] - Optional parameter variables
237
     * @param {?object<string,string>} [options.cookies] - Optional cookie variables
238
     * @returns {Promise<{}>}
239
     * @fulfill {{}}
240
     * @reject {string} - Error Reason
241
     */
242
    async callMethod(options) {
243
        let that = this;
244
        let sign = true;
245
        options = options || {};
246
        options.params = options.params || {};
247
        return new Promise(async function (resolve, reject) {
248
            if (!options.method) {
249
                reject('callMethod(): requires a supplied method');
250
                return;
251
            }
252
253
            // Sign all calls except for api_init
254
            if (options.method === 'api_init') sign = false;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
255
256
            // await a valid session before continuing (skipping waiting on __initialize())
257
            if (sign === true) {
258
                try {
259
                    await that.waitForInitialization();
260
                } catch (e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
261
                }
262
            }
263
264
            // Gather our sessions variables together
265
            let reqParams = {
266
                api_m: options.method,
267
                api_c: that.clientSessionVars.apiClientId, //clientId
268
                api_s: that.clientSessionVars.apiAccessToken, //apiAccessToken (may be empty)
269
                api_v: that.clientSessionVars.apiVersion //api version
270
            };
271
            _.extend(reqParams, options.params); // Combine the arrays
272
273
            if (sign === true) {
274
                // Generate a signature to validate that we are authenticated
275
                if (that.clientSessionVars.inited) {
276
                    reqParams.api_sig = md5(that.clientSessionVars.apiAccessToken + that.clientSessionVars.apiClientId + that.clientSessionVars.secret + that.defaultVars.apiKey);
277
                } else {
278
                    reject('callMethod(): requires initialization. Not initialized');
279
                    return;
280
                }
281
            }
282
283
            // Create a valid http Request
284
            let reqOptions = {
285
                url: that.defaultVars.apiUrl,
286
                formData: reqParams,
287
                headers: {
288
                    'User-Agent': that.defaultVars.clientName
289
                }
290
            };
291
292
            // Some command require adding a cookie, we'll do that here
293
            if (options.cookies) {
294
                let j = request.jar();
295
                for (let variable in options.cookies) {
296
                    if (options.cookies.hasOwnProperty(variable)) {
297
                        let cookieString = variable + '=' + options.cookies[variable];
298
                        let cookie = request.cookie(cookieString);
299
                        j.setCookie(cookie, that.defaultVars.baseUrl);
300
                    }
301
                }
302
                reqOptions.jar = j;// Adds cookies to the request
303
            }
304
305
            try {
306
                request.post(
307
                    reqOptions,
308
                    function (error, response, body) {
309
                        if (!error && response.statusCode === 200) {
310
                            resolve(JSON.parse(body));
311
                        } else {
312
                            //console.log('No response');
313
                            reject('callMethod(): no response.');
314
                        }
315
                    }
316
                );
317
            } catch (e) {
318
                reject(e);
319
            }
320
        });
321
    }
322
323
    /**
324
     * Attempts to log in a user.
325
     * @param {string} username - Username
326
     * @param {string} password - clear text password TODO need to secure this more?
327
     * @param {object=} options
328
     * @param {string=} options.username - Ignore, already required at username
329
     * @param {string=} options.password - Ignore, already required at password
330
     * @returns {Promise<UserVars>}
331
     * @fulfill {UserVars}
332
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
333
     */
334
    async login(username, password, options) {
335
        options = options || {};
336
        options.username = username || options.username || '';
337
        options.password = md5(password || options.password || '');
338
        return await this.loginMD5('', '', options);
339
    }
340
341
    /**
342
     *
343
     * Attempts to log in a user. Requires the password to be pre md5 hashed.
344
     * @param {string} username - Username
345
     * @param {string} password - MD5 hashed password TODO need to secure this more?
346
     * @param {object=} options
347
     * @param {string=} options.username - Ignore, already required at username
348
     * @param {string=} options.password - Ignore, already required at password
349
     * @returns {Promise<UserVars>}
350
     * @fulfill {UserVars}
351
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
352
     */
353
    async loginMD5(username, password, options) {
354
        let that = this;
355
        options = options || {};
356
        options.username = username || options.username || {};
357
        options.password = password || options.password || {};
358
        return new Promise(async function (resolve, reject) {
359
            try {
360
                let response = await that.callMethod(
361
                    {
362
                        method: 'login_login',
363
                        params: {
364
                            vb_login_username: options.username || '',
365
                            vb_login_md5password: options.password || ''
366
                        }
367
                    }
368
                );
369
                /**
370
                 redirect_login - (NOT A ERROR) Login successful
371
                 badlogin - Username or Password incorrect. Login failed.
372
                 badlogin_strikes - Username or Password incorrect. Login failed. You have used {X} out of 5 login attempts. After all 5 have been used, you will be unable to login for 15 minutes.
373
                 */
374
                let error = that.constructor.parseErrorMessage(response);
375
                if (response.session) {
376
                    that.userSessionVars = response.session;
377
                    if (error === 'redirect_login') {
378
                        that.userSessionVars.username = options.username;
379
                        that.userSessionVars.loggedIn = true;
380
                    }
381
                }
382
                if (error === 'redirect_login') {
383
                    error = null;
384
                }
385
                if (error === null) {
386
                    resolve(that.userSessionVars);
387
                } else {
388
                    reject(error);
389
                }
390
391
            } catch (e) {
392
                reject(e);
393
            }
394
        });
395
    }
396
397
    /**
398
     * Attempts to log the user out.
399
     * @returns {Promise<boolean>} - Returns true on success, otherwise error code is rejected
400
     * @fulfill {boolean}
401
     * @reject {string} - Error Reason
402
     */
403
    async logout() {
404
        let that = this;
405
        return new Promise(async function (resolve, reject) {
406
            let error;
407
            try {
408
                let response = await that.callMethod({
409
                    method: 'login_logout'
410
                });
411
                error = that.constructor.parseErrorMessage(response);
412
                if (response.session) {
413
                    that.userSessionVars = response.session;
414
                    if (error === 'cookieclear') {
415
                        that.userSessionVars.username = '';
416
                        that.userSessionVars.loggedIn = false;
417
                    }
418
                }
419
                if (error === 'cookieclear') {
420
                    error = null;
421
                }
422
            } catch (e) {
423
                reject(e);
424
            }
425
            return error || true;
426
            if (error) {
0 ignored issues
show
This code is unreachable and can thus be removed without consequences.
Loading history...
427
                reject(error);
428
            } else {
429
                resolve(true)
430
            }
431
        });
432
    }
433
434
    /**
435
     * Return a Mac address of a network interface for machine identification
436
     * @returns {string} macAddress
437
     */
438
    static getMacAddress() {
439
        let interfaces = os.networkInterfaces();
440
        let address;
441
        loop1:
442
            for (let k in interfaces) {
443
                if (interfaces.hasOwnProperty(k)) {
444
                    for (let k2 in interfaces[k]) {
445
                        if (interfaces[k].hasOwnProperty(k2)) {
446
                            let addressI = interfaces[k][k2];
447
                            if (
448
                                (addressI.family === 'IPv4' || addressI.family === 'IPv6')
449
                                && addressI.hasOwnProperty('internal')
450
                                && addressI.internal === false
451
                                && addressI.hasOwnProperty('mac')
452
                                && addressI.mac !== '00:00:00:00:00:00'
453
                            ) {
454
                                address = addressI.mac;
455
                                break loop1;
456
                            }
457
                        }
458
                    }
459
                }
460
            }
461
        return address;
0 ignored issues
show
The variable address seems to not be initialized for all possible execution paths.
Loading history...
462
    }
463
464
    /**
465
     *
466
     * @param {object} response - Response object from callMethod()
467
     * @returns {string} status - Error message
468
     */
469
    static parseErrorMessage(response) {
470
        let retur = '';
471
        if (
472
            response.hasOwnProperty('response')
473
            && response.response.hasOwnProperty('errormessage')
474
        ) {
475
            if (_.isArray(response.response.errormessage)) {
476
                retur = response.response.errormessage[0]
477
            } else {
478
                retur = response.response.errormessage;
479
            }
480
        }
481
        return retur;
482
    }
483
484
    /**
485
     * List every Forum and sub forum available to the user.
486
     * @returns {Promise<Forum[]>} - Array of Forum objects
487
     * @fulfill {Forum[]}
488
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
489
     */
490
    async getForums() {
491
        let that = this;
492
        return new Promise(async function (resolve, reject) {
493
            let forums = [];
494
            try {
495
                let response = await that.callMethod(
496
                    {
497
                        method: 'api_forumlist'
498
                    });
499
500
                if (response) {
501
                    for (let forum in response) {
502
                        if (response.hasOwnProperty(forum)) {
503
                            forums.push(new Forum(response[forum]));
504
                        }
505
                    }
506
                }
507
            } catch (e) {
508
                reject(e);
509
            }
510
            resolve(forums);
511
        });
512
    }
513
514
    /**
515
     * List detailed info about a forum and it's sub-forums and threads
516
     * @param {number} forumId - Forum id
517
     * @param {object=} options - Secondary Options
518
     * @param {number=} options.forumid - Ignore, already required at forumId
519
     * TODO note additional options
520
     * @returns {Promise<Forum>} - Returns a Forum object
521
     * @fulfill {Forum}
522
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
523
     */
524
    async getForum(forumId, options) {
525
        let that = this;
526
        options = options || {};
527
        options.forumid = forumId || options.forumid || ''; //required
528
529
        return new Promise(async function (resolve, reject) {
530
            let forum;
531
            try {
532
                let response = await that.callMethod({
533
                    method: 'forumdisplay',
534
                    params: options
535
                });
536
                if (
537
                    response
538
                    && response.hasOwnProperty('response')
539
                ) {
540
                    forum = new Forum(response.response);
541
                }
542
            } catch (e) {
543
                reject(e);
544
            }
545
            resolve(forum);
0 ignored issues
show
The variable forum seems to not be initialized for all possible execution paths. Are you sure resolve handles undefined variables?
Loading history...
546
        });
547
    }
548
549
    /**
550
     * List detailed information about a Thread and it's Posts
551
     * @param {number} threadId - Thread id
552
     * @param {object=} options - Secondary Options
553
     * @param {number=} options.threadid - Ignore, already required at threadId
554
     * TODO note additional options
555
     * @returns {Promise<Thread>} - Returns a Thread object
556
     * @fulfill {Thread}
557
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
558
     */
559
    async getThread(threadId, options) {
560
        let that = this;
561
        options = options || {};
562
        options.threadid = threadId || options.threadid || ''; //required
563
564
        return new Promise(async function (resolve, reject) {
565
            let thread;
566
            try {
567
                let response = await that.callMethod({
568
                    method: 'showthread',
569
                    params: options
570
                });
571
                if (
572
                    response
573
                    && response.hasOwnProperty('response')
574
                ) {
575
                    thread = new Thread(response.response);
576
                }
577
            } catch (e) {
578
                reject(e);
579
            }
580
            resolve(thread);
0 ignored issues
show
The variable thread seems to not be initialized for all possible execution paths. Are you sure resolve handles undefined variables?
Loading history...
581
        });
582
    }
583
584
    /**
585
     * Attempts to submit a new Post into a specified Thread
586
     * @param {number} threadId - Thread id
587
     * @param {string} message - Post Message
588
     * @param {object=} options
589
     * @param {boolean=} options.signature  - Optionally append your signature
590
     * @param {number=} options.threadid - Ignore, already required at threadId
591
     * @param {string=} options.message - Ignore, already required at message
592
     * TODO note additional options
593
     * @returns {Promise<*>} - Returns a unhandled response currently
594
     * @fulfill {*}
595
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
596
     */
597
    async newPost(threadId, message, options) {
598
        let that = this;
599
        options = options || {};
600
        options.threadid = threadId || options.threadid || ''; //required
601
        options.message = message || options.message || ''; //required
602
        if (options.signature === true) {
603
            //System only handle 1 or 0. defaults to 0
604
            options.signature = '1';
605
        }
606
607
        return new Promise(async function (resolve, reject) {
608
            try {
609
                let response = await that.callMethod({
610
                    method: 'newreply_postreply',
611
                    params: options
612
                });
613
                let possibleError = that.constructor.parseErrorMessage(response);
614
                //success is errormessgae 'redirect_postthanks'
615
                //error 'threadclosed' if thread is closed. FIXME does not error
616
                //reports threadid and postid
617
                if (
618
                    possibleError === 'redirect_postthanks'
619
                    && response.hasOwnProperty('show')
620
                ) {
621
                    resolve(response.show);
622
                } else {
623
                    reject(possibleError || response);
624
                }
625
            } catch (e) {
626
                reject(e);
627
            }
628
        });
629
    }
630
631
    /**
632
     * Attempts to edit an existing Post
633
     * @param {number} postId - Post id
634
     * @param {string} message - Post Message
635
     * @param {object=} options
636
     * @param {string=} options.reason - Reason for editing
637
     * @param {boolean=} options.signature - Optionally append your signature
638
     * @param {number=} options.postid - Ignore, already required at postId
639
     * @param {string=} options.message - Ignore, already required at message
640
     * TODO note additional options
641
     * @returns {Promise<*>} - Returns a unhandled response currently
642
     * @fulfill {*}
643
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
644
     */
645
    async editPost(postId, message, options) {
646
        let that = this;
647
        options = options || {};
648
        options.postid = postId || options.postid || ''; //required
649
        options.message = message || options.message || ''; //required
650
        if (options.signature === true) {
651
            //System only handle 1 or 0. defaults to 0
652
            options.signature = '1';
653
        }
654
655
        return new Promise(async function (resolve, reject) {
656
            try {
657
                let response = await that.callMethod({
658
                    method: 'editpost_updatepost',
659
                    params: options
660
                });
661
                let possibleError = that.constructor.parseErrorMessage(response);
662
                //success is errormessgae 'redirect_editthanks'
663
                if (possibleError === 'redirect_editthanks') {
664
                    resolve({postid: options.postid});
665
                } else {
666
                    reject(possibleError || response);
667
                }
668
            } catch (e) {
669
                reject(e);
670
            }
671
        });
672
    }
673
674
    /**
675
     * Attempts to retrieve data about a specific user found by username
676
     * @param {string} username - Username
677
     * @param {object=} options - Secondary Options
678
     * @param {string=} options.username - Ignore, already required at username
679
     * @returns {Promise<Member>} - Returns a Member object
680
     * @fulfill {Member}
681
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
682
     */
683
    async getMember(username, options) {
684
        let that = this;
685
        options = options || {};
686
        options.username = username || options.username || ''; //required
687
688
        return new Promise(async function (resolve, reject) {
689
            let thread;
690
            try {
691
                let response = await that.callMethod({
692
                    method: 'member',
693
                    params: options
694
                });
695
                if (
696
                    response
697
                    && response.hasOwnProperty('response')
698
                ) {
699
                    thread = new Member(response.response);
700
                }
701
            } catch (e) {
702
                reject(e);
703
            }
704
            resolve(thread);
0 ignored issues
show
The variable thread seems to not be initialized for all possible execution paths. Are you sure resolve handles undefined variables?
Loading history...
705
        });
706
    }
707
708
    /**
709
     * TODO untested - does not seem to function yet
710
     * Attempts to delete an existing Post
711
     * @param {number} postId - Post id
712
     * @param {number} threadId - Thread id
713
     * @param {object=} options
714
     * @param {string=} options.reason - Reason for deleting
715
     * @param {number=} options.postid - Ignore, already required at postId
716
     * @param {number=} options.threadid - Ignore, already required at threadId
717
     * TODO note additional options
718
     * @returns {Promise<*>} - Returns a unhandled response currently
719
     * @fulfill {*}
720
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
721
     */
722
    async deletePost(postId, threadId, options) {
723
        let that = this;
724
        options = options || {};
725
        options.postid = postId || options.postid || ''; //required
726
        options.threadid = threadId || options.threadid || ''; // TODO required????
727
728
        return new Promise(async function (resolve, reject) {
729
            try {
730
                let response = await that.callMethod({
731
                    method: 'editpost_deletepost',
732
                    params: options
733
                });
734
                let possibleError = that.constructor.parseErrorMessage(response);
735
                //unknown response
736
                if (
737
                    possibleError === 'redirect_deletepost'
738
                    && response.hasOwnProperty('show')
739
                ) {
740
                    //console.log('response', response);
741
                    resolve(response.show);
742
                } else {
743
                    reject(possibleError || response);
744
                }
745
            } catch (e) {
746
                reject(e);
747
            }
748
        });
749
    }
750
751
    /**
752
     * Attempts to submit a new Thread into a specified Forum. This will also be considered the first Post
753
     * @param {number} forumId - Forum Id
754
     * @param {string} subject - Post/Thread Subject
755
     * @param {string} message - Post Message
756
     * @param {object=} options
757
     * @param {boolean=} options.signature - Optionally append your signature
758
     * @param {number=} options.forumid - Ignore, already required at postId
759
     * @param {string=} options.subject - Ignore, already required at postId
760
     * @param {string=} options.message - Ignore, already required at postId
761
     * TODO note additional options
762
     * @returns {Promise<*>} - Returns a unhandled response currently
763
     * @fulfill {*}
764
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
765
     */
766
    async newThread(forumId, subject, message, options) {
767
        let that = this;
768
        options = options || {};
769
        options.forumid = forumId || options.forumid || ''; //required
770
        options.subject = subject || options.subject || ''; //required
771
        options.message = message || options.message || ''; //required
772
773
        if (options.signature === true) {
774
            //System only handle 1 or 0. defaults to 0
775
            options.signature = '1';
776
        }
777
778
        return new Promise(async function (resolve, reject) {
779
            try {
780
                let response = await that.callMethod({
781
                    method: 'newthread_postthread',
782
                    params: options
783
                });
784
                let possibleError = that.constructor.parseErrorMessage(response);
785
                //success is errormessgae 'redirect_postthanks'
786
                //reports threadid and postid
787
                if (
788
                    possibleError === 'redirect_postthanks'
789
                    && response.hasOwnProperty('show')
790
                ) {
791
                    resolve(response.show);
792
                } else {
793
                    reject(possibleError || response);
794
                }
795
            } catch (e) {
796
                reject(e);
797
            }
798
        });
799
    }
800
801
    /**
802
     * TODO incomplete - does not seem to function yet
803
     * Attempts to close a specific Thread. Requires a user to have a 'inline mod' permissions
804
     * @param {number} threadId - Id of Thread to close
805
     * @returns {Promise<*>} - Returns a unhandled response currently
806
     * @fulfill {*}
807
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
808
     */
809
    async modCloseThread(threadId) {
810
        let that = this;
811
        let cookies = {};
812
        if (threadId) {
813
            //TODO multiple ids are delimited with a '-'. eg: 123-345-456
814
            cookies.vbulletin_inlinethread = threadId;
815
        }
816
        return new Promise(async function (resolve, reject) {
817
            try {
818
                let response = await that.callMethod({
819
                    method: 'inlinemod_close',
820
                    cookies: cookies || {}
821
                });
822
                //let possibleError = that.constructor.parseErrorMessage(response);
823
                //unknown responses
824
                /*if (
825
                    possibleError === 'redirect_postthanks'
826
                    && response.hasOwnProperty('show')
827
                ) {*/
828
                resolve(response);
829
                /*} else {
830
                    reject(possibleError || response);
831
                }*/
832
            } catch (e) {
833
                reject(e);
834
            }
835
        });
836
    }
837
838
    /**
839
     * TODO incomplete - does not seem to function yet
840
     * Attempts to open a specific Thread. Requires a user to have a 'inline mod' permissions
841
     * @param {number} threadId - Id of Thread to open
842
     * @returns {Promise<*>} - Returns a unhandled response currently
843
     * @fulfill {*}
844
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
845
     */
846
    async modOpenThread(threadId) {
847
        let that = this;
848
        let cookies = {};
849
        if (threadId) {
850
            //TODO multiple ids are delimited with a '-'. eg: 123-345-456
851
            cookies.vbulletin_inlinethread = threadId;
852
        }
853
        return new Promise(async function (resolve, reject) {
854
            try {
855
                let response = await that.callMethod({
856
                    method: 'inlinemod_open',
857
                    cookies: cookies || {}
858
                });
859
                //let possibleError = that.constructor.parseErrorMessage(response);
860
                //unknown responses
861
                /*if (
862
                    possibleError === 'redirect_postthanks'
863
                    && response.hasOwnProperty('show')
864
                ) {*/
865
                resolve(response);
866
                /*} else {
867
                    reject(possibleError || response);
868
                }*/
869
            } catch (e) {
870
                reject(e);
871
            }
872
        });
873
    }
874
875
    /**
876
     * TODO incomplete - does not seem to function yet
877
     * Attempts to delete a specific Thread. Requires a user to have a 'inline mod' permissions
878
     * @param {number} threadId - Id of Thread to close
879
     * @returns {Promise<*>} - Returns a unhandled response currently
880
     * @fulfill {*}
881
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
882
     */
883
    async modDeleteThread(threadId) {
884
        let that = this;
885
        let cookies = {};
886
        if (threadId) {
887
            //TODO multiple ids are delimited with a '-'. eg: 123-345-456
888
            cookies.vbulletin_inlinethread = threadId;
889
        }
890
        return new Promise(async function (resolve, reject) {
891
            try {
892
                let response = await that.callMethod({
893
                    method: 'inlinemod_dodeletethreads',
894
                    cookies: cookies || {}
895
                });
896
                //let possibleError = that.constructor.parseErrorMessage(response);
897
                //unknown responses
898
                /*if (
899
                    possibleError === 'redirect_postthanks'
900
                    && response.hasOwnProperty('show')
901
                ) {*/
902
                resolve(response);
903
                /*} else {
904
                    reject(possibleError || response);
905
                }*/
906
            } catch (e) {
907
                reject(e);
908
            }
909
        });
910
    }
911
}
912
913
module.exports = VBApi;
914