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

nodeVBulletinAPI.js (1 issue)

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
            let error = null;
100
            let result = null;
101
            if (
102
                !options.hasOwnProperty('apiUrl')
103
                || !options.hasOwnProperty('apiKey')
104
                || !options.hasOwnProperty('platformName')
105
                || !options.hasOwnProperty('platformVersion')
106
                || options.platformName === ''
107
                || options.platformVersion === ''
108
            ) {
109
                that.clientSessionVars.error = 'apiInit(): Initialization requires a `apiUrl`, `apiKey`, `platformName`, and `platformVersion`';
110
                that.__waitingForInitializationCallback(false);
111
                error = that.clientSessionVars.error;
112
            }
113
114
            if (error === null) {
115
                let regex_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
116
                let url_parts = regex_url.exec(options.apiUrl);
117
                that.defaultVars.baseUrl = that.defaultVars.baseUrl || url_parts[1] + ':' + url_parts[2] + url_parts[3] + '/';
118
                that.defaultVars.apiUrl = that.defaultVars.apiUrl || options.apiUrl;
119
                that.defaultVars.apiKey = that.defaultVars.apiKey || options.apiKey;
120
                that.defaultVars.uniqueId = that.defaultVars.uniqueId || md5(that.defaultVars.clientName + that.defaultVars.clientVersion + options.platformName + options.platformVersion + that.constructor.getMacAddress() + new Date().getTime());
121
122
                try {
123
                    /**
124
                     *
125
                     * @type {{}}
126
                     * @property {string} apiversion
127
                     * @property {string} apiaccesstoken
128
                     * @property {string} sessionhash
129
                     * @property {string} apiclientid
130
                     * @property {string} secret
131
                     */
132
                    let response = await that.callMethod({
133
                        method: 'api_init',
134
                        params: {
135
                            clientname: that.defaultVars.clientName,
136
                            clientversion: that.defaultVars.clientVersion,
137
                            platformname: options.platformName,
138
                            platformversion: options.platformVersion,
139
                            uniqueid: that.defaultVars.uniqueId
140
                        }
141
                    });
142
143
                    that.clientSessionVars.apiVersion = '';
144
                    that.clientSessionVars.apiAccessToken = '';
145
                    that.clientSessionVars.sessionHash = '';
146
                    that.clientSessionVars.apiClientId = '';
147
                    that.clientSessionVars.secret = '';
148
                    that.clientSessionVars.inited = false;
149
                    if (
150
                        response.apiversion
151
                        && response.apiaccesstoken
152
                        && response.sessionhash
153
                        && response.apiclientid
154
                        && response.secret
155
                    ) {
156
                        that.clientSessionVars.apiVersion = response.apiversion;
157
                        that.clientSessionVars.apiAccessToken = response.apiaccesstoken;
158
                        that.clientSessionVars.sessionHash = response.sessionhash;
159
                        that.clientSessionVars.apiClientId = response.apiclientid;
160
                        that.clientSessionVars.secret = response.secret;
161
                        that.clientSessionVars.inited = true;
162
                        that.__waitingForInitializationCallback(true);
163
                        result = that;
164
                    }
165
166
                    if (result === null) {
167
                        that.clientSessionVars.error = that.constructor.parseErrorMessage(response) || 'TODO ERROR (api connection did not return a session)';
168
                        that.__waitingForInitializationCallback(false);
169
                        error = that.clientSessionVars.error;
170
                    }
171
                } catch (e) {
172
                    that.clientSessionVars.error = e;
173
                    that.__waitingForInitializationCallback(false);
174
                    // reject(e);
175
                    error = e;
176
                }
177
            }
178
            return error || result;
179
        }());
180
    }
181
182
    /**
183
     * Will return after #initialize() is complete. Otherwise may reject() after 15 second timeout
184
     * @param {number=15} waitTime
185
     * @returns {Promise<void>}
186
     * @fulfill {void}
187
     * @reject {string} - Error Reason
188
     */
