2
0

utils.js 26 KB

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