@@ -206,7 +206,7 @@ app.post("/admin/:action/:guild/:target", authMiddleware, async (req, res) => {
206206 } catch ( err ) {
207207 return res . status ( 500 ) . json ( { message : "Internal server error" , err } ) ;
208208 }
209- case 'set' :
209+ case 'set' :
210210 if ( ! extraData || typeof extraData . channelId === "undefined" ) {
211211 return res . status ( 400 ) . json ( { message : "Illegal request" } ) ;
212212 }
@@ -308,7 +308,7 @@ app.post("/admin/:action/:guild/:target", authMiddleware, async (req, res) => {
308308 return res . status ( 400 ) . json ( { message : "Illegal request" } ) ;
309309 }
310310
311- if ( ! extraData || ! extraData . user || ! extraData . value ) {
311+ if ( ! extraData || ! extraData . user || ! extraData . value ) {
312312 return res . status ( 400 ) . json ( { message : "Illegal request" } ) ;
313313 }
314314
@@ -339,6 +339,61 @@ app.post("/admin/:action/:guild/:target", authMiddleware, async (req, res) => {
339339 return res . status ( 500 ) . json ( { message : "Internal server error" } ) ;
340340 }
341341 }
342+ case "sync" : {
343+ if ( target !== "polaris" && target !== "mee6" && target !== "lurkr" ) {
344+ return res . status ( 400 ) . json ( { message : "Illegal request" } ) ;
345+ }
346+
347+ switch ( target ) {
348+ case "polaris" : {
349+ try {
350+ const [ err , success ] = await syncFromPolaris ( guild ) ;
351+ if ( err ) {
352+ if ( err instanceof Error && err . message === "Server not found in Polaris" ) {
353+ return res . status ( 404 ) . json ( { message : "Server not found in Polaris" } ) ;
354+ }
355+ return res . status ( 500 ) . json ( { message : "Internal server error" , err } ) ;
356+ } else {
357+ return res . status ( 200 ) . json ( success ) ;
358+ }
359+ } catch ( err ) {
360+ return res . status ( 500 ) . json ( { message : "Internal server error" , err } ) ;
361+ }
362+ }
363+ case "mee6" : {
364+ try {
365+ const [ err , success ] = await syncFromMee6 ( guild ) ;
366+ if ( err ) {
367+ if ( err instanceof Error && err . message === "Server not found in MEE6" ) {
368+ return res . status ( 404 ) . json ( { message : "Server not found in MEE6" } ) ;
369+ }
370+ return res . status ( 500 ) . json ( { message : "Internal server error" , err } ) ;
371+ } else {
372+ return res . status ( 200 ) . json ( success ) ;
373+ }
374+ } catch ( err ) {
375+ return res . status ( 500 ) . json ( { message : "Internal server error" , err } ) ;
376+ }
377+ }
378+ case "lurkr" : {
379+ try {
380+ const [ err , success ] = await syncFromLurkr ( guild ) ;
381+ if ( err ) {
382+ if ( err instanceof Error && err . message === "Server not found in Lurkr" ) {
383+ return res . status ( 404 ) . json ( { message : "Server not found in Lurkr" } ) ;
384+ }
385+ return res . status ( 500 ) . json ( { message : "Internal server error" , err } ) ;
386+ } else {
387+ return res . status ( 200 ) . json ( success ) ;
388+ }
389+ } catch ( err ) {
390+ return res . status ( 500 ) . json ( { message : "Internal server error" , err } ) ;
391+ }
392+ }
393+ default :
394+ return res . status ( 500 ) . json ( { message : "Internal server error" } ) ;
395+ }
396+ }
342397 default :
343398 return res . status ( 400 ) . json ( { message : "Illegal request" } ) ;
344399 }
@@ -445,3 +500,194 @@ async function adminRolesAdd(guild: string, role: string, level: number) {
445500 } ) ;
446501}
447502//#endregion
503+
504+ //#region Syncing
505+ async function syncFromPolaris ( guild : string ) {
506+ const res = await fetch ( `https://gdcolon.com/polaris/api/leaderboard/${ guild } ` ) ;
507+ const data = await res . json ( ) ;
508+ if ( data . apiError && data . code === "invalidServer" ) {
509+ return [ new Error ( "Server not found in Polaris" ) , false ] ;
510+ }
511+ const users = data . leaderboard ;
512+ for ( let i = 1 ; i < data . pageInfo . pageCount ; i ++ ) {
513+ const res = await fetch ( `https://gdcolon.com/polaris/api/leaderboard/${ guild } ?page=${ i + 1 } ` ) ;
514+ const data = await res . json ( ) ;
515+ users . push ( ...data . leaderboard ) ;
516+ }
517+
518+ if ( users . length === 0 ) {
519+ return [ new Error ( "No users found" ) , false ] ;
520+ }
521+
522+ try {
523+ for ( const user of users ) {
524+ const xpValue = user . xp ;
525+ const level = Math . floor ( Math . sqrt ( xpValue / 100 ) ) ;
526+ const nextLevel = level + 1 ;
527+ const nextLevelXp = Math . pow ( nextLevel , 2 ) * 100 ;
528+ const xpNeededForNextLevel = nextLevelXp - xpValue ;
529+ const currentLevelXp = Math . pow ( level , 2 ) * 100 ;
530+ const progressToNextLevel =
531+ ( ( xpValue - currentLevelXp ) / ( nextLevelXp - currentLevelXp ) ) * 100 ;
532+
533+ await new Promise ( ( resolve , reject ) => {
534+ pool . query (
535+ `INSERT INTO users (id, guild_id, xp, pfp, name, nickname, level, xp_needed_next_level, progress_next_level) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)` ,
536+ [
537+ user . id ,
538+ guild ,
539+ xpValue ,
540+ user . avatar ,
541+ user . username ,
542+ user . nickname ?? user . displayName ,
543+ level ,
544+ xpNeededForNextLevel ,
545+ progressToNextLevel . toFixed ( 2 ) ,
546+ ] ,
547+ ( err ) => {
548+ if ( err ) {
549+ console . error ( "Error syncing from Polaris:" , err ) ;
550+ reject ( err ) ;
551+ } else {
552+ resolve ( null ) ;
553+ }
554+ } ,
555+ ) ;
556+ } ) ;
557+ }
558+ return [ null , true ]
559+ } catch ( err ) {
560+ return [ err , false ] ;
561+ }
562+
563+ }
564+
565+ async function syncFromMee6 ( guild : string ) {
566+ const res = await fetch ( `https://mee6.xyz/api/plugins/levels/leaderboard/${ guild } ?limit=1000&page=0` ) ;
567+ const data = await res . json ( ) ;
568+ if ( data . status_code === 404 ) {
569+ return [ new Error ( "Server not found in MEE6" ) , false ] ;
570+ }
571+ const users = data . players ;
572+ let pageNumber = 1 ;
573+ // this is needed because MEE6 doesn't give us the total amount of pages
574+ // eslint-disable-next-line no-constant-condition
575+ while ( true ) {
576+ const res = await fetch ( `https://mee6.xyz/api/plugins/levels/leaderboard/${ guild } ?limit=1000&page=${ pageNumber } ` ) ;
577+ const data = await res . json ( ) ;
578+ users . push ( ...data . players ) ;
579+ if ( data . players . length < 1000 ) break ;
580+ pageNumber += 1 ;
581+ }
582+
583+ if ( users . length === 0 ) {
584+ return [ new Error ( "No users found" ) , false ] ;
585+ }
586+
587+ try {
588+ for ( const user of users ) {
589+ const xpValue = user . xp ;
590+ const level = Math . floor ( Math . sqrt ( xpValue / 100 ) ) ;
591+ const nextLevel = level + 1 ;
592+ const nextLevelXp = Math . pow ( nextLevel , 2 ) * 100 ;
593+ const xpNeededForNextLevel = nextLevelXp - xpValue ;
594+ const currentLevelXp = Math . pow ( level , 2 ) * 100 ;
595+ const progressToNextLevel =
596+ ( ( xpValue - currentLevelXp ) / ( nextLevelXp - currentLevelXp ) ) * 100 ;
597+
598+ await new Promise ( ( resolve , reject ) => {
599+ pool . query (
600+ `INSERT INTO users (id, guild_id, xp, pfp, name, nickname, level, xp_needed_next_level, progress_next_level) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)` ,
601+ [
602+ user . id ,
603+ guild ,
604+ xpValue ,
605+ `https://cdn.discordapp.com/avatars/${ user . id } /${ user . avatar } .webp` ,
606+ user . username ,
607+ user . username ,
608+ level ,
609+ xpNeededForNextLevel ,
610+ progressToNextLevel . toFixed ( 2 ) ,
611+ ] ,
612+ ( err ) => {
613+ if ( err ) {
614+ console . error ( "Error syncing from MEE6:" , err ) ;
615+ reject ( err ) ;
616+ } else {
617+ resolve ( null ) ;
618+ }
619+ } ,
620+ ) ;
621+ } ) ;
622+ }
623+ return [ null , true ]
624+ } catch ( err ) {
625+ return [ err , false ] ;
626+ }
627+ }
628+
629+ async function syncFromLurkr ( guild : string ) {
630+ const res = await fetch ( `https://api.lurkr.gg/v2/levels/${ guild } ?page=1` ) ;
631+ const data = await res . json ( ) ;
632+ if ( data . message === "Guild no found" ) {
633+ return [ new Error ( "Server not found in Lurkr" ) , false ] ;
634+ }
635+ const users = data . levels ;
636+
637+ if ( users . length === 0 ) {
638+ return [ new Error ( "No users found" ) , false ] ;
639+ }
640+
641+ let pageNumber = 2 ;
642+ // this is needed because Lurkr doesn't give us the total amount of pages
643+ // eslint-disable-next-line no-constant-condition
644+ while ( true ) {
645+ const res = await fetch ( `https://api.lurkr.gg/v2/levels/${ guild } ?page=${ pageNumber } ` ) ;
646+ const data = await res . json ( ) ;
647+ users . push ( ...data . levels ) ;
648+ if ( data . levels . length < 100 ) break ;
649+ pageNumber += 1 ;
650+ }
651+
652+ try {
653+ for ( const user of users ) {
654+ const xpValue = user . xp ;
655+ const level = Math . floor ( Math . sqrt ( user . xp / 100 ) ) ;
656+ const nextLevel = level + 1 ;
657+ const nextLevelXp = Math . pow ( nextLevel , 2 ) * 100 ;
658+ const xpNeededForNextLevel = nextLevelXp - user . xp ;
659+ const currentLevelXp = Math . pow ( level , 2 ) * 100 ;
660+ const progressToNextLevel =
661+ ( ( user . xp - currentLevelXp ) / ( nextLevelXp - currentLevelXp ) ) * 100 ;
662+
663+ await new Promise ( ( resolve , reject ) => {
664+ pool . query (
665+ `INSERT INTO users (id, guild_id, xp, pfp, name, nickname, level, xp_needed_next_level, progress_next_level) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)` ,
666+ [
667+ user . userId ,
668+ guild ,
669+ xpValue ,
670+ `https://cdn.discordapp.com/avatars/${ user . userId } /${ user . user . avatar } .webp` ,
671+ user . user . username ,
672+ user . user . username ,
673+ level ,
674+ xpNeededForNextLevel ,
675+ progressToNextLevel . toFixed ( 2 ) ,
676+ ] ,
677+ ( err ) => {
678+ if ( err ) {
679+ console . error ( "Error syncing from Lurkr:" , err ) ;
680+ reject ( err ) ;
681+ } else {
682+ resolve ( null ) ;
683+ }
684+ } ,
685+ ) ;
686+ } ) ;
687+ }
688+ return [ null , true ]
689+ } catch ( err ) {
690+ return [ err , false ] ;
691+ }
692+ }
693+ //#endregion
0 commit comments