189
    async waitForInitialization(waitTime) {
190
        let that = this;
191
        waitTime = waitTime || 15;
192
        return new Promise(async function (resolve, reject) {
193
            if (that.clientSessionVars.inited === true) {
194
                resolve();
195
            } else if (that.clientSessionVars.error !== null) {
196
                reject(that.clientSessionVars.error);
197
            }
198
199
            /**
200
             * @type {number}
201
             * @private
202
             */
203
            that.__waitingForInitializationTimeout = setTimeout(
204
                function () {
205
                    that.__waitingForInitializationCallback = function () {
206
                    }; // Set back to a blank function
207
                    if (that.clientSessionVars.inited === true) {
208
                        resolve();
209
                    } else {
210
                        reject('Connection could not be achieved due to timed out', that.clientSessionVars.error);
211
                    }
212
213
                },
214
                waitTime * 1000 // 1 minute timeout
215
            );
216
            /**
217
             * @param {boolean=true} success
218
             * @private
219
             */
220
            that.__waitingForInitializationCallback = function (success) {
221
                if (that.__waitingForInitializationTimeout) {
222
                    clearTimeout(that.__waitingForInitializationTimeout);
223
                }
224
                if (success === false) {
225
                    reject(that.clientSessionVars.error);
226
                } else {
227
                    resolve();
228
                }
229
            };
230
        })
231
    }
232
233
    /**
234
     *
235
     * @param {object} options
236
     * @param {string} options.method - Required action to take
237
     * @param {object<string,string>} [options.params={}] - Optional parameter variables
238
     * @param {?object<string,string>} [options.cookies] - Optional cookie variables
239
     * @returns {Promise<{}>}
240
     * @fulfill {{}}
241
     * @reject {string} - Error Reason
242
     */
243
    async callMethod(options) {
244
        let that = this;
245
        let sign = true;
246
        options = options || {};
247
        options.params = options.params || {};
248
        return new Promise(async function (resolve, reject) {
249
            if (!options.method) {
250
                reject('callMethod(): requires a supplied method');
251
                return;
252
            }
253
254
            // Sign all calls except for api_init
255
            if (options.method === 'api_init') {
256
                sign = false;
257
            }
258
259
            // await a valid session before continuing (skipping waiting on __initialize())
260
            if (sign === true) {
261
                try {
262
                    await that.waitForInitialization();
263
                } 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...
264
                    // For some reason is waitForInitialization(), there's nothing to be done, ignore it
265
                }
266
            }
267
268
            // Gather our sessions variables together
269
            let reqParams = {
270
                api_m: options.method,
271
                api_c: that.clientSessionVars.apiClientId, //clientId
272
                api_s: that.clientSessionVars.apiAccessToken, //apiAccessToken (may be empty)
273
                api_v: that.clientSessionVars.apiVersion //api version
274
            };
275
            _.extend(reqParams, options.params); // Combine the arrays
276
277
            if (sign === true) {
278
                // Generate a signature to validate that we are authenticated
279
                if (that.clientSessionVars.inited) {
280
                    reqParams.api_sig = md5(that.clientSessionVars.apiAccessToken + that.clientSessionVars.apiClientId + that.clientSessionVars.secret + that.defaultVars.apiKey);
281
                } else {
282
                    reject('callMethod(): requires initialization. Not initialized');
283
                    return;
284
                }
285
            }
286
287
            // Create a valid http Request
288
            let reqOptions = {
289
                url: that.defaultVars.apiUrl,
290
                formData: reqParams,
291
                headers: {
292
                    'User-Agent': that.defaultVars.clientName
293
                }
294
            };
295
296
            // Some command require adding a cookie, we'll do that here
297
            if (options.cookies) {
298
                let j = request.jar();
299
                for (let variable in options.cookies) {
300
                    if (options.cookies.hasOwnProperty(variable)) {
301
                        let cookieString = variable + '=' + options.cookies[variable];
302
                        let cookie = request.cookie(cookieString);
303
                        j.setCookie(cookie, that.defaultVars.baseUrl);
304
                    }
305
                }
306
                reqOptions.jar = j;// Adds cookies to the request
307
            }
308
309
            try {
310
                request.post(
311
                    reqOptions,
312
                    function (error, response, body) {
313
                        if (!error && response.statusCode === 200) {
314
                            resolve(JSON.parse(body));
315
                        } else {
316
                            //console.log('No response');
317
                            reject('callMethod(): no response.');
318
                        }
319
                    }
320
                );
321
            } catch (e) {
322
                reject(e);
323
            }
324
        });
