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
![]() |
|||
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
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 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. ![]() |
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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 |