utils.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. import config from "config";
  2. import async from "async";
  3. import crypto from "crypto";
  4. import request from "request";
  5. import CoreClass from "../core";
  6. class UtilsModule extends CoreClass {
  7. constructor() {
  8. super("utils");
  9. this.youtubeRequestCallbacks = [];
  10. this.youtubeRequestsPending = 0;
  11. this.youtubeRequestsActive = false;
  12. }
  13. initialize() {
  14. return new Promise(resolve => {
  15. this.io = this.moduleManager.modules.io;
  16. this.db = this.moduleManager.modules.db;
  17. this.spotify = this.moduleManager.modules.spotify;
  18. this.cache = this.moduleManager.modules.cache;
  19. resolve();
  20. });
  21. }
  22. PARSE_COOKIES(payload) {
  23. // cookieString
  24. return new Promise((resolve, reject) => {
  25. const cookies = {};
  26. if (typeof payload.cookieString !== "string") return reject(new Error("Cookie string is not a string"));
  27. // eslint-disable-next-line array-callback-return
  28. payload.cookieString.split("; ").map(cookie => {
  29. cookies[cookie.substring(0, cookie.indexOf("="))] = cookie.substring(
  30. cookie.indexOf("=") + 1,
  31. cookie.length
  32. );
  33. });
  34. return resolve(cookies);
  35. });
  36. }
  37. // COOKIES_TO_STRING() {//cookies
  38. // return new Promise((resolve, reject) => {
  39. // let newCookie = [];
  40. // for (let prop in cookie) {
  41. // newCookie.push(prop + "=" + cookie[prop]);
  42. // }
  43. // return newCookie.join("; ");
  44. // });
  45. // }
  46. REMOVE_COOKIE(payload) {
  47. // cookieString, cookieName
  48. return new Promise((resolve, reject) => {
  49. let cookies;
  50. try {
  51. cookies = this.runJob("PARSE_COOKIES", {
  52. cookieString: payload.cookieString
  53. });
  54. } catch (err) {
  55. return reject(err);
  56. }
  57. delete cookies[payload.cookieName];
  58. return resolve(this.toString(cookies));
  59. });
  60. }
  61. HTML_ENTITIES(payload) {
  62. // str
  63. return new Promise(resolve => {
  64. resolve(
  65. String(payload.str)
  66. .replace(/&/g, "&")
  67. .replace(/</g, "&lt;")
  68. .replace(/>/g, "&gt;")
  69. .replace(/"/g, "&quot;")
  70. );
  71. });
  72. }
  73. GENERATE_RANDOM_STRING(payload) {
  74. // length
  75. return new Promise((resolve, reject) => {
  76. const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
  77. let randomNum;
  78. try {
  79. randomNum = this.runJob("GET_RANDOM_NUMBER", {
  80. min: 0,
  81. max: chars.length - 1
  82. });
  83. } catch (err) {
  84. return reject(err);
  85. }
  86. const result = [];
  87. for (let i = 0; i < payload.length; i += 1) {
  88. result.push(chars[randomNum]);
  89. }
  90. return resolve(result.join(""));
  91. });
  92. }
  93. async GET_SOCKET_FROM_ID(payload) {
  94. // socketId
  95. const io = await this.io.runJob("IO", {});
  96. return new Promise(resolve => resolve(io.sockets.sockets[payload.socketId]));
  97. }
  98. GET_RANDOM_NUMBER(payload) {
  99. // min, max
  100. return new Promise(resolve => {
  101. resolve(Math.floor(Math.random() * (payload.max - payload.min + 1)) + payload.min);
  102. });
  103. }
  104. CONVERT_TIME(payload) {
  105. // duration
  106. return new Promise(resolve => {
  107. let { duration } = payload;
  108. let a = duration.match(/\d+/g);
  109. if (duration.indexOf("M") >= 0 && duration.indexOf("H") === -1 && duration.indexOf("S") === -1) {
  110. a = [0, a[0], 0];
  111. }
  112. if (duration.indexOf("H") >= 0 && duration.indexOf("M") === -1) {
  113. a = [a[0], 0, a[1]];
  114. }
  115. if (duration.indexOf("H") >= 0 && duration.indexOf("M") === -1 && duration.indexOf("S") === -1) {
  116. a = [a[0], 0, 0];
  117. }
  118. duration = 0;
  119. if (a.length === 3) {
  120. duration += parseInt(a[0]) * 3600;
  121. duration += parseInt(a[1]) * 60;
  122. duration += parseInt(a[2]);
  123. }
  124. if (a.length === 2) {
  125. duration += parseInt(a[0]) * 60;
  126. duration += parseInt(a[1]);
  127. }
  128. if (a.length === 1) {
  129. duration += parseInt(a[0]);
  130. }
  131. const hours = Math.floor(duration / 3600);
  132. const minutes = Math.floor((duration % 3600) / 60);
  133. const seconds = Math.floor((duration % 3600) % 60);
  134. resolve(
  135. (hours < 10 ? `0${hours}:` : `${hours}:`) +
  136. (minutes < 10 ? `0${minutes}:` : `${minutes}:`) +
  137. (seconds < 10 ? `0${seconds}` : seconds)
  138. );
  139. });
  140. }
  141. GUID() {
  142. return new Promise(resolve => {
  143. resolve(
  144. [1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1]
  145. .map(b =>
  146. b
  147. ? Math.floor((1 + Math.random()) * 0x10000)
  148. .toString(16)
  149. .substring(1)
  150. : "-"
  151. )
  152. .join("")
  153. );
  154. });
  155. }
  156. async SOCKET_FROM_SESSION(payload) {
  157. // socketId
  158. const io = await this.io.runJob("IO", {});
  159. return new Promise((resolve, reject) => {
  160. const ns = io.of("/");
  161. if (ns) {
  162. return resolve(ns.connected[payload.socketId]);
  163. }
  164. return reject();
  165. });
  166. }
  167. async SOCKETS_FROM_SESSION_ID(payload) {
  168. const io = await this.io.runJob("IO", {});
  169. return new Promise(resolve => {
  170. const ns = io.of("/");
  171. const sockets = [];
  172. if (ns) {
  173. return async.each(
  174. Object.keys(ns.connected),
  175. (id, next) => {
  176. const { session } = ns.connected[id];
  177. if (session.sessionId === payload.sessionId) sockets.push(session.sessionId);
  178. next();
  179. },
  180. () => {
  181. resolve({ sockets });
  182. }
  183. );
  184. }
  185. return resolve();
  186. });
  187. }
  188. async SOCKETS_FROM_USER(payload) {
  189. const io = await this.io.runJob("IO", {});
  190. return new Promise((resolve, reject) => {
  191. const ns = io.of("/");
  192. const sockets = [];
  193. if (ns) {
  194. return async.each(
  195. Object.keys(ns.connected),
  196. (id, next) => {
  197. const { session } = ns.connected[id];
  198. this.cache
  199. .runJob("HGET", {
  200. table: "sessions",
  201. key: session.sessionId
  202. })
  203. .then(session => {
  204. if (session && session.userId === payload.userId) sockets.push(ns.connected[id]);
  205. next();
  206. })
  207. .catch(err => {
  208. next(err);
  209. });
  210. },
  211. err => {
  212. if (err) return reject(err);
  213. return resolve({ sockets });
  214. }
  215. );
  216. }
  217. return resolve();
  218. });
  219. }
  220. async SOCKETS_FROM_IP(payload) {
  221. const io = await this.io.runJob("IO", {});
  222. return new Promise(resolve => {
  223. const ns = io.of("/");
  224. const sockets = [];
  225. if (ns) {
  226. return async.each(
  227. Object.keys(ns.connected),
  228. (id, next) => {
  229. const { session } = ns.connected[id];
  230. this.cache
  231. .runJob("HGET", {
  232. table: "sessions",
  233. key: session.sessionId
  234. })
  235. .then(session => {
  236. if (session && ns.connected[id].ip === payload.ip) sockets.push(ns.connected[id]);
  237. next();
  238. })
  239. .catch(() => next());
  240. },
  241. () => {
  242. resolve({ sockets });
  243. }
  244. );
  245. }
  246. return resolve();
  247. });
  248. }
  249. async SOCKETS_FROM_USER_WITHOUT_CACHE(payload) {
  250. const io = await this.io.runJob("IO", {});
  251. return new Promise(resolve => {
  252. const ns = io.of("/");
  253. const sockets = [];
  254. if (ns) {
  255. return async.each(
  256. Object.keys(ns.connected),
  257. (id, next) => {
  258. const { session } = ns.connected[id];
  259. if (session.userId === payload.userId) sockets.push(ns.connected[id]);
  260. next();
  261. },
  262. () => {
  263. resolve({ sockets });
  264. }
  265. );
  266. }
  267. return resolve();
  268. });
  269. }
  270. SOCKET_LEAVE_ROOMS(payload) {
  271. // socketId
  272. return new Promise((resolve, reject) => {
  273. let socket;
  274. try {
  275. socket = this.runJob("SOCKET_FROM_SESSION", {
  276. socketId: payload.socketId
  277. });
  278. } catch (err) {
  279. return reject(err);
  280. }
  281. const { rooms } = socket;
  282. for (let room = 0, roomKeys = Object.keys(rooms); room < roomKeys.length; room += 1) {
  283. socket.leave(room);
  284. }
  285. return resolve();
  286. });
  287. }
  288. async SOCKET_JOIN_ROOM(payload) {
  289. const socket = await this.runJob("SOCKET_FROM_SESSION", {
  290. socketId: payload.socketId
  291. });
  292. // socketId, room
  293. return new Promise(resolve => {
  294. const { rooms } = socket;
  295. for (let room = 0, roomKeys = Object.keys(rooms); room < roomKeys.length; room += 1) {
  296. socket.leave(room);
  297. }
  298. socket.join(payload.room);
  299. return resolve();
  300. });
  301. }
  302. async SOCKET_JOIN_SONG_ROOM(payload) {
  303. // socketId, room
  304. const socket = await this.runJob("SOCKET_FROM_SESSION", {
  305. socketId: payload.socketId
  306. });
  307. return new Promise(resolve => {
  308. const { rooms } = socket;
  309. for (let room = 0, roomKeys = Object.keys(rooms); room < roomKeys.length; room += 1) {
  310. if (room.indexOf("song.") !== -1) socket.leave(rooms);
  311. }
  312. socket.join(payload.room);
  313. return resolve();
  314. });
  315. }
  316. SOCKETS_JOIN_SONG_ROOM(payload) {
  317. // sockets, room
  318. return new Promise(resolve => {
  319. for (let id = 0, socketKeys = Object.keys(payload.sockets); id < socketKeys.length; id += 1) {
  320. const socket = payload.sockets[socketKeys[id]];
  321. const { rooms } = socket;
  322. for (let room = 0, roomKeys = Object.keys(rooms); room < roomKeys.length; room += 1) {
  323. if (room.indexOf("song.") !== -1) socket.leave(room);
  324. }
  325. socket.join(payload.room);
  326. }
  327. return resolve();
  328. });
  329. }
  330. SOCKETS_LEAVE_SONG_ROOMS(payload) {
  331. // sockets
  332. return new Promise(resolve => {
  333. for (let id = 0, socketKeys = Object.keys(payload.sockets); id < socketKeys.length; id += 1) {
  334. const socket = payload.sockets[socketKeys[id]];
  335. const { rooms } = socket;
  336. for (let room = 0, roomKeys = Object.keys(rooms); room < roomKeys.length; room += 1) {
  337. if (room.indexOf("song.") !== -1) socket.leave(room);
  338. }
  339. }
  340. resolve();
  341. });
  342. }
  343. async EMIT_TO_ROOM(payload) {
  344. // room, ...args
  345. const io = await this.io.runJob("IO", {});
  346. return new Promise(resolve => {
  347. const { sockets } = io.sockets;
  348. for (let id = 0, socketKeys = Object.keys(sockets); id < socketKeys.length; id += 1) {
  349. const socket = sockets[socketKeys[id]];
  350. if (socket.rooms[payload.room]) {
  351. socket.emit(...payload.args);
  352. }
  353. }
  354. return resolve();
  355. });
  356. }
  357. async GET_ROOM_SOCKETS(payload) {
  358. const io = await this.io.runJob("IO", {});
  359. // room
  360. return new Promise(resolve => {
  361. const { sockets } = io.sockets;
  362. const roomSockets = [];
  363. for (let id = 0, socketKeys = Object.keys(sockets); id < socketKeys.length; id += 1) {
  364. const socket = sockets[socketKeys[id]];
  365. if (socket.rooms[payload.room]) roomSockets.push(socket);
  366. }
  367. return resolve(roomSockets);
  368. });
  369. }
  370. GET_SONG_FROM_YOUTUBE(payload) {
  371. // songId, cb
  372. return new Promise((resolve, reject) => {
  373. this.youtubeRequestCallbacks.push({
  374. cb: () => {
  375. this.youtubeRequestsActive = true;
  376. const youtubeParams = [
  377. "part=snippet,contentDetails,statistics,status",
  378. `id=${encodeURIComponent(payload.songId)}`,
  379. `key=${config.get("apis.youtube.key")}`
  380. ].join("&");
  381. request(`https://www.googleapis.com/youtube/v3/videos?${youtubeParams}`, (err, res, body) => {
  382. this.youtubeRequestCallbacks.splice(0, 1);
  383. if (this.youtubeRequestCallbacks.length > 0) {
  384. this.youtubeRequestCallbacks[0].cb(this.youtubeRequestCallbacks[0].songId);
  385. } else this.youtubeRequestsActive = false;
  386. if (err) {
  387. console.error(err);
  388. return null;
  389. }
  390. body = JSON.parse(body);
  391. if (body.error) {
  392. console.log("ERROR", "GET_SONG_FROM_YOUTUBE", `${body.error.message}`);
  393. return reject(new Error("An error has occured. Please try again later."));
  394. }
  395. if (body.items[0] === undefined)
  396. return reject(
  397. new Error("The specified video does not exist or cannot be publicly accessed.")
  398. );
  399. // TODO Clean up duration converter
  400. let dur = body.items[0].contentDetails.duration;
  401. dur = dur.replace("PT", "");
  402. let duration = 0;
  403. dur = dur.replace(/([\d]*)H/, (v, v2) => {
  404. v2 = Number(v2);
  405. duration = v2 * 60 * 60;
  406. return "";
  407. });
  408. dur = dur.replace(/([\d]*)M/, (v, v2) => {
  409. v2 = Number(v2);
  410. duration += v2 * 60;
  411. return "";
  412. });
  413. // eslint-disable-next-line no-unused-vars
  414. dur = dur.replace(/([\d]*)S/, (v, v2) => {
  415. v2 = Number(v2);
  416. duration += v2;
  417. return "";
  418. });
  419. const song = {
  420. songId: body.items[0].id,
  421. title: body.items[0].snippet.title,
  422. duration
  423. };
  424. return resolve({ song });
  425. });
  426. },
  427. songId: payload.songId
  428. });
  429. if (!this.youtubeRequestsActive) {
  430. this.youtubeRequestCallbacks[0].cb(this.youtubeRequestCallbacks[0].songId);
  431. }
  432. });
  433. }
  434. FILTER_MUSIC_VIDEOS_YOUTUBE(payload) {
  435. // videoIds, cb
  436. return new Promise((resolve, reject) => {
  437. /**
  438. * @param {Function} cb2 - callback
  439. */
  440. function getNextPage(cb2) {
  441. const localVideoIds = payload.videoIds.splice(0, 50);
  442. const youtubeParams = [
  443. "part=topicDetails",
  444. `id=${encodeURIComponent(localVideoIds.join(","))}`,
  445. `maxResults=50`,
  446. `key=${config.get("apis.youtube.key")}`
  447. ].join("&");
  448. request(`https://www.googleapis.com/youtube/v3/videos?${youtubeParams}`, (err, res, body) => {
  449. if (err) {
  450. console.error(err);
  451. return reject(new Error("Failed to find playlist from YouTube"));
  452. }
  453. body = JSON.parse(body);
  454. if (body.error) {
  455. console.log("ERROR", "FILTER_MUSIC_VIDEOS_YOUTUBE", `${body.error.message}`);
  456. return reject(new Error("An error has occured. Please try again later."));
  457. }
  458. const songIds = [];
  459. body.items.forEach(item => {
  460. const songId = item.id;
  461. if (!item.topicDetails) return;
  462. if (item.topicDetails.relevantTopicIds.indexOf("/m/04rlf") !== -1) {
  463. songIds.push(songId);
  464. }
  465. });
  466. if (payload.videoIds.length > 0) {
  467. return getNextPage(newSongIds => {
  468. cb2(songIds.concat(newSongIds));
  469. });
  470. }
  471. return cb2(songIds);
  472. });
  473. }
  474. if (payload.videoIds.length === 0) resolve({ songIds: [] });
  475. else getNextPage(songIds => resolve({ songIds }));
  476. });
  477. }
  478. GET_PLAYLIST_FROM_YOUTUBE(payload) {
  479. // payload includes: url, musicOnly
  480. return new Promise((resolve, reject) => {
  481. const local = this;
  482. const name = "list".replace(/[\\[]/, "\\[").replace(/[\]]/, "\\]");
  483. const regex = new RegExp(`[\\?&]${name}=([^&#]*)`);
  484. const splitQuery = regex.exec(payload.url);
  485. if (!splitQuery) {
  486. console.log("ERROR", "GET_PLAYLIST_FROM_YOUTUBE", "Invalid YouTube playlist URL query.");
  487. return reject(new Error("An error has occured. Please try again later."));
  488. }
  489. const playlistId = splitQuery[1];
  490. /**
  491. * @param {string} pageToken - page token for YouTube API
  492. * @param {Array} songs - array of sogns
  493. */
  494. function getPage(pageToken, songs) {
  495. const nextPageToken = pageToken ? `pageToken=${pageToken}` : "";
  496. const youtubeParams = [
  497. "part=contentDetails",
  498. `playlistId=${encodeURIComponent(playlistId)}`,
  499. `maxResults=50`,
  500. `key=${config.get("apis.youtube.key")}`,
  501. nextPageToken
  502. ].join("&");
  503. request(
  504. `https://www.googleapis.com/youtube/v3/playlistItems?${youtubeParams}`,
  505. async (err, res, body) => {
  506. if (err) {
  507. console.error(err);
  508. return reject(new Error("Failed to find playlist from YouTube"));
  509. }
  510. body = JSON.parse(body);
  511. if (body.error) {
  512. console.log("ERROR", "GET_PLAYLIST_FROM_YOUTUBE", `${body.error.message}`);
  513. return reject(new Error("An error has occured. Please try again later."));
  514. }
  515. songs = songs.concat(body.items);
  516. if (body.nextPageToken) return getPage(body.nextPageToken, songs);
  517. songs = songs.map(song => song.contentDetails.videoId);
  518. if (!payload.musicOnly) return resolve({ songs });
  519. return local
  520. .runJob("FILTER_MUSIC_VIDEOS_YOUTUBE", {
  521. videoIds: songs.slice()
  522. })
  523. .then(filteredSongs => {
  524. resolve({ filteredSongs, songs });
  525. });
  526. }
  527. );
  528. }
  529. return getPage(null, []);
  530. });
  531. }
  532. async GET_SONG_FROM_SPOTIFY(payload) {
  533. // song
  534. const token = await this.spotify.runJob("GET_TOKEN", {});
  535. return new Promise((resolve, reject) => {
  536. if (!config.get("apis.spotify.enabled")) return reject(new Error("Spotify is not enabled."));
  537. const song = { ...payload.song };
  538. const spotifyParams = [`q=${encodeURIComponent(payload.song.title)}`, `type=track`].join("&");
  539. const options = {
  540. url: `https://api.spotify.com/v1/search?${spotifyParams}`,
  541. headers: {
  542. Authorization: `Bearer ${token}`
  543. }
  544. };
  545. return request(options, (err, res, body) => {
  546. if (err) console.error(err);
  547. body = JSON.parse(body);
  548. if (body.error) console.error(body.error);
  549. for (let i = 0, bodyKeys = Object.keys(body); i < bodyKeys.length; i += 1) {
  550. const { items } = body[i];
  551. for (let j = 0, itemKeys = Object.keys(body); j < itemKeys.length; j += 1) {
  552. const item = items[j];
  553. let hasArtist = false;
  554. for (let k = 0; k < item.artists.length; k += 1) {
  555. const artist = item.artists[k];
  556. if (song.title.indexOf(artist.name) !== -1) hasArtist = true;
  557. }
  558. if (hasArtist && song.title.indexOf(item.name) !== -1) {
  559. song.duration = item.duration_ms / 1000;
  560. song.artists = item.artists.map(artist => artist.name);
  561. song.title = item.name;
  562. song.explicit = item.explicit;
  563. song.thumbnail = item.album.images[1].url;
  564. break;
  565. }
  566. }
  567. }
  568. resolve({ song });
  569. });
  570. });
  571. }
  572. async GET_SONGS_FROM_SPOTIFY(payload) {
  573. // title, artist
  574. const token = await this.spotify.runJob("GET_TOKEN", {});
  575. return new Promise((resolve, reject) => {
  576. if (!config.get("apis.spotify.enabled")) return reject(new Error("Spotify is not enabled."));
  577. const spotifyParams = [`q=${encodeURIComponent(payload.title)}`, `type=track`].join("&");
  578. const options = {
  579. url: `https://api.spotify.com/v1/search?${spotifyParams}`,
  580. headers: {
  581. Authorization: `Bearer ${token}`
  582. }
  583. };
  584. return request(options, (err, res, body) => {
  585. if (err) return console.error(err);
  586. body = JSON.parse(body);
  587. if (body.error) return console.error(body.error);
  588. const songs = [];
  589. for (let i = 0, bodyKeys = Object.keys(body); i < bodyKeys.length; i += 1) {
  590. const { items } = body[i];
  591. for (let j = 0, itemKeys = Object.keys(body); j < itemKeys.length; j += 1) {
  592. const item = items[j];
  593. let hasArtist = false;
  594. for (let k = 0; k < item.artists.length; k += 1) {
  595. const localArtist = item.artists[k];
  596. if (payload.artist.toLowerCase() === localArtist.name.toLowerCase()) hasArtist = true;
  597. }
  598. if (
  599. hasArtist &&
  600. (payload.title.indexOf(item.name) !== -1 || item.name.indexOf(payload.title) !== -1)
  601. ) {
  602. const song = {};
  603. song.duration = item.duration_ms / 1000;
  604. song.artists = item.artists.map(artist => artist.name);
  605. song.title = item.name;
  606. song.explicit = item.explicit;
  607. song.thumbnail = item.album.images[1].url;
  608. songs.push(song);
  609. }
  610. }
  611. }
  612. return resolve({ songs });
  613. });
  614. });
  615. }
  616. SHUFFLE(payload) {
  617. // array
  618. return new Promise(resolve => {
  619. const array = payload.array.slice();
  620. let currentIndex = payload.array.length;
  621. let temporaryValue;
  622. let randomIndex;
  623. // While there remain elements to shuffle...
  624. while (currentIndex !== 0) {
  625. // Pick a remaining element...
  626. randomIndex = Math.floor(Math.random() * currentIndex);
  627. currentIndex -= 1;
  628. // And swap it with the current element.
  629. temporaryValue = array[currentIndex];
  630. array[currentIndex] = array[randomIndex];
  631. array[randomIndex] = temporaryValue;
  632. }
  633. resolve({ array });
  634. });
  635. }
  636. GET_ERROR(payload) {
  637. // err
  638. return new Promise(resolve => {
  639. let error = "An error occurred.";
  640. if (typeof payload.error === "string") error = payload.error;
  641. else if (payload.error.message) {
  642. if (payload.error.message !== "Validation failed") error = payload.error.message;
  643. else error = payload.error.errors[Object.keys(payload.error.errors)].message;
  644. }
  645. resolve(error);
  646. });
  647. }
  648. CREATE_GRAVATAR(payload) {
  649. // email
  650. return new Promise(resolve => {
  651. const hash = crypto.createHash("md5").update(payload.email).digest("hex");
  652. resolve(`https://www.gravatar.com/avatar/${hash}`);
  653. });
  654. }
  655. DEBUG() {
  656. return new Promise(resolve => resolve());
  657. }
  658. }
  659. export default new UtilsModule();