325
    }
326
327
    /**
328
     * Attempts to log in a user.
329
     * @param {string} username - Username
330
     * @param {string} password - clear text password TODO need to secure this more?
331
     * @param {object=} options
332
     * @param {string=} options.username - Ignore, already required at username
333
     * @param {string=} options.password - Ignore, already required at password
334
     * @returns {Promise<UserVars>}
335
     * @fulfill {UserVars}
336
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
337
     */
338
    async login(username, password, options) {
339
        options = options || {};
340
        options.username = username || options.username || '';
341
        options.password = md5(password || options.password || '');
342
        return await this.loginMD5('', '', options);
343
    }
344
345
    /**
346
     *
347
     * Attempts to log in a user. Requires the password to be pre md5 hashed.
348
     * @param {string} username - Username
349
     * @param {string} password - MD5 hashed password TODO need to secure this more?
350
     * @param {object=} options
351
     * @param {string=} options.username - Ignore, already required at username
352
     * @param {string=} options.password - Ignore, already required at password
353
     * @returns {Promise<UserVars>}
354
     * @fulfill {UserVars}
355
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
356
     */
357
    async loginMD5(username, password, options) {
358
        let that = this;
359
        options = options || {};
360
        options.username = username || options.username || {};
361
        options.password = password || options.password || {};
362
        return new Promise(async function (resolve, reject) {
363
            try {
364
                let response = await that.callMethod(
365
                    {
366
                        method: 'login_login',
367
                        params: {
368
                            vb_login_username: options.username || '',
369
                            vb_login_md5password: options.password || ''
370
                        }
371
                    }
372
                );
373
                /**
374
                 redirect_login - (NOT A ERROR) Login successful
375
                 badlogin - Username or Password incorrect. Login failed.
376
                 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.
377
                 */
378
                let error = that.constructor.parseErrorMessage(response);
379
                if (response.session) {
380
                    that.userSessionVars = response.session;
381
                    if (error === 'redirect_login') {
382
                        that.userSessionVars.username = options.username;
383
                        that.userSessionVars.loggedIn = true;
384
                    }
385
                }
386
                if (error === 'redirect_login') {
387
                    error = null;
388
                }
389
                if (error === null) {
390
                    resolve(that.userSessionVars);
391
                } else {
392
                    reject(error);
393
                }
394
395
            } catch (e) {
396
                reject(e);
397
            }
398
        });
399
    }
400
401
    /**
402
     * Attempts to log the user out.
403
     * @returns {Promise<boolean>} - Returns true on success, otherwise error code is rejected
404
     * @fulfill {boolean}
405
     * @reject {string} - Error Reason
406
     */
407
    async logout() {
408
        let that = this;
409
        return new Promise(async function (resolve, reject) {
410
            let error;
411
            try {
412
                let response = await that.callMethod({
413
                    method: 'login_logout'
414
                });
415
                error = that.constructor.parseErrorMessage(response);
416
                if (response.session) {
417
                    that.userSessionVars = response.session;
418
                    if (error === 'cookieclear') {
419
                        that.userSessionVars.username = '';
420
                        that.userSessionVars.loggedIn = false;
421
                    }
422
                }
423
                if (error === 'cookieclear') {
424
                    error = null;
425
                }
426
            } catch (e) {
427
                reject(e);
428
            }
429
430
            if (error) {
431
                reject(error);
432
            } else {
433
                resolve(true)
434
            }
435
        });
436
    }
437
438
    /**
439
     * Return a Mac address of a network interface for machine identification
440
     * @returns {string} macAddress
441
     */
442
    static getMacAddress() {
443
        let interfaces = os.networkInterfaces();
444
        let address = '';
445
        loop1:
446
            for (let k in interfaces) {
447
                if (interfaces.hasOwnProperty(k)) {
448
                    for (let k2 in interfaces[k]) {
449
                        if (interfaces[k].hasOwnProperty(k2)) {
450
                            let addressI = interfaces[k][k2];
451
                            if (
452
                                (addressI.family === 'IPv4' || addressI.family === 'IPv6')
453
                                && addressI.hasOwnProperty('internal')
454
                                && addressI.internal === false
455
                                && addressI.hasOwnProperty('mac')
456
                                && addressI.mac !== '00:00:00:00:00:00'
457
                            ) {
458
                                address = addressI.mac;
459
                                break loop1;
460
                            }
461
                        }
462
                    }
463
                }
464
            }
465
        return address;
466
    }
467
468
    /**
469
     *
470
     * @param {object} response - Response object from callMethod()
471
     * @returns {string} status - Error message
472
     */
473
    static parseErrorMessage(response) {
474
        let retur = '';
475
        if (
476
            response.hasOwnProperty('response')
477
            && response.response.hasOwnProperty('errormessage')
478
        ) {
479
            if (_.isArray(response.response.errormessage)) {
480
                retur = response.response.errormessage[0]
481
            } else {
482
                retur = response.response.errormessage;
483
            }
484
        }
485
        return retur;
486
    }
487
488
    /**
489
     * List every Forum and sub forum available to the user.
490
     * @returns {Promise<Forum[]>} - Array of Forum objects
491
     * @fulfill {Forum[]}
492
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
493
     */
494
    async getForums() {
495
        let that = this;
496
        return new Promise(async function (resolve, reject) {
497
            let forums = [];
498
            try {
499
                let response = await that.callMethod(
500
                    {
501
                        method: 'api_forumlist'
502
                    });
503
504
                if (response) {
505
                    for (let forum in response) {
506
                        if (response.hasOwnProperty(forum)) {
507
                            forums.push(new Forum(response[forum]));
508
                        }
509
                    }
510
                }
511
            } catch (e) {
512
                reject(e);
513
            }
514
            resolve(forums);
515
        });
516
    }
517
518
    /**
519
     * List detailed info about a forum and it's sub-forums and threads
520
     * @param {number} forumId - Forum id
521
     * @param {object=} options - Secondary Options
522
     * @param {number=} options.forumid - Ignore, already required at forumId
523
     * TODO note additional options
524
     * @returns {Promise<Forum>} - Returns a Forum object
525
     * @fulfill {Forum}
526
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
527
     */
528
    async getForum(forumId, options) {
529
        let that = this;
530
        options = options || {};
531
        options.forumid = forumId || options.forumid || ''; //required
532
533
        return new Promise(async function (resolve, reject) {
534
            let forum = null;
535
            try {
536
                let response = await that.callMethod({
537
                    method: 'forumdisplay',
538
                    params: options
539
                });
540
                if (
541
                    response
542
                    && response.hasOwnProperty('response')
543
                ) {
544
                    forum = new Forum(response.response);
545
                }
546
            } catch (e) {
547
                reject(e);
548
            }
549
            if (forum !== null) {
550
                resolve(forum);
551
            } else {
552
                reject();
553
            }
554
        });
555
    }
556
557
    /**
558
     * List detailed information about a Thread and it's Posts
559
     * @param {number} threadId - Thread id
560
     * @param {object=} options - Secondary Options
561
     * @param {number=} options.threadid - Ignore, already required at threadId
562
     * TODO note additional options
563
     * @returns {Promise<Thread>} - Returns a Thread object
564
     * @fulfill {Thread}
565
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
566
     */
567
    async getThread(threadId, options) {
568
        let that = this;
569
        options = options || {};
570
        options.threadid = threadId || options.threadid || ''; //required
571
572
        return new Promise(async function (resolve, reject) {
573
            let thread = null;
574
            try {
575
                let response = await that.callMethod({
576
                    method: 'showthread',
577
                    params: options
578
                });
579
                if (
580
                    response
581
                    && response.hasOwnProperty('response')
582
                ) {
583
                    thread = new Thread(response.response);
584
                }
585
            } catch (e) {
586
                reject(e);
587
            }
588
            if (thread !== null) {
589
                resolve(thread);
590
            } else {
591
                reject();
592
            }
593
        });
594
    }
595
596
    /**
597
     * Attempts to submit a new Post into a specified Thread
598
     * @param {number} threadId - Thread id
599
     * @param {string} message - Post Message
600
     * @param {object=} options
601
     * @param {boolean=} options.signature  - Optionally append your signature
602
     * @param {number=} options.threadid - Ignore, already required at threadId
603
     * @param {string=} options.message - Ignore, already required at message
604
     * TODO note additional options
605
     * @returns {Promise<*>} - Returns a unhandled response currently
606
     * @fulfill {*}
607
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
608
     */
609
    async newPost(threadId, message, options) {
610
        let that = this;
611
        options = options || {};
612
        options.threadid = threadId || options.threadid || ''; //required
613
        options.message = message || options.message || ''; //required
614
        if (options.signature === true) {
615
            //System only handle 1 or 0. defaults to 0
616
            options.signature = '1';
617
        }
618
619
        return new Promise(async function (resolve, reject) {
620
            try {
621
                let response = await that.callMethod({
622
                    method: 'newreply_postreply',
623
                    params: options
624
                });
625
                let possibleError = that.constructor.parseErrorMessage(response);
626
                //success is errormessgae 'redirect_postthanks'
627
                //error 'threadclosed' if thread is closed. FIXME does not error
628
                //reports threadid and postid
629
                if (
630
                    possibleError === 'redirect_postthanks'
631
                    && response.hasOwnProperty('show')
632
                ) {
633
                    resolve(response.show);
634
                } else {
635
                    reject(possibleError || response);
636
                }
637
            } catch (e) {
638
                reject(e);
639
            }
640
        });
641
    }
642
643
    /**
644
     * Attempts to edit an existing Post
645
     * @param {number} postId - Post id
646
     * @param {string} message - Post Message
647
     * @param {object=} options
648
     * @param {string=} options.reason - Reason for editing
649
     * @param {boolean=} options.signature - Optionally append your signature
650
     * @param {number=} options.postid - Ignore, already required at postId
651
     * @param {string=} options.message - Ignore, already required at message
652
     * TODO note additional options
653
     * @returns {Promise<*>} - Returns a unhandled response currently
654
     * @fulfill {*}
655
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
656
     */
657
    async editPost(postId, message, options) {
658
        let that = this;
659
        options = options || {};
660
        options.postid = postId || options.postid || ''; //required
661
        options.message = message || options.message || ''; //required
662
        if (options.signature === true) {
663
            //System only handle 1 or 0. defaults to 0
664
            options.signature = '1';
665
        }
666
667
        return new Promise(async function (resolve, reject) {
668
            try {
669
                let response = await that.callMethod({
670
                    method: 'editpost_updatepost',
671
                    params: options
672
                });
673
                let possibleError = that.constructor.parseErrorMessage(response);
674
                //success is errormessgae 'redirect_editthanks'
675
                if (possibleError === 'redirect_editthanks') {
676
                    resolve({postid: options.postid});
677
                } else {
678
                    reject(possibleError || response);
679
                }
680
            } catch (e) {
681
                reject(e);
682
            }
683
        });
684
    }
685
686
    /**
687
     * Attempts to retrieve data about a specific user found by username
688
     * @param {string} username - Username
689
     * @param {object=} options - Secondary Options
690
     * @param {string=} options.username - Ignore, already required at username
691
     * @returns {Promise<Member>} - Returns a Member object
692
     * @fulfill {Member}
693
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
694
     */
695
    async getMember(username, options) {
696
        let that = this;
697
        options = options || {};
698
        options.username = username || options.username || ''; //required
699
700
        return new Promise(async function (resolve, reject) {
701
            let thread = null;
702
            try {
703
                let response = await that.callMethod({
704
                    method: 'member',
705
                    params: options
706
                });
707
                if (
708
                    response
709
                    && response.hasOwnProperty('response')
710
                ) {
711
                    thread = new Member(response.response);
712
                }
713
            } catch (e) {
714
                reject(e);
715
            }
716
            if (thread !== null) {
717
                resolve(thread);
718
            } else {
719
                reject();
720
            }
721
        });
722
    }
723
724
    /**
725
     * TODO untested - does not seem to function yet
726
     * Attempts to delete an existing Post
727
     * @param {number} postId - Post id
728
     * @param {number} threadId - Thread id
729
     * @param {object=} options
730
     * @param {string=} options.reason - Reason for deleting
731
     * @param {number=} options.postid - Ignore, already required at postId
732
     * @param {number=} options.threadid - Ignore, already required at threadId
733
     * TODO note additional options
734
     * @returns {Promise<*>} - Returns a unhandled response currently
735
     * @fulfill {*}
736
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
737
     */
738
    async deletePost(postId, threadId, options) {
739
        let that = this;
740
        options = options || {};
741
        options.postid = postId || options.postid || ''; //required
742
        options.threadid = threadId || options.threadid || ''; // TODO required????
743
744
        return new Promise(async function (resolve, reject) {
745
            try {
746
                let response = await that.callMethod({
747
                    method: 'editpost_deletepost',
748
                    params: options
749
                });
750
                let possibleError = that.constructor.parseErrorMessage(response);
751
                //unknown response
752
                if (
753
                    possibleError === 'redirect_deletepost'
754
                    && response.hasOwnProperty('show')
755
                ) {
756
                    //console.log('response', response);
757
                    resolve(response.show);
758
                } else {
759
                    reject(possibleError || response);
760
                }
761
            } catch (e) {
762
                reject(e);
763
            }
764
        });
765
    }
766
767
    /**
768
     * Attempts to submit a new Thread into a specified Forum. This will also be considered the first Post
769
     * @param {number} forumId - Forum Id
770
     * @param {string} subject - Post/Thread Subject
771
     * @param {string} message - Post Message
772
     * @param {object=} options
773
     * @param {boolean=} options.signature - Optionally append your signature
774
     * @param {number=} options.forumid - Ignore, already required at postId
775
     * @param {string=} options.subject - Ignore, already required at postId
776
     * @param {string=} options.message - Ignore, already required at postId
777
     * TODO note additional options
778
     * @returns {Promise<*>} - Returns a unhandled response currently
779
     * @fulfill {*}
780
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
781
     */
782
    async newThread(forumId, subject, message, options) {
783
        let that = this;
784
        options = options || {};
785
        options.forumid = forumId || options.forumid || ''; //required
786
        options.subject = subject || options.subject || ''; //required
787
        options.message = message || options.message || ''; //required
788
789
        if (options.signature === true) {
790
            //System only handle 1 or 0. defaults to 0
791
            options.signature = '1'; // FIXME This didn't seem to work
792
        }
793
794
        return new Promise(async function (resolve, reject) {
795
            try {
796
                let response = await that.callMethod({
797
                    method: 'newthread_postthread',
798
                    params: options
799
                });
800
                let possibleError = that.constructor.parseErrorMessage(response);
801
                //success is errormessgae 'redirect_postthanks'
802
                //reports threadid and postid
803
                if (
804
                    possibleError === 'redirect_postthanks'
805
                    && response.hasOwnProperty('show')
806
                ) {
807
                    resolve(response.show);
808
                } else {
809
                    reject(possibleError || response);
810
                }
811
            } catch (e) {
812
                reject(e);
813
            }
814
        });
815
    }
816
817
    /**
818
     * TODO incomplete - does not seem to function yet
819
     * Attempts to close a specific Thread. Requires a user to have a 'inline mod' permissions
820
     * @param {number} threadId - Id of Thread to close
821
     * @returns {Promise<*>} - Returns a unhandled response currently
822
     * @fulfill {*}
823
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
824
     */
825
    async modCloseThread(threadId) {
826
        let that = this;
827
        let cookies = {};
828
        if (threadId) {
829
            //TODO multiple ids are delimited with a '-'. eg: 123-345-456
830
            cookies.vbulletin_inlinethread = threadId;
831
        }
832
        return new Promise(async function (resolve, reject) {
833
            try {
834
                let response = await that.callMethod({
835
                    method: 'inlinemod_close',
836
                    cookies: cookies || {}
837
                });
838
                //let possibleError = that.constructor.parseErrorMessage(response);
839
                //unknown responses
840
                /*if (
841
                    possibleError === 'redirect_postthanks'
842
                    && response.hasOwnProperty('show')
843
                ) {*/
844
                resolve(response);
845
                /*} else {
846
                    reject(possibleError || response);
847
                }*/
848
            } catch (e) {
849
                reject(e);
850
            }
851
        });
852
    }
853
854
    /**
855
     * TODO incomplete - does not seem to function yet
856
     * Attempts to open a specific Thread. Requires a user to have a 'inline mod' permissions
857
     * @param {number} threadId - Id of Thread to open
858
     * @returns {Promise<*>} - Returns a unhandled response currently
859
     * @fulfill {*}
860
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
861
     */
862
    async modOpenThread(threadId) {
863
        let that = this;
864
        let cookies = {};
865
        if (threadId) {
866
            //TODO multiple ids are delimited with a '-'. eg: 123-345-456
867
            cookies.vbulletin_inlinethread = threadId;
868
        }
869
        return new Promise(async function (resolve, reject) {
870
            try {
871
                let response = await that.callMethod({
872
                    method: 'inlinemod_open',
873
                    cookies: cookies || {}
874
                });
875
                //let possibleError = that.constructor.parseErrorMessage(response);
876
                //unknown responses
877
                /*if (
878
                    possibleError === 'redirect_postthanks'
879
                    && response.hasOwnProperty('show')
880
                ) {*/
881
                resolve(response);
882
                /*} else {
883
                    reject(possibleError || response);
884
                }*/
885
            } catch (e) {
886
                reject(e);
887
            }
888
        });
889
    }
890
891
    /**
892
     * TODO incomplete - does not seem to function yet
893
     * Attempts to delete a specific Thread. Requires a user to have a 'inline mod' permissions
894
     * @param {number} threadId - Id of Thread to close
895
     * @returns {Promise<*>} - Returns a unhandled response currently
896
     * @fulfill {*}
897
     * @reject {string} - Error Reason. Expects: (TODO list common errors here)
898
     */
899
    async modDeleteThread(threadId) {
900
        let that = this;
901
        let cookies = {};
902
        if (threadId) {
903
            //TODO multiple ids are delimited with a '-'. eg: 123-345-456
904
            cookies.vbulletin_inlinethread = threadId;
905
        }
906
        return new Promise(async function (resolve, reject) {
907
            try {
908
                let response = await that.callMethod({
909
                    method: 'inlinemod_dodeletethreads',
910
                    cookies: cookies || {}
911
                });
912
                //let possibleError = that.constructor.parseErrorMessage(response);
913
                //unknown responses
914
                /*if (
915
                    possibleError === 'redirect_postthanks'
916
                    && response.hasOwnProperty('show')
917
                ) {*/
918
                resolve(response);
919
                /*} else {
920
                    reject(possibleError || response);
921
                }*/
922
            } catch (e) {
923
                reject(e);
924
            }
925
        });
926
    }
927
}
928
929
module.exports = VBApi;
930