diff --git a/package.json b/package.json index b22f812..352806f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "squad-whitelister", - "version": "1.4.5", + "version": "1.4.6", "scripts": { "dev": "vite", "build": "vue-tsc --noEmit && vite build", diff --git a/release/package.json b/release/package.json index fc5f663..77522c2 100644 --- a/release/package.json +++ b/release/package.json @@ -1,6 +1,6 @@ { "name": "squad-whitelister", - "version": "1.4.5", + "version": "1.4.6", "dependencies": { "axios": "^0.26.1", "body-parser": "^1.20.0", diff --git a/release/server.js b/release/server.js index 6f8e366..b08559a 100644 --- a/release/server.js +++ b/release/server.js @@ -1 +1 @@ -const{match:match}=require("assert"),cp=require("child_process");var installingDependencies=!1;const irequire=async e=>{try{require.resolve(e)}catch(e){installingDependencies||(installingDependencies=!0,console.log("INSTALLING DEPENDENCIES...\nTHIS PROCESS MAY TAKE SOME TIME. PLEASE WAIT")),cp.execSync("npm install"),await setImmediate((()=>{})),console.log("DEPENDECIES INSTALLED")}console.log(`Requiring "${e}"`);try{return require(e)}catch(s){console.log(`Could not include "${e}". Restart the script`),restartProcess(0,1)}};installUpdateDependencies=async()=>{console.log("INSTALLING/UPDATING DEPENDENCIES...\nTHIS PROCESS MAY TAKE SOME TIME. PLEASE WAIT"),cp.execSync("npm install")};var subcomponent_status={discord_bot:!1,squadjs:[]},subcomponent_data={discord_bot:{invite_link:""},squadjs:[],database:{root_user_registered:!1},updater:{updating:!1}};async function init(){const e=(await irequire("./package.json")).version,s=await irequire("fs-extra"),t=await irequire("node-stream-zip"),o=await irequire("https"),n=await irequire("http"),i=await irequire("express"),r=i(),a=await irequire("path"),l=await irequire("mongodb"),c=l.MongoClient,d=l.ObjectID,u=await irequire("crypto"),p=await irequire("body-parser"),_=await irequire("cookie-parser"),m=await irequire("nocache"),g=await irequire("log4js"),f=await irequire("axios"),h=(await irequire("minimist"))(process.argv.slice(2)),y=(await irequire("node-run-cmd"),await irequire("express-force-ssl")),w=await irequire("find-free-port"),{mainModule:S}=await irequire("process"),v=await irequire("discord.js"),{io:b}=await irequire("socket.io-client"),$=await irequire("dns"),k=require("util").promisify($.lookup);try{(await irequire("dotenv")).config()}catch(e){console.error(e)}const I=!0;var D=0;const O=h.c||"conf.json",C=console.log,x=console.error;let q=new Date;const A=a.join(__dirname,"logs",q.toISOString().replace(/T/g,"_").replace(/(:|-|\.|Z)/g,"")+".log");s.existsSync("logs")||s.mkdirSync("logs"),s.existsSync(A)||s.writeFileSync(A,""),g.configure({appenders:{App:{type:"file",filename:A}},categories:{default:{appenders:["App"],level:"all"}}});const T=g.getLogger("App");console.log("Log-file:",A);var E,R={http:void 0,https:void 0,configs:{https:{port:void 0},http:{port:void 0}},logging:{requests:!1}},P={ws:null,initDone:!1},j=[];const L=!0;var N,U;function F(){function n(e,s,t){z((e=>{e.collection("whitelists").deleteOne({expiration:{$lte:new Date}},((e,s)=>{e&&console.error(e),t&&t()}))}))}function l(e,s){const t=e.originalUrl.replace(/\?.*$/,"");let o=[];for(let e of o)if(t.startsWith(e))return e;return t.endsWith("/")?t.substring(0,t.length-1):t}function c(e,s,t){w(e)?t():s.redirect("/")}function g(o=!1,n=null){let i=new Date;console.log("Current version: ",e,"\n > Checking for updates",i.toLocaleString()),f.get("https://api.github.com/repos/fantinodavide/Squad_Whitelister/releases").then((i=>{const r=i.data[0],l=r.tag_name.toUpperCase().replace("V","").split("."),c=e.toString().split("."),d=E.other.install_beta_versions&&r.prerelease||!r.prerelease,u=parseInt(c[0]) Update found: "+r.tag_name,r.name),o?function(e){const o=e.assets.filter((e=>"release.zip"==e.name))[0].browser_download_url;console.log(" > Downloading update: "+e.tag_name,e.name,o);const n=a.resolve(__dirname,"tmp_update"),i=a.resolve(n,"gitupd.zip");s.existsSync(n)||s.mkdirSync(n);const r=s.createWriteStream(i);f({method:"get",url:o,responseType:"stream"}).then((e=>{e.data.pipe(r)})),r.on("finish",(e=>{setTimeout((()=>{!function(e,o,n){const i=new t({file:o,storeEntries:!0,skipEntryNameValidation:!0});i.on("ready",(()=>{s.remove(__dirname+"/dist",(()=>{i.extract("release/",__dirname,(async(t,o)=>{i.close(),await installUpdateDependencies(),console.log(" > Extracted",o,"files"),s.remove(e,(()=>{console.log(`${e} folder deleted`);const s=5e3;console.log(" > Restart in",s/1e3,"seconds"),restartProcess(s,0,h)}))}))}))}))}(n,i)}),1e3)})),r.on("error",(e=>{console.error(e)}))}(r):n&&n()):(console.log(" > No updates found"),n&&n())})).catch((e=>{console.error(" > Couldn't check for updates. Proceding startup",e),n&&n()}))}function w(e){return e.userSession&&e.userSession.access_level<=5}g(E.other.automatic_updates,(()=>{console.log(" > Starting up"),setInterval((()=>{g(E.other.automatic_updates)}),1e3*E.other.update_check_interval_seconds),function(){function e(){z((async e=>{const s=await e.collection("configs").findOne({category:"seeding_tracker"});if(!s)return;const t=s.config;"fixed_reset"==t.tracking_mode&&t.reset_seeding_time&&t.next_reset&&new Date>new Date(t.next_reset)&&(e.collection("players").updateMany({},{$set:{seeding_points:0}}),e.collection("configs").updateOne({category:"seeding_tracker"},{$set:{"config.next_reset":new Date((new Date).valueOf()+t.reset_seeding_time.value*t.reset_seeding_time.option).toISOString().split("T")[0]}}))}))}e(),setInterval(e,20)}(),U((async()=>{if(await async function(e=null){let s=null,t=[];console.log("Starting SquadJS WebSockets");for(let n in E.squadjs){subcomponent_status.squadjs[n]=!1;const i=E.squadjs[n];if(i.websocket&&""!=i.websocket.token&&""!=i.websocket.host){const r=setTimeout((()=>{console.error(` > Connection ${+n+1} timed out. Check your SquadJS WebSocket configuration.`),console.log(` > Proceding without SquadJS WebSocket ${+n+1}.`)}),1e4),a=(await k(i.websocket.host)).address;async function o(e,s=5e3){z((async t=>{const o=[{$match:{steamid64:e.player.steamID}},{$lookup:{from:"groups",localField:"id_group",foreignField:"_id",as:"group_full_data"}}];t.collection("whitelists").aggregate(o).toArray((async(o,i)=>{o?W(null,o):t.collection("players").findOne({steamid64:e.player.steamID},(async(o,i)=>{if(o)W(null,o);else{const o=(await t.collection("configs").findOne({category:"seeding_tracker"})).config,r=o.reward_needed_time.value*(o.reward_needed_time.option/1e3/60),a=Math.floor(100*i.seeding_points/r)||0;let l="Welcome "+e.player.name+"\n\n";if(subcomponent_status.squadjs){let s=(await B(e.player.steamID)).filter((e=>e.approved));if(s.length>0){l+="Groups:\n";for(let e of s)l+=` - ${e.name}`,e.expiration&&(l+=": "+(((e.expiration-new Date)/1e3/60/60).toFixed(1)+"h left")),l+="\n"}}if(subcomponent_status.discord_bot){let e="";if(i&&i.discord_user_id&&""!=i.discord_user_id){const s=await U.users.fetch(i.discord_user_id);e=s.username+(s.discriminator?"#"+s.discriminator:"")}"true"==o.reward_enabled&&(l+="\nSeeding Reward: "+a+"%"),l+="\nDiscord Username: "+(""!=e?e:"Not linked")}subcomponent_status.squadjs&&setTimeout((()=>{subcomponent_data.squadjs[n].socket.emit("rcon.warn",e.player.steamID,l,(e=>{})),console.log(l)}),s)}}))}))}))}subcomponent_data.squadjs[n]||(subcomponent_data.squadjs[n]={}),subcomponent_data.squadjs[n].socket=b(`ws://${a}:${i.websocket.port}`,{auth:{token:i.websocket.token},autoUnref:!0}),subcomponent_data.squadjs[n].socket.on("connect",(async()=>{clearTimeout(r),console.log(`SquadJS Websocket ${+n+1} Connected`),subcomponent_data.squadjs[n].socket.emit("rcon.warn","76561198419229279","Whitelister Test Connected",(()=>{})),clearInterval(s),subcomponent_status.squadjs[n]=!0,P.initDone||(P.initDone=!0)})),subcomponent_data.squadjs[n].socket.on("disconnect",(async()=>{subcomponent_status.squadjs[n]=!1,console.log("SquadJS WebSocket\n > Disconnected\n > Trying to reconnect"),s=setInterval((()=>{subcomponent_status.squadjs||subcomponent_data.squadjs[n].connect()}),1e4)})),subcomponent_data.squadjs[n].socket.on("PLAYER_CONNECTED",(async e=>{try{e&&e.player&&e.player.steamID&&(z((async s=>{s.collection("players").updateOne({steamid64:e.player.steamID},{$set:{username:e.player.name}},{upsert:!0})})),setTimeout((()=>{o(e)}),1e4))}catch(e){console.error("PLAYER_CONNECTED ERROR",e)}})),subcomponent_data.squadjs[n].socket.on("CHAT_MESSAGE",(async e=>{switch(e.message.toLowerCase().replace(/^(!|\/)/,"")){case"test":break;case"playerinfo":console.log(e);const s=await z(),t=await s.collection("players").findOne({steamid64:e.player.steamID},{projection:{_id:0,seeding_points:1}});console.log("olddata",t);break;case"profile":o(e,0);break;default:6!=e.message.length||e.message.includes(" ")||z((async s=>{s.collection("profilesLinking").findOne({code:e.message},(async(t,o)=>{if(t)W(null,t);else if(o)if(o.expiration>new Date){const t=await U.users.fetch(o.discordUserId),i=t.username+(t.discriminator?"#"+t.discriminator:""),r=await s.collection("players").findOne({steamid64:e.player.steamID},{projection:{_id:0,seeding_points:1}});s.collection("players").updateOne({discord_user_id:o.discordUserId},{$set:{steamid64:e.player.steamID,username:e.player.name,discord_user_id:o.discordUserId,discord_username:i,...r}},{upsert:!0},((r,a)=>{s.collection("players").deleteOne({steamid64:e.player.steamID,discord_user_id:{$exists:!1}},((r,a)=>{if(r)return W(null,r);s.collection("profilesLinking").deleteOne({_id:o._id}),r?W(null,r):(subcomponent_data.squadjs[n].socket.emit("rcon.warn",e.steamID,"Linked Discord profile: "+i,(e=>{})),t.send({embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Profile Linked").setDescription("Your Discord profile has been linked to a Steam profile").addFields({name:"Steam Username",value:e.name,inline:!0},{name:"SteamID",value:v.hyperlink(e.steamID,"https://steamcommunity.com/profiles/"+e.steamID),inline:!0})]}))}))}))}else s.collection("profilesLinking").deleteOne({_id:o._id})}))}))}}))}else console.log(` > ${+n+1} Not configured. Skipping.`),e&&e()}await Promise.all(t)}(),async function(){console.log("Seeding Tracker started");const e=1;let s=!0;async function t(){const e=await z(),t=await e.collection("configs").findOne({category:"seeding_tracker"}),o=t.config,n=o.reward_needed_time.value*(o.reward_needed_time.option/1e3/60),i=[],r=[];for(let e in subcomponent_data.squadjs){if(!subcomponent_status.squadjs[e])continue;const s=(await M(subcomponent_data.squadjs[e].socket,"rcon.getListPlayers",{})).map((s=>({...s,sqJsConnectionIndex:+e})));s&&s.length>=(o.seeding_start_player_count||2)&&(r[e]=!0),i.push(...s)}s=!1,r.includes(!0)&&z((async e=>{if(i&&i.length>0){if("incremental"==t.config.tracking_mode){let s=0;"point_minute"==t.config.time_deduction.option?s=t.config.time_deduction.value:"perc_minute"==t.config.time_deduction.option&&(s=t.config.time_deduction.value*n/100),await e.collection("players").updateMany({steamid64:{$nin:i.map((e=>e.steamID))},seeding_points:{$gt:s}},{$inc:{seeding_points:-s}})}if(i.length<=o.seeding_player_threshold)for(let s of i){if(!r[s.sqJsConnectionIndex])continue;const i=await e.collection("players").findOne({steamid64:s.steamID});e.collection("players").findOneAndUpdate({steamid64:s.steamID},{$set:{steamid64:s.steamID,username:s.name},$inc:{seeding_points:1}},{upsert:!0,returnDocument:"after"},(async(r,a)=>{if(r)W(null,r);else if("true"==o.reward_enabled){const r=Math.min(Math.floor(10*i?.seeding_points/n),10)||0,l=Math.min(Math.floor(10*a.value?.seeding_points/n),10)||0,c=10*l;if(l>0&&l>r)if(c<100){subcomponent_data.squadjs[s.sqJsConnectionIndex].socket.emit("rcon.warn",s.steamID,`Seeding Reward: \n\n${c}% completed`,(e=>{}));const e={embeds:[{color:v.resolveColor(E.app_personalization.accent_color),title:`${s.name}`,url:G(s.steamID),fields:[{name:"Score",value:c+"%",inline:!0},{name:"SteamID",value:v.hyperlink(s.steamID,G(s.steamID)),inline:!0},{name:"Discord User",value:a.value.discord_user_id?v.userMention(a.value.discord_user_id):"Not Linked",inline:!1}],footer:{text:new Array(10).fill("◼",0,l).fill("◻",l,10).join("")+` ${c}%`,icon_url:E.app_personalization.favicon||E.app_personalization.logo_url},thumbnail:{url:E.app_personalization.logo_url},timestamp:(new Date).toISOString()}],ephemeral:!1};U.channels.cache.get(o.discord_seeding_score_channel)?.send(e)}else if(100==c){const n=await e.collection("groups").findOne({_id:d(t.config.reward_group_id)});let i=`Seeding Reward Completed!\n\nYou have received: ${n.group_name}\n`;if("fixed_reset"==t.config.tracking_mode?i+=`Active until: ${new Date(t.config.next_reset).toLocaleDateString()}`:"incremental"==t.config.tracking_mode&&(i+="Don't drop below 100% to keep your reward!"),subcomponent_data.squadjs[s.sqJsConnectionIndex].socket.emit("rcon.warn",s.steamID,i,(e=>{})),subcomponent_status.discord_bot){const e=[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle(`${s.name} received the Seeding Reward!`).setURL(G(s.steamID)).addFields({name:"Username",value:s.name,inline:!0},{name:"SteamID",value:v.hyperlink(s.steamID,"https://steamcommunity.com/profiles/"+s.steamID),inline:!0},{name:"Discord User",value:a.value.discord_user_id?v.userMention(a.value.discord_user_id):"Not Linked",inline:!1},{name:"Reward Group",value:n.group_name,inline:!0}).setThumbnail(E.app_personalization.logo_url).setFooter({text:new Array(10).fill("◼",0,10).join("")+" 100%",iconURL:E.app_personalization.favicon||E.app_personalization.logo_url}).setTimestamp(new Date)];U.channels.cache.get(o.discord_seeding_reward_channel)?.send({embeds:e})}}}}))}}}))}t(),setInterval(t,60*e*1e3)}(),I){const t=__dirname+"/ALTERNATIVE PORTS.txt";s.removeSync(t);const i=["certificates/certificate.crt","certificates/fullchain.pem","certificates/default.crt"];let a=K(["certificates/certificate.key","certificates/privkey.pem","certificates/default.key"]),l=K(i);function e(e,o){if(e!=o){const n="!!! WARNING !!! Port "+e+" is not available! Closest free port found: "+o+"\n";console.log(n),s.writeFileSync(t,n,{flag:"a+"})}}Z(E.web_server.http_port,(t=>{Z(E.web_server.https_port,(n=>{if(t?R.http=r.listen(t,E.web_server.bind_ip,(function(){var s=R.http.address().address;console.log("HTTP server listening at http://%s:%s",s,t),R.configs.http.port=t,e(E.web_server.http_port,t)})):console.error("Couldn't start HTTP server"),a&&l&&("true"!==process.env.HTTPS_SERVER_DISABLED&&"1"!==process.env.HTTPS_SERVER_DISABLED||!process.env.HTTPS_SERVER_DISABLED)){console.log("Using Certificate:",l,a);const t={key:s.readFileSync(a),cert:s.readFileSync(l)};R.https=o.createServer(t,r),n?(r.set("forceSSLOptions",{httpsPort:n}),R.configs.https.port=n,R.https.listen(n),console.log("HTTPS server listening at https://%s:%s",E.web_server.bind_ip,n),e(E.web_server.https_port,n)):console.error("Couldn't start HTTPS server"),async function(){}()}}))})),setInterval(n,6e4)}}))})),r.use(m()),r.set("etag",!1),r.use("/",p.json()),r.use("/",p.urlencoded({extended:!0})),r.use(_()),r.use((function(e,s,t){if(!E.web_server.force_https)return t();y(e,s,t)})),r.use("/",(function(e,s,t=null){const o=e.cookies;null!=o.stok&&""!=o.stok?z((n=>{n.collection("sessions").findOne({token:o.stok},{projection:{_id:0}},((o,i)=>{o?s.sendStatus(500):null!=i&&i.session_expiration>new Date?(e.userSession=i,n.collection("users").findOne({_id:i.id_user},{projection:{_id:0}},((o,n)=>{null!=n?(e.userSession={...e.userSession,...n},t&&t()):s.send({status:"login_required"}).status(401)}))):t&&t()}))})):t()})),r.use((function(e,s,t){const o=e.get("host");j[o]?j[o]++:j[o]=1;t()})),r.post("/api/changepassword",((e,s,t)=>{const o=e.body;z((t=>{const n=u.createHash("sha512").update(o.new_password).digest("hex"),i=u.createHash("sha512").update(o.old_password).digest("hex");t.collection("users").updateOne({_id:e.userSession.id_user,password:i},{$set:{password:n}},((e,t)=>{e?W(500,e):s.send(t)}))}))})),r.post("/api/login",((e,s,t)=>{const o=e.body;z((e=>{let t=u.createHash("sha512").update(o.password).digest("hex");e.collection("users").findOne({$or:[{username_lower:o.username.toLowerCase()},{username:o.username}],password:t},((e,t)=>{if(e)s.sendStatus(500),console.error(e);else if(null==t)s.sendStatus(401);else{const e=60*E.web_server.session_duration_hours*60*1e3;let o,n={login_date:new Date,session_expiration:new Date(Date.now()+e),id_user:t._id};do{o=!1,n.token=V(128),z((e=>{e.collection("sessions").findOne({token:n.token},((t,i)=>{t?(s.sendStatus(500),console.error(t)):null==i?e.collection("sessions").insertOne(n,((e,t)=>{e?(s.sendStatus(500),console.error(e)):(s.cookie("stok",n.token,{expires:n.session_expiration}),s.cookie("uid",n.id_user,{expires:n.session_expiration}),s.send({status:"login_ok",userDt:n}))})):o=!0}))}))}while(o)}}))}))})),r.post("/api/signup",((e,s,t)=>{const o=e.body;let n,i={username:o.username,username_lower:o.username.toLowerCase(),password:u.createHash("sha512").update(o.password).digest("hex"),access_level:subcomponent_data.database.root_user_registered?100:0,clan_code:o.clan_code,registration_date:new Date,discord_username:o.discord_username};0!=i.access_level||subcomponent_data.database.root_user_registered||(subcomponent_data.database.root_user_registered=!0);const r=60*E.web_server.session_duration_hours*60*1e3;let a={...i};a.login_date=new Date,a.session_expiration=new Date(Date.now()+r),z((e=>{e.collection("users").findOne({username_lower:o.username.toLowerCase()},((t,o)=>{t?(s.sendStatus(500),console.error(t)):null==o?e.collection("users").insertOne(i,((e,t)=>{e?(s.sendStatus(500),console.error(e)):(console.log("\n\n\n\n\ninserted id=>",t.insertedId,"=>",i,"\n\n\n\n\n\n"),n=!1,a.token=V(128),s.redirect(307,"/api/login"))})):s.status(401).send({message:"Username already exists",field:"username"})}))}))})),r.use("/",(function(e,s,t){if(!R.logging.requests)return t();const o=Object.keys(e.query).length>0,n=o?e.query:e.body,i=l(e);console.log("\nREQ: "+i+"\nSESSION: ",e.userSession,"\nPARM "+(o?"GET":"POST")+": ",n),t()})),r.get("/api/getVersion",((s,t,o)=>{t.send(e)})),r.use("/",i.static(__dirname+"/dist")),r.get("/api/getAppPersonalization",(function(e,s,t){s.send(E.app_personalization)})),r.get("/api/getTabs",((e,s,t)=>{const o=[subcomponent_data.database.root_user_registered?{}:{name:"Root User Registration",order:0,type:"tab",max_access_level:null},{name:"Clans",order:5,type:"tab",max_access_level:5},{name:"Whitelist",order:10,type:"tab",max_access_level:100},{name:"Groups",order:15,type:"tab",max_access_level:100},{name:"Approvals",order:25,type:"tab",max_access_level:30},{name:"Seeding",order:27,type:"tab",max_access_level:30},{name:"Users and Roles",order:30,type:"tab",max_access_level:5},{name:"Configuration",order:40,type:"tab",max_access_level:5}];let n;n=subcomponent_data.updater.updating?[{name:"Updating",order:0,type:"tab",max_access_level:null}]:o.filter((s=>null==s.max_access_level&&!e.userSession||e.userSession&&s.max_access_level&&e.userSession.access_level<=s.max_access_level)),s.send({tabs:n})})),r.get("/api/getContextMenu",((e,s,t)=>{let o=[{name:"",action:"",url:"",method:"",order:0}];w(e)&&(o=o.concat([])),s.send(o)})),r.get("/:basePath/:clan_code?",((e,s,t)=>{n(null,null,(()=>{z((o=>{o.collection("lists").findOne({output_path:e.params.basePath},((n,i)=>{if(n)W(s,n);else if(null!=i){s.type("text/plain");let t=e.params.clan_code?{clan_code:e.params.clan_code}:{},n="",r=[],a=[],l=[],c=[],d=[];const u=null!=e.query.usernamesOnly,p=V(6);o.collection("clans").find(t).toArray(((t,_)=>{for(let e of _)a[e._id.toString()]=e,l.push(e._id);o.collection("groups").find().sort({group_name:1}).toArray(((t,_)=>{for(let e of _)r[e._id.toString()]=e;r[p]={group_name:p,group_permissions:["reserve"]};const m=[{$match:{approved:!0,id_clan:{$in:l},id_list:i._id}},{$lookup:{from:"players",let:{steamid64:"$steamid64"},pipeline:[{$match:{$expr:{$eq:["$steamid64","$$steamid64"]},discord_user_id:{$exists:!0}}}],as:"serverPlayerData"}},{$sort:{id_clan:1,id_group:1,username_l:1}}];o.collection("whitelists").aggregate(m).toArray(((t,i)=>{if(t)W(s,t);else if(null!=i){for(let _ of i){let m=(_.serverPlayerData&&_.serverPlayerData[0]?_.serverPlayerData[0].discord_username:null)||_.discord_username||"";""==m||m.startsWith("@")||(m="@"+m),d.push({username:_.username,steamid64:_.steamid64,groupId:_.id_group,clanTag:a[_.id_clan].tag,discordUsername:m})}if(e.params.clan_code)l();else{const g=[{$match:{steamid64:{$ne:null},discord_roles_ids:{$exists:!0}}},{$lookup:{from:"lists",let:{pl_roles:"$discord_roles_ids"},pipeline:[{$match:{output_path:e.params.basePath}},{$addFields:{int_r:{$setIntersection:["$discord_roles","$$pl_roles"]}}},{$match:{int_r:{$ne:[]}}}],as:"lists"}},{$lookup:{from:"groups",let:{pl_roles:"$discord_roles_ids"},pipeline:[{$addFields:{int_r:{$setIntersection:["$discord_roles","$$pl_roles"]}}},{$match:{int_r:{$ne:[]}}}],as:"groups"}},{$project:{discord_roles_ids:0,"groups.discord_roles":0,"groups.intersection_roles":0,"groups.int_r":0,"groups.require_appr":0}},{$match:{lists:{$ne:[]}}}];o.collection("players").aggregate(g).toArray(((e,t)=>{if(e)s.sendStatus(500),console.error(e);else for(let e of t)if(u)n+=e.username+"\n";else for(let s of e.groups)d.push({username:e.username,steamid64:e.steamid64,groupId:s._id,clanTag:"Discord Role",discordUsername:null!=e.discord_username?e.discord_username:""});o.collection("configs").findOne({category:"seeding_tracker"},((e,t)=>{if(t&&t.config.reward_enabled&&"true"==t.config.reward_enabled){const e=t.config,n=e.reward_needed_time.value*e.reward_needed_time.option/1e3/60;o.collection("players").find({steamid64:{$ne:null},seeding_points:{$gte:n}}).toArray(((t,o)=>{t&&W(s,t);const n=o.map((s=>({username:s.username,steamid64:s.steamid64,groupId:e.reward_group_id,clanTag:"Seeder",discordUsername:null!=s.discord_username?s.discord_username:""})));d.push(...n),l()}))}else l()}))}))}function l(){!E.other.whitelist_developers||u||e.params.clan_code||d.push({username:"JetDave",steamid64:"76561198419229279",groupId:p,clanTag:"SQUAD Whitelister Developer",discordUsername:"@=BIA=JetDave#1001"},{username:"Radix",steamid64:"76561198301315305",groupId:p,clanTag:"SQUAD Whitelister Developer",discordUsername:""},{username:"Phil_Caine",steamid64:"76561198272599483",groupId:p,clanTag:"SQUAD Whitelister Developer",discordUsername:""}),function(){for(let e of d)r[e.groupId]?(e.groupId=`${e.groupId}`,n+=`Admin=${e.steamid64}:${r[e.groupId].group_name} // [${e.clanTag}] ${e.username} ${e.discordUsername}\n`,c.includes(e.groupId)||c.push(e.groupId)):(console.log("Could not find group with id",e.groupId,r[e.groupId]),o.collection("whitelists").deleteMany({id_group:e.groupId}));n="\n"+n;for(let e of c){const s=r[e];n=`Group=${s.group_name}:${s.group_permissions.join(",")}\n`+n}}(),s.send(n)}}else s.send("")}))}))}))}else t()}))}))}))})),r.get("/dsTest",((e,s,t)=>{s.type("text/plain");const o=[{$match:{steamid64:{$ne:null},discord_roles_ids:{$exists:!0}}},{$lookup:{from:"groups",let:{pl_roles:"$discord_roles_ids"},pipeline:[{$addFields:{int_r:{$setIntersection:["$discord_roles","$$pl_roles"]}}},{$match:{int_r:{$ne:[]}}}],as:"groups"}},{$project:{discord_roles_ids:0,"groups.discord_roles":0,"groups.intersection_roles":0,"groups.int_r":0,"groups.require_appr":0}}];z((e=>{e.collection("players").aggregate(o).toArray(((e,t)=>{if(e)s.sendStatus(500),console.error(e);else{let e="";for(let s of t)for(let t of s.groups)e+="Admin="+s.steamid64+":"+t.group_name+" // [Discord Role] "+s.username+(null!=s.discord_username?" "+s.discord_username:"")+"\n";s.send(e)}}))}))})),r.get("/api/checkSession",((e,s,t)=>{e.userSession?s.send({status:"session_valid",userSession:e.userSession}):s.send({status:"login_required"}).status(401)})),r.use("/",(function(e,s,t=null){Object.keys(e.query).length>0?e.query:e.body,l(e);e.userSession?t():s.send({status:"login_required"}).status(401)})),r.use("/api/restart",((e,s,t)=>{s.send({status:"restarting"}),restartProcess(0,0,h)})),r.use("/api/logout",((e,s,t)=>{s.clearCookie("stok"),s.clearCookie("uid"),z((t=>{t.collection("sessions").deleteOne({token:e.userSession.token},((e,t)=>{e?W(s,e):s.send({status:"logout_ok"})}))}))})),r.use("/api/users/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/users/read/getAll",((e,s,t)=>{e.query;z((t=>{const o=[{$match:{...e.userSession.access_level>=100?{clan_code:e.userSession.clan_code}:{}}},{$lookup:{from:"clans",localField:"clan_code",foreignField:"clan_code",as:"clan_data"}},{$sort:{username:1}}];t.collection("users").aggregate(o).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.post("/api/users/write/remove",((e,s,t)=>{const o=e.body,n=h.demo?{username:{$ne:"demoadmin"}}:{};z((e=>{e.collection("users").deleteOne({_id:d(o._id),...n,access_level:{$gt:1}},((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.post("/api/users/write/updateAccessLevel",((e,s,t)=>{const o=e.body,n=h.demo?{username:{$ne:"demoadmin"}}:{};console.log("\nFilter\n",n),e.userSession.access_level<=parseInt(o.upd)&&z((e=>{e.collection("users").updateOne({_id:d(o._id),...n,access_level:{$gt:1}},{$set:{access_level:parseInt(o.upd)}},((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.use("/api/roles/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/roles/read/getAll",((e,s,t)=>{s.send({0:{name:"Root",access_level:0},5:{name:"Admin",access_level:5},30:{name:"Approver",access_level:30},100:{name:"User",access_level:100}})})),r.use("/api/api_keys/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/api_keys/read/getAll",((e,s,t)=>{s.send([])})),r.post("/api/api_keys/write/create",((e,s,t)=>null)),r.get("/api/subcomponent/read/:subComp/status",(async(e,s,t)=>{s.send(subcomponent_status[e.params.subComp])})),r.use("/api/config/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/config/read/getFull",(async(e,s,t)=>{let o={...E};if(h.demo&&e.userSession.access_level>0&&(o.discord_bot.token="hidden"),E.app_personalization.favicon=E.app_personalization.favicon||E.app_personalization.logo_url,process.env.HIDDEN_CONFIG_TABS)for(let e of process.env.HIDDEN_CONFIG_TABS.split(";"))try{delete o[e]}catch(e){}s.send(o)})),r.use("/api/config/write",((e,s,t)=>{h.demo&&0!=e.userSession.access_level?s.sendStatus(403):t()})),r.post("/api/config/write/update",(async(e,t,o)=>{const n=e.body;let i={};process.env.HIDDEN_CONFIG_TABS&&process.env.HIDDEN_CONFIG_TABS.split(";").includes(n.category)?i.status="config_rejected":(E[n.category]=n.config,s.writeFileSync(O+".bak",s.readFileSync(O)),s.writeFileSync(O,JSON.stringify(E,null,"\t")),i.status="config_updated"),i.action="reload",t.send(i),restartProcess(0,0,h)})),r.use("/api/dbconfig/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/dbconfig/read/getFull",(async(e,s,t)=>{e.body;z((e=>{e.collection("configs").find({config:{$exists:!0,$ne:null},category:{$exists:!0,$ne:null}}).toArray(((e,t)=>{e?W(s,e):t&&s.send(t)}))}))})),r.get("/api/dbconfig/read/:category",(async(e,s,t)=>{e.body;z((t=>{t.collection("configs").findOne({category:e.params.category},((e,t)=>{e?W(s,e):t&&s.send(t.config)}))}))})),r.use("/api/dbconfig/write",((e,s,t)=>{h.demo&&0!=e.userSession.access_level?s.sendStatus(403):t()})),r.post("/api/dbconfig/write/update",(async(e,s,t)=>{const o=e.body;z((e=>{e.collection("configs").updateOne({category:o.category},{$set:{config:o.config}},{upsert:!0},((e,t)=>{e?W(s,e):s.send({status:"config_updated",action:"reload"})}))}))})),r.use("/api/lists/read/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=100&&t()})),r.get("/api/lists/read/getAll",((e,s,t)=>{z((t=>{let o=e.userSession.access_level<100?{}:{hidden_managers:!1};t.collection("lists").find(o).toArray(((e,t)=>{e?W(s,e):s.send(t)}))}))})),r.use("/api/lists/write/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=10?t():s.sendStatus(401)})),r.use("/api/lists/write/checkPerm",(async(e,s,t)=>{s.send({status:"permission_granted"})})),r.post("/api/lists/write/addNewList",((e,s,t)=>{const o=e.body;z((e=>{const t={title:o.title,output_path:o.output_path,hidden_managers:o.hidden_managers,require_appr:o.require_appr,discord_roles:o.discord_roles};e.collection("lists").insertOne(t,((e,t)=>{e?W(s,e):s.send({status:"inserted_new_list",...t})}))}))})),r.post("/api/lists/write/deleteList",((e,s,t)=>{const o=e.body;z((e=>{e.collection("whitelists").deleteMany({id_list:d(o.sel_list_id)},((t,n)=>{t?W(s,t):e.collection("lists").deleteMany({_id:d(o.sel_list_id)},((e,t)=>{e?W(s,e):s.send({status:"removed_list",...t})}))}))}))})),r.post("/api/lists/write/editList",((e,s,t)=>{const o=e.body;z((e=>{const t={title:o.title,output_path:o.output_path,hidden_managers:o.hidden_managers,require_appr:o.require_appr,discord_roles:o.discord_roles};e.collection("lists").updateOne({_id:d(o.sel_list_id)},{$set:t},((e,t)=>{e?W(s,e):s.send({status:"edited_list",...t})}))}))})),r.use("/api/whitelist/read/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=100&&t()})),r.get("/api/whitelist/read/getAllClans",((e,s,t)=>{e.query;z((t=>{const o=[{$match:e.userSession.access_level>=100?{clan_code:e.userSession.clan_code}:{}},{$lookup:{from:"whitelists",localField:"_id",foreignField:"id_clan",as:"clan_whitelist"}},{$addFields:{player_count:{$size:"$clan_whitelist"}}},{$project:{clan_whitelist:0}},{$sort:{full_name:1}}];t.collection("clans").aggregate(o).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.get("/api/whitelist/read/getAll",((e,s,t)=>{const o=e.query;z((e=>{let t=o.sel_clan_id?{id_clan:d(o.sel_clan_id)}:{};const n=[{$match:{id_list:d(o.sel_list_id),...t}},{$lookup:{from:"groups",localField:"id_group",foreignField:"_id",as:"group_full_data"}},{$lookup:{from:"users",localField:"inserted_by",foreignField:"_id",as:"inserted_by"}},{$lookup:{from:"players",localField:"steamid64",foreignField:"steamid64",as:"serverData"}},{$sort:{id_clan:1,approved:-1,id_group:1,username:1}}];e.collection("whitelists").aggregate(n).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.get("/api/whitelist/read/getPendingApprovalClans",((e,s,t)=>{e.query;z((t=>{const o=[{$match:e.userSession.access_level>=100?{clan_code:e.userSession.clan_code}:{}},{$lookup:{from:"whitelists",let:{id_clan:"$_id"},pipeline:[{$match:{$expr:{$eq:["$id_clan","$$id_clan"]},approved:!1}}],as:"whitelists"}},{$sort:{whitelists_data:-1}},{$match:{whitelists:{$exists:!0,$ne:[]}}}];t.collection("clans").aggregate(o).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):(console.log(t),s.send(t))}))}))})),r.get("/api/whitelist/read/getPendingApproval",((e,s,t)=>{const o=e.query;z((e=>{const t=[{$lookup:{from:"whitelists",let:{id_list:"$_id"},pipeline:[{$match:{$expr:{$eq:["$id_list","$$id_list"]},approved:!1,id_clan:d(o.sel_clan_id)}},{$lookup:{from:"groups",localField:"id_group",foreignField:"_id",as:"group_full_data"}},{$lookup:{from:"users",localField:"inserted_by",foreignField:"_id",as:"inserted_by"}},{$sort:{id_clan:1,approved:-1,id_group:1,username:1}}],as:"wl_data"}},{$match:{wl_data:{$exists:!0,$ne:[]}}}];e.collection("lists").aggregate(t).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):(s.send(t),console.log("\n\n\n",t,"\n\n\n"))}))}))})),r.use("/api/whitelist/write/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<30?t():z(((o,n)=>{let i=e.userSession.access_level>=100?{clan_code:e.userSession.clan_code,admins:e.userSession.id_user.toString()}:{};o.collection("clans").findOne(i,((o,n)=>{o?W(s,o):null!=n?(console.log("authorizing",e.userSession.username,"=>",n),t()):(console.log("blocking",n),s.sendStatus(401))}))}))})),r.use("/api/whitelist/write/checkPerm",(async(e,s,t)=>{s.send({status:"permission_granted"})})),r.post("/api/whitelist/write/addPlayer",((e,s,t)=>{const o=e.body;z((t=>{const n=[{$match:e.userSession.access_level>=100?{clan_code:e.userSession.clan_code,admins:e.userSession.id_user.toString()}:{_id:d(o.sel_clan_id)}},{$lookup:{from:"whitelists",localField:"_id",foreignField:"id_clan",as:"clan_whitelist"}},{$addFields:{player_count:{$size:"$clan_whitelist"}}},{$project:{clan_whitelist:0}}];t.collection("clans").aggregate(n).toArray(((n,i)=>{console.log("====>",i);let r=i[0];if(n)console.log("error",n);else if(null!=r)if(""==r.player_limit||r.player_count{o?W(s,o):e.userSession.access_level<100||!a.hidden_managers?t.collection("groups").findOne(n.id_group,((o,l)=>{o?console.log("error",o):null!=l?(n.approved=!(l.require_appr||r.confirmation_ovrd||a.require_appr)||e.userSession.access_level<=30,t.collection("whitelists").insertOne(n,((t,o)=>{if(t)console.log("ERR",t);else if(s.send({status:"inserted_new_player",player:{...n,inserted_by:[{username:e.userSession.username}]},...o}),subcomponent_status.discord_bot){let s,t=[];n.approved?s={}:(s=(new v.ActionRowBuilder).addComponents((new v.ButtonBuilder).setCustomId("approval:approve:"+n._id).setLabel("Approve").setStyle(v.ButtonStyle.Success),(new v.ButtonBuilder).setCustomId("approval:reject:"+n._id).setLabel("Reject").setStyle(v.ButtonStyle.Danger)),t.push(s));const o=[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Whitelist Update").addFields({name:"Username",value:n.username,inline:!0},{name:"SteamID",value:v.hyperlink(n.steamid64,"https://steamcommunity.com/profiles/"+n.steamid64),inline:!0},{name:"Clan",value:i[0].full_name},{name:"Group",value:l.group_name,inline:!0})];n.expiration&&o[0].addFields({name:"Expiration",value:v.time(n.expiration,"R"),inline:!0}),o[0].addFields({name:"Manager",value:e.userSession.username},{name:"List",value:a.title},{name:"Approval",value:n.approved?":white_check_mark: Approved":":hourglass: Pending",inline:!0}),U.channels.cache.get(E.discord_bot.whitelist_updates_channel_id)?.send({embeds:o,components:t})}}))):s.send({status:"not_inserted",reason:"could find corresponding id"})})):s.sendStatus(402)}))}else s.send({status:"not_inserted",reason:"Player limit reached"});else s.sendStatus(401)}))}))})),r.post("/api/whitelist/write/removePlayer",((e,s,t)=>{const o=e.body;z((e=>{e.collection("whitelists").deleteOne({_id:d(o._id)},((e,t)=>{e?W(s,e):s.send({status:"removing_ok",...t})}))}))})),r.post("/api/whitelist/write/clearList",((e,s,t)=>{const o=e.body;z((e=>{e.collection("whitelists").deleteMany({id_clan:d(o.sel_clan_id),id_list:d(o.sel_list_id)},((e,t)=>{e?W(s,e):s.send({status:"clearing_ok",...t})}))}))})),r.use("/api/seeding/read/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<30?t():s.sendStatus(401)})),r.get("/api/seeding/read/getPlayers",((e,s,t)=>{z((e=>{e.collection("players").find({seeding_points:{$gte:1},username:{$ne:null}}).toArray(((e,t)=>{e?W(s,e):s.send(t)}))}))})),r.use("/api/players/read/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<30?t():s.sendStatus(401)})),r.get("/api/players/read/from/steamId/:id",((e,s,t)=>{z((t=>{t.collection("players").findOne({steamid64:e.params.id},((e,t)=>{e?W(s,e):s.send(t)}))}))})),r.get("/api/players/read/from/discordUserId/:id",((e,s,t)=>{z((t=>{t.collection("players").findOne({discord_user_id:e.params.id},((e,t)=>{e?W(s,e):s.send(t)}))}))})),r.use("/api/approval/write/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<30?t():s.sendStatus(401)})),r.use("/api/approval/write/setApprovedStatus",((e,s,t)=>{Y(e.body,s)})),r.use("/api/gameGroups/write/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<10?t():s.sendStatus(401)})),r.use("/api/gameGroups/write/checkPerm",(async(e,s,t)=>{s.send({status:"permission_granted"})})),r.post("/api/gameGroups/write/newGroup",((e,s,t)=>{const o=e.body;z((e=>{e.collection("groups").insertOne(o,((e,t)=>{e?W(s,e):s.send({status:"group_created",data:o,dbRes:{...t}})}))}))})),r.post("/api/gameGroups/write/editGroup",((e,s,t)=>{let o={...e.body};delete o._id,z((t=>{t.collection("groups").updateOne({_id:d(e.body._id)},{$set:o},((o,n)=>{o?W(s,o):t.collection("groups").findOne({_id:d(e.body._id)},((e,t)=>{s.send({status:"edit_ok",...t})}))}))}))})),r.post("/api/gameGroups/write/remove",((e,s,t)=>{z((t=>{t.collection("groups").deleteOne({_id:d(e.body._id)},((e,t)=>{e?W(s,e):s.send({status:"removing_ok",...t})}))}))})),r.get("/api/gameGroups/read/getAllGroups",((e,s,t)=>{z((t=>{let o={};function n(){t.collection("groups").find(o).sort({group_name:1}).toArray(((e,t)=>{e?W(s,e):s.send(t)}))}e.userSession&&e.userSession.access_level>=100?t.collection("clans").findOne({clan_code:e.userSession.clan_code},((e,t)=>{if(e)W(s,e);else{let e=[];if(t)for(let s of t.available_groups)e.push(d(s));o={_id:{$in:e}},n()}})):n()}))})),r.use("/api/discord/*",((...e)=>{Q(30,...e)})),r.use("/api/discord/write",((...e)=>{Q(10,...e)})),r.get("/api/discord/read/getStatus",((e,s,t)=>{s.send(subcomponent_status.discord_bot)})),r.get("/api/discord/read/getRoles",((e,s,t)=>{e.query;if(subcomponent_status.discord_bot){const e=U.guilds.cache.find((e=>e.id==E.discord_bot.server_id));let t=[];for(let s of e.roles.cache)"@everyone"!==s[1].name.toLowerCase()&&t.push({id:s[1].id,name:s[1].name});s.send(t)}else s.sendStatus(404)})),r.get("/api/discord/read/getServers",((e,s,t)=>{e.query;if(subcomponent_status.discord_bot){let e=[];for(let s of U.guilds.cache)e.push({id:s[1].id,name:s[1].name});s.send(e)}else s.sendStatus(404)})),r.get("/api/discord/read/getChannels",(async(e,s,t)=>{e.query;if(subcomponent_status.discord_bot){s.send((await U.guilds.fetch(E.discord_bot.server_id)).channels.cache.sort(((e,s)=>e.rawPosition-s.rawPosition)).filter((e=>4!=e.type)))}else s.sendStatus(404)})),r.get("/api/discord/read/inviteLink",(async(e,s,t)=>{s.send({url:subcomponent_data.discord_bot.invite_link})})),r.use("/api/clans*",((e,s,t)=>{e.userSession&&e.userSession.access_level<10&&t()})),r.get("/api/clans/getAllClans",((e,s,t)=>{z((e=>{e.collection("clans").find().sort({full_name:1,tag:1}).toArray(((e,t)=>{s.send(t)}))}))})),r.post("/api/clans/removeClan",((e,s,t)=>{z((t=>{t.collection("clans").deleteOne({_id:d(e.body._id)},((e,t)=>{e?W(s,e):s.send({status:"removing_ok",...t})}))}))})),r.post("/api/clans/editClan",((e,s,t)=>{let o={...e.body};delete o._id,z((t=>{t.collection("clans").updateOne({_id:d(e.body._id)},{$set:o},((o,n)=>{o?W(s,o):t.collection("clans").findOne({_id:d(e.body._id)},((e,t)=>{s.send({status:"edit_ok",...t})}))}))}))})),r.get("/api/clans/getClanUsers",((e,s,t)=>{const o=e.query;z((e=>{e.collection("users").find({clan_code:o.clan_code}).toArray(((e,t)=>{s.send(t)}))}))})),r.get("/api/clans/getClanAdmins",((e,s,t)=>{const o=e.query;z((e=>{e.collection("clans").findOne({_id:d(o._id)},{projection:{admins:1}},((e,t)=>{if(e)W(s,e);else{let e=t.admins?t.admins:[];s.send(e)}}))}))})),r.post("/api/clans/editClanAdmins",((e,s,t)=>{const o=e.body;z((t=>{t.collection("clans").updateOne({_id:d(e.body._id)},{$set:{admins:o.clan_admins}},((e,t)=>{s.send({status:"edit_ok",...t})}))}))})),r.post("/api/clans/newClan",((e,s,t)=>{const o=e.body;let n;do{let e=V(8);n=!1,z((t=>{t.collection("clans").findOne({full_name_lower:o.full_name.toLowerCase()},((i,r)=>{let a={...o,clan_code:e};a.full_name_lower=o.full_name.toLowerCase(),i?(s.sendStatus(500),console.error(i)):null==r?t.collection("clans").findOne({clan_code:e},((e,o)=>{e?(s.sendStatus(500),console.error(e)):null==o?t.collection("clans").insertOne(a,((e,t)=>{e?(s.sendStatus(500),console.error(e)):(s.send({status:"clan_created",clan_data:a}),console.log("New clan created:",a))})):n=!0})):(s.send({status:"clan_already_registered"}).status(409),console.log("Trying to register an already registered clan:",a))}))}))}while(n)})),r.use("/admin*",c),r.use("/admin",(function(e,s,t){i.static("admin")(e,s,t)})),r.use("/api/admin*",c),r.get("/api/admin",((e,s,t)=>{s.send({status:"Ok"})})),r.get("/api/admin/getConfig",((e,s,t)=>{s.send(E)})),r.get("/api/admin/checkInstallUpdate",((e,s,t)=>{s.send({status:"Ok"}),g(!0)})),r.get("/api/admin/restartApplication",((e,s,t)=>{s.send({status:"Ok"}),restartProcess(e.query.delay?e.query.delay:0,0,h)})),r.use(((e,s,t)=>{s.redirect(404,"/")}))}function U(e=null){if(console.log("Discord BOT"),E.discord_bot&&""!=E.discord_bot.token){const o=new v.Client({intents:[v.GatewayIntentBits.Guilds,v.GatewayIntentBits.GuildMessages,v.GatewayIntentBits.GuildMembers]});o.login(E.discord_bot.token);const n=setTimeout((()=>{console.error(" > Connection timed out. Check your discord_bot configuration."),console.log(" > Proceding without discord bot."),e()}),1e4),i=[{name:"ping",description:"Replies with Pong!"},{name:"listclans",description:"Gives a full list of clans with corresponding info"},{name:"topseed",description:"Gives a list of seeders"},{name:"profile",description:"Links the Discord profile to the Steam profile",options:[{name:"user",description:"Leave empty to get info of yourself, or fill to get info of a specific user",type:6,required:!1}]}];async function s(e){try{const s=await o.guilds.cache.get(E.discord_bot.server_id).members.cache.find((s=>s.id==e));if(s){const t=s.user,o=s._roles;try{z((s=>{s.collection("players").updateOne({discord_user_id:e},{$set:{discord_user_id:e,discord_username:t.username+"#"+t.discriminator,discord_roles_ids:o}},{upsert:!0})}))}catch(e){console.error(e)}}}catch(e){console.error(e)}}async function t(e,s,t=0){e.id;z((async e=>{let o=await e.collection("players").find({seeding_points:{$gte:1}}).skip(10*t).limit(11).sort({seeding_points:-1}).toArray();const n=(await e.collection("configs").findOne({category:"seeding_tracker"})).config,i=n.reward_needed_time.value*(n.reward_needed_time.option/1e3/60),r=o.splice(0,10).map(((e,s)=>[`**${10*t+s+1})**`,`${v.hyperlink(e.username,G(e.steamid64))}`,e.discord_user_id?v.userMention(e.discord_user_id):null,`*${Math.floor(100*(e.seeding_points||0)/i)}%*`].filter((e=>null!=e)).join(" "))),a={embeds:[{color:v.resolveColor(E.app_personalization.accent_color),title:"Top 10 Seeders",description:r.join("\n")}],components:[(new v.ActionRowBuilder).addComponents((new v.ButtonBuilder).setCustomId("topseed:page:"+(+t-1)).setLabel("⮜").setStyle(v.ButtonStyle.Success).setDisabled(t-1<0),(new v.ButtonBuilder).setCustomId("topseed:page:"+(+t+1)).setLabel("⮞").setStyle(v.ButtonStyle.Success).setDisabled(0==o.length))],ephemeral:!1};s.isButton()?(await s.deferUpdate(),await s.message.edit(a)):await s.reply(a)}))}o.on("ready",(async()=>{clearTimeout(n),U=new Proxy(o,{});subcomponent_data.discord_bot.invite_link=`https://discord.com/api/oauth2/authorize?client_id=${o.user.id}&permissions=268564544&scope=bot%20applications.commands`,console.log(" > Logged-in!"),console.log(` > Tag: ${o.user.tag}`),console.log(` > ID: ${o.user.id}`),console.log(` > Invite: ${subcomponent_data.discord_bot.invite_link}`),e();new v.REST({version:"10"}).setToken(E.discord_bot.token).put(v.Routes.applicationCommands(o.user.id),{body:i});let t=[];if(o.guilds)for(let e of o.guilds.cache)t.push({id:e[1].id,name:e[1].name});function r(){z((e=>{e.collection("players").find({discord_user_id:{$exists:!0}}).toArray(((e,t)=>{for(let e of t)s(e.discord_user_id)}))}))}""==E.discord_bot.server_id&&t.length>0&&(E.discord_bot.server_id=t[0].id),""!=E.discord_bot.server_id&&(subcomponent_status.discord_bot=!0,r(),setInterval(r,3e5))})),o.on("raw",(e=>{if("GUILD_MEMBER_UPDATE"===e.t){const s=e.d.user.id;let t=e.d.roles;z((o=>{o.collection("players").updateOne({discord_user_id:s},{$set:{discord_user_id:s,discord_username:e.d.user.username+"#"+e.d.user.discriminator,discord_roles_ids:t}},{upsert:!0})}))}})),o.on("interactionCreate",(async e=>{const n=e.member?e.member.user:e.user,i=`${n.id}`;if(e.isChatInputCommand()){switch(e.commandName){case"ping":await e.deferReply(),await e.followUp("Pong!");break;case"listclans":await e.deferReply(),z((s=>{s.collection("lists").find().toArray(((t,n)=>{s.collection("clans").aggregate([{$lookup:{from:"whitelists",localField:"_id",foreignField:"id_clan",as:"clan_whitelist"}},{$addFields:{uniqueSteamids:{$setUnion:"$clan_whitelist.steamid64"}}},{$addFields:{unique_players:{$size:"$uniqueSteamids"}}},{$project:{_id:0,admins:0,available_groups:0,clan_whitelist:0,uniqueSteamids:0}}]).toArray(((s,t)=>{s&&console.error(s);let r=[],a=!0;for(let s of t){let t=[];for(let e of["tag","clan_code","player_limit","unique_players"])"player_limit"==e&&""==s[e]&&(s[e]="ထ"),t.push({name:H(e.replace(/\_/g," ")),value:s[e].toString(),inline:"full_name"!=e});const l=j.sort(),c=Object.keys(l)[0];if(l[c]){let e=[];for(let t of n){const o="https://"+c+":"+R.configs.https.port+"/"+t.output_path+"/"+s.clan_code;e.push(v.hyperlink(t.title,o))}t.push({name:"Whitelist",value:e.join(" - "),inline:!1})}r.push((new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle(H(s.full_name.replace(/\_/g," "))).addFields(...t)),r.length%10==0&&(a?(a=!1,e.reply({content:v.userMention(i),embeds:r})):o.channels.cache.get(e.channelId).send({embeds:r}),r=[])}r.length>0&&o.channels.cache.get(e.channelId).followUp({embeds:r})}))}))}));break;case"profile":let s=null!=e.options.getUser("user"),r=s?e.options.getUser("user"):n;const a=!s;await e.deferReply({ephemeral:a}),z((t=>{t.collection("players").findOne({discord_user_id:s?r.id:i},(async(s,o)=>{if(s)W(null,s);else{let s=[{name:"Steam "+(o&&o.steamid64?"Username ":""),value:o&&o.steamid64?o.username:"*Not linked*",inline:!0}];if(o&&o.steamid64){s.push({name:"SteamID",value:v.hyperlink(o.steamid64,"https://steamcommunity.com/profiles/"+o.steamid64),inline:!0});const e=(await t.collection("configs").findOne({category:"seeding_tracker"})).config,n=e.reward_needed_time.value*(e.reward_needed_time.option/1e3/60),i=Math.floor(100*(o.seeding_points||0)/n);"true"==e.reward_enabled&&s.push({name:"Seeding Reward",value:`${i}%`,inline:!1});const r=await B(o.steamid64);for(let e of r.filter((e=>e.approved))){let t="Unlimited";e.expiration&&(t=`Expired ${v.time(e.expiration,"R")}`),s.push({name:e.name,value:t,inline:!0})}}let n=await e.followUp({content:v.userMention(r.id),embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setAuthor({name:r.username,iconURL:r.avatarURL()}).setTitle("Linked Profiles").addFields(...s)],components:[(new v.ActionRowBuilder).addComponents(o&&o.steamid64?(new v.ButtonBuilder).setCustomId("profilelink:steam:unlink").setLabel("Unlink Steam").setStyle(v.ButtonStyle.Danger):(new v.ButtonBuilder).setCustomId("profilelink:steam:link").setLabel("Link Steam").setStyle(v.ButtonStyle.Success))],ephemeral:a});n.interaction.ephemeral||setTimeout((async()=>{try{const e=await(n?.interaction?.webhook?.fetchMessage());e&&e.edit({components:[]})}catch(e){console.error(e)}}),3e4)}}))}));break;case"topseed":t(n,e)}s(i)}else if(e.isButton()){const s=e.customId.split(":");switch(s[0]){case"approval":const o="approve"==s[1];Y({_id:s[2],approve_update:o}),e.reply({content:"Done",ephemeral:!0}),e.message.edit({components:[]});let r=e.message.embeds[0];r.fields.filter((e=>"Approval"==e.name))[0].value=o?":white_check_mark: Approved":":x: Rejected",r.fields.push({name:(o?"Approved":"Rejected")+" by",value:v.userMention(n.id),inline:!0}),e.message.edit({embeds:[r]});break;case"profilelink":if(e.message.mentions.users.find((e=>e.id==i))||e.message.ephemeral){if("steam"===s[1])switch(s[2]){case"link":let t;do{let s=V(6);t=!1;const o=3e5,n=new Date(Date.now()+o);z((r=>{r.collection("profilesLinking").deleteOne({discordUserId:i},((a,l)=>{r.collection("profilesLinking").findOne({code:s},((a,l)=>{a?(res.sendStatus(500),console.error(a)):null==l?r.collection("profilesLinking").insertOne({source:"Discord",discordUserId:i,code:s,expiration:n},((t,a)=>{t?(res.sendStatus(500),console.error(t)):(e.reply({content:v.userMention(i),embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Link Steam Profile").setDescription("Join our Squad server and send in any chat the following code (case-sensitive)").addFields({name:"Linking Code",value:s,inline:!1},{name:"Expiration",value:v.time(n,"R"),inline:!1})],ephemeral:!0}),setInterval((async()=>{r.collection("profilesLinking").deleteOne({_id:a.insertedId})}),o))})):t=!0}))}))}))}while(t);break;case"unlink":if(s[3]){if("confirm"===s[3])try{z((s=>{s.collection("players").updateOne({discord_user_id:i},{$unset:{discord_user_id:1}},((s,t)=>{s?W(null,s):(console.log(t),1==t.modifiedCount?e.reply({content:v.userMention(i)+"\nYour Steam account has been unlinked",ephemeral:!0}):e.reply({content:v.userMention(i)+"\nYou don't have a Steam account to unlink",ephemeral:!0}))}))}))}catch(s){e.reply({content:"An error occurred and this interaction cannot be completed",ephemeral:!0}),console.error(s)}}else await e.reply({content:v.userMention(i),embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Unlink Steam").setDescription("Do you really want to unlink your steam profile?")],components:[(new v.ActionRowBuilder).addComponents((new v.ButtonBuilder).setCustomId("profilelink:steam:unlink:confirm").setLabel("Confirm").setStyle(v.ButtonStyle.Danger))],ephemeral:!0})}}else e.reply({content:v.userMention(i),embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Unauthorized").setDescription("Only the owner of the profile can use this action")],ephemeral:!0});break;case"topseed":"page"==s[1]&&t(n,e,s[2])}}else e.isModalSubmit()&&e.reply({content:"Modal received",ephemeral:!0})}))}else console.log(" > Not configured. Skipping."),e()}function M(e,s,t){return new Promise(((o,n)=>{e.emit(s,t,(e=>{e.error?n(new Error(e.error)):o(e)}))}))}async function B(e){const s=await z(),t=await s.collection("groups").find().toArray(),o=await s.collection("configs").findOne({category:"seeding_tracker"}),n=o.config,i=n.reward_needed_time.value*(n.reward_needed_time.option/1e3/60);let r;console.log("CREATING objIdRewardGroup from value:",o.config.reward_group_id);try{r=d(o.config.reward_group_id)}catch(e){r=null,console.log("FAILED TO CREATE objIdRewardGroup from value:",o.config.reward_group_id,"\n",n)}let a,l=[];r&&(a=o.config.reward_group_id?await s.collection("groups").findOne({_id:r}):null),l.push(...(await s.collection("whitelists").find({steamid64:e}).toArray()).map((e=>{const s=t.find((s=>s._id.toString()==e.id_group.toString()));let o={};return o.id=e.id_group.toString(),o.name=s.group_name,o.expiration=e.expiration,o.approved=e.approved,o.source="Whitelists",o})));const c=[{$match:{steamid64:e,discord_roles_ids:{$exists:!0}}},{$lookup:{from:"groups",let:{pl_roles:"$discord_roles_ids"},pipeline:[{$addFields:{int_r:{$setIntersection:["$discord_roles","$$pl_roles"]}}},{$match:{int_r:{$ne:[]}}}],as:"groups"}},{$project:{discord_roles_ids:0,"groups.discord_roles":0,"groups.intersection_roles":0,"groups.int_r":0,"groups.require_appr":0}},{$match:{lists:{$ne:[]}}}],u=await s.collection("players").aggregate(c).toArray();for(let e of u){Math.round(100*e.seeding_points/i)>=100&&a&&l.push({id:a._id?.toString(),name:a.group_name,expiration:"fixed_reset"==n.tracking_mode&&new Date(n.next_reset),approved:"true"==n.reward_enabled,source:"Seeding"});for(let s of e.groups)l.push({id:s._id,name:s.group_name,expiration:!1,approved:!0,source:"Discord"})}return l}function G(e){return"https://steamcommunity.com/profiles/"+e}async function z(e=(()=>{}),s=!1){if(L&&!s)return e(N),N;{let s,t;process.env.MONGODB_CONNECTION_STRING?s=process.env.MONGODB_CONNECTION_STRING:(s=E.database.mongo.host.includes("://")?E.database.mongo.host:"mongodb://"+E.database.mongo.host+":"+E.database.mongo.port,t=E.database.mongo.database);c.connect(s,(function(s,o){s&&console.error(s);var n=t?o.db(t):o.db();return e(n),n}))}}function W(e,s){e&&e.sendStatus(500),console.error(s)}function H(e){return e.charAt(0).toUpperCase()+e.slice(1)}function J(e,s){console.log("upgrading conf");for(let t in s)if(void 0!==e[t])if(Array.isArray(s[t])){Array.isArray(e[t])||(e[t]=[e[t]]);for(let o=0;o=e[t].length?e[t].push(JSON.parse(JSON.stringify(s[t][o]))):J(e[t][o],s[t][o])}else"object"==typeof s[t]?("object"==typeof e[t]&&null!==e[t]||(e[t]={}),J(e[t],s[t])):typeof e[t]!=typeof s[t]&&(e[t]=s[t]);else e[t]=JSON.parse(JSON.stringify(s[t]))}function Y(e,s=null){z((t=>{!e.approve_update||1!=e.approve_update&&"true"!=e.approve_update?t.collection("whitelists").deleteOne({_id:d(e._id)},((e,t)=>{e?W(s,e):s&&s.send({status:"rejected",...t})})):t.collection("whitelists").updateOne({_id:d(e._id)},{$set:{approved:!0}},((e,t)=>{e?W(s,e):s&&s.send({status:"approved",...t})}))}))}function V(e=64){const s=u.randomBytes(e).toString("base64").slice(0,e);return s.match(/^[a-zA-Z\d]{1,}$/)?s:V(e)}function Q(e,s,t,o){s.userSession&&s.userSession.access_level<=e?o():t.sendStatus(401)}function K(e,t=0){if(t>=e.length)return null;return s.existsSync(e[t])?e[t]:K(e,++t)}function Z(e,s=(()=>{}),t=15){console.log("Looking for free port close to "+e);try{w(e,(async function r(a,l){i.push(l);let c=l,d=n.createServer(),u=!1;await d.listen(c).on("error",(s=>{console.error(" > Failed",s.port),u=!0;let n=(443==e?4443:8080)+100*o;if(++o Couldn't find a free port.\n > Terminating process..."),process.exit(1)})),d.close(),u||(console.log(" > Found free port: "+c),s(c))}))}catch(e){}let o=0,i=[]}!function(e){console.log("Current dir: ",__dirname),console.log(`Configuration file path: ${O}`);let t={web_server:{bind_ip:"0.0.0.0",http_port:80,https_port:443,force_https:!1,session_duration_hours:168},database:{mongo:{host:process.env.MONGODB_CONNECTION_STRING||"127.0.0.1",port:27017,database:"Whitelister"}},app_personalization:{name:"Whitelister",favicon:"",accent_color:"#ffc40b",logo_url:"https://joinsquad.com/wp-content/themes/squad/img/logo.png",logo_border_radius:"10",title_hidden_in_header:!1},discord_bot:{token:"",server_id:"",whitelist_updates_channel_id:""},squadjs:[{websocket:{host:"",port:3e3,token:""}}],other:{automatic_updates:!0,update_check_interval_seconds:3600,whitelist_developers:!0,install_beta_versions:!1,logs_max_file_count:10}};if(s.existsSync(O)){var o={...JSON.parse(s.readFileSync(O,"utf-8").toString())};J(o,t),s.writeFileSync(O,JSON.stringify(o,null,"\t")),e()}else Z(t.web_server.http_port,(function(o){t.web_server.http_port=o,Z(t.web_server.https_port,(function(o){t.web_server.https_port=o,console.log('Configuration file created, set your parameters and run again "node server".\nTerminating execution...'),s.writeFileSync(O,JSON.stringify(t,null,"\t")),process.env.PROCESS_MANAGER_TYPE&&"DOCKER"==process.env.PROCESS_MANAGER_TYPE.toUpperCase()?e():process.exit(0)}))}))}((()=>{console.log=(...e)=>{C(...e),T.trace(...e)},console.error=(...e)=>{x(...e),T.error(...e)},s.readdir(a.join(__dirname,"logs"),{withFileTypes:!0},((e,t)=>{(E&&E.other&&t.length>E.other.logs_max_file_count||t.length>10)&&(t=t.slice(0,t.length-E.other.logs_max_file_count)).forEach((e=>{s.remove(a.join(__dirname,"logs",e.name))}))})),console.log("ARGS:",h),console.log("ENV:",process.env),s.watchFile(O,((e,t)=>{console.log("Reloading configuration");let o,n=!1;try{o=JSON.parse(s.readFileSync(O,"utf-8").toString())}catch(e){console.log("Error found in conf.json file. Couldn't reload configuration."),n=!0}n||(E=o,console.log("Reloaded configuration.",E))})),E=JSON.parse(s.readFileSync(O,"utf-8").toString()),console.log(E),function(e=null){if(L){console.log("MongoDB connection");const s=setTimeout((()=>{console.error(" > Connection failed. Check your Database configuration."),restartProcess(0,1,h)}),1e4);z((t=>{N=t,console.log(" > Successfully connected"),clearTimeout(s),e&&e()}),!0)}}((()=>{!function(e){const s={title:"Main",output_path:"wl",hidden_managers:!1,require_appr:!1,discord_roles:[]},t={category:"seeding_tracker",config:{reset_seeding_time:{value:1,option:864e5},reward_needed_time:{value:0,option:36e5},reward_group_id:"",next_reset:"",seeding_player_threshold:50,seeding_start_player_count:2,reward_enabled:"false",discord_seeding_reward_channel:"",discord_seeding_score_channel:"",tracking_mode:"incremental",time_deduction:{value:1,option:"perc_minute"}}};z((async o=>{function n(e){o.listCollections({name:"lists"}).next(((t,n)=>{null==n?o.collection("lists").insertOne(s,((s,t)=>{s?W(res,s):(console.log("Collection 'lists' created.\n",t),o.collection("whitelists").updateMany({id_list:{$exists:!1}},{$set:{id_list:t.insertedId}},((s,t)=>{s?W(res,s):(console.log("Updated references"),e())})))})):e()}))}async function i(e){let t=!1;const n=Object.keys(s);async function i(r){const a=n[r];await o.collection("lists").updateMany({[a]:{$exists:!1}},{$set:{[a]:s[a]}},(async(s,o)=>{s?console.error(s):(o.modifiedCount>0&&(t||(t=!0,console.log("Repairing Lists format"))),r{})){let s=!1;const n=Object.keys(t.config);async function i(r){const a=`config.${n[r]}`;await o.collection("configs").updateOne({category:"seeding_tracker",[a]:{$exists:!1}},{$set:{[a]:t.config[n[r]]}},(async(t,o)=>{t?console.error(t):(o.modifiedCount>0&&(s||(s=!0,console.log("Repairing SD Config format"))),r Syncing collection "${s}"`);for(let t of e[s]){let e=!1;await o.collection(s).createIndex({[t]:1}).catch((s=>{e=!0,console.log(` > "${t}": Fail. Error: ${s}`)})),e||console.log(` > "${t}": Success`)}}}subcomponent_data.database.root_user_registered=!!await o.collection("users").findOne({access_level:0}),h.demo&&o.collection("users").updateOne({username:"demoadmin"},{$set:{password:u.createHash("sha512").update("demo").digest("hex"),access_level:5}},{upsert:!0}),await a(),o.collection("players").deleteMany({discord_user_id:{$exists:!0},steamid64:{$exists:!1}}),await o.collection("configs").findOne({category:"seeding_tracker",config:{$exists:!0}})||o.collection("configs").updateOne({category:"seeding_tracker"},{$set:{config:{tracking_mode:"incremental"}}},{upsert:!0}),await r(),n((()=>{i(e)}))}))}(F)}))})),process.on("uncaughtException",(function(e){console.error("Uncaught Exception",e.message,e.stack),++D>=(h["self-pm"],5)&&(console.error("Too many errors occurred during the current run. Terminating execution..."),restartProcess(0,1,h))}))}function restartProcess(e=0,s=0,t=null,o=!1){function n(e){e&&e()}t&&t["self-pm"]&&1==t["self-pm"]||o?(process.on("exit",(function(){console.log("Process terminated\nStarting new process"),require("child_process").spawn(process.argv.shift(),process.argv,{cwd:process.cwd(),detached:!0,stdio:"inherit"})})),setTimeout((()=>{n((()=>{process.exit(s)}))}),e)):setTimeout((()=>{console.log("Terminating execution. Process manager will restart me."),n((()=>{process.exit(s)}))}),e)}function terminateAndSpawnChildProcess(e=0,s=0){process.on("exit",(function(){console.log("Process terminated\nStarting new process"),require("child_process").spawn(process.argv.shift(),process.argv,{cwd:process.cwd(),detached:!0,stdio:"inherit"})})),setTimeout((()=>{process.exit(e)}),s)}init(); \ No newline at end of file +const{match:match}=require("assert"),cp=require("child_process");var installingDependencies=!1;const irequire=async e=>{try{require.resolve(e)}catch(e){installingDependencies||(installingDependencies=!0,console.log("INSTALLING DEPENDENCIES...\nTHIS PROCESS MAY TAKE SOME TIME. PLEASE WAIT")),cp.execSync("npm install"),await setImmediate((()=>{})),console.log("DEPENDECIES INSTALLED")}console.log(`Requiring "${e}"`);try{return require(e)}catch(s){console.log(`Could not include "${e}". Restart the script`),restartProcess(0,1)}};installUpdateDependencies=async()=>{console.log("INSTALLING/UPDATING DEPENDENCIES...\nTHIS PROCESS MAY TAKE SOME TIME. PLEASE WAIT"),cp.execSync("npm install")};var subcomponent_status={discord_bot:!1,squadjs:[]},subcomponent_data={discord_bot:{invite_link:""},squadjs:[],database:{root_user_registered:!1},updater:{updating:!1}};async function init(){const e=(await irequire("./package.json")).version,s=await irequire("fs-extra"),t=await irequire("node-stream-zip"),o=await irequire("https"),n=await irequire("http"),i=await irequire("express"),r=i(),a=await irequire("path"),l=await irequire("mongodb"),c=l.MongoClient,d=l.ObjectID,u=await irequire("crypto"),p=await irequire("body-parser"),_=await irequire("cookie-parser"),m=await irequire("nocache"),g=await irequire("log4js"),f=await irequire("axios"),h=(await irequire("minimist"))(process.argv.slice(2)),y=(await irequire("node-run-cmd"),await irequire("express-force-ssl")),w=await irequire("find-free-port"),{mainModule:S}=await irequire("process"),v=await irequire("discord.js"),{io:b}=await irequire("socket.io-client"),$=await irequire("dns"),k=require("util").promisify($.lookup);try{(await irequire("dotenv")).config()}catch(e){console.error(e)}const I=!0;var D=0;const O=h.c||"conf.json",C=console.log,x=console.error;let q=new Date;const A=a.join(__dirname,"logs",q.toISOString().replace(/T/g,"_").replace(/(:|-|\.|Z)/g,"")+".log");s.existsSync("logs")||s.mkdirSync("logs"),s.existsSync(A)||s.writeFileSync(A,""),g.configure({appenders:{App:{type:"file",filename:A}},categories:{default:{appenders:["App"],level:"all"}}});const T=g.getLogger("App");console.log("Log-file:",A);var E,R={http:void 0,https:void 0,configs:{https:{port:void 0},http:{port:void 0}},logging:{requests:!1}},P={ws:null,initDone:!1},j=[];const L=!0;var N,U;function F(){function n(e,s,t){z((e=>{e.collection("whitelists").deleteOne({expiration:{$lte:new Date}},((e,s)=>{e&&console.error(e),t&&t()}))}))}function l(e,s){const t=e.originalUrl.replace(/\?.*$/,"");let o=[];for(let e of o)if(t.startsWith(e))return e;return t.endsWith("/")?t.substring(0,t.length-1):t}function c(e,s,t){w(e)?t():s.redirect("/")}function g(o=!1,n=null){let i=new Date;console.log("Current version: ",e,"\n > Checking for updates",i.toLocaleString()),f.get("https://api.github.com/repos/fantinodavide/Squad_Whitelister/releases").then((i=>{const r=i.data[0],l=r.tag_name.toUpperCase().replace("V","").split("."),c=e.toString().split("."),d=E.other.install_beta_versions&&r.prerelease||!r.prerelease,u=parseInt(c[0]) Update found: "+r.tag_name,r.name),o?function(e){const o=e.assets.filter((e=>"release.zip"==e.name))[0].browser_download_url;console.log(" > Downloading update: "+e.tag_name,e.name,o);const n=a.resolve(__dirname,"tmp_update"),i=a.resolve(n,"gitupd.zip");s.existsSync(n)||s.mkdirSync(n);const r=s.createWriteStream(i);f({method:"get",url:o,responseType:"stream"}).then((e=>{e.data.pipe(r)})),r.on("finish",(e=>{setTimeout((()=>{!function(e,o,n){const i=new t({file:o,storeEntries:!0,skipEntryNameValidation:!0});i.on("ready",(()=>{s.remove(__dirname+"/dist",(()=>{i.extract("release/",__dirname,(async(t,o)=>{i.close(),await installUpdateDependencies(),console.log(" > Extracted",o,"files"),s.remove(e,(()=>{console.log(`${e} folder deleted`);const s=5e3;console.log(" > Restart in",s/1e3,"seconds"),restartProcess(s,0,h)}))}))}))}))}(n,i)}),1e3)})),r.on("error",(e=>{console.error(e)}))}(r):n&&n()):(console.log(" > No updates found"),n&&n())})).catch((e=>{console.error(" > Couldn't check for updates. Proceding startup",e),n&&n()}))}function w(e){return e.userSession&&e.userSession.access_level<=5}g(E.other.automatic_updates,(()=>{console.log(" > Starting up"),setInterval((()=>{g(E.other.automatic_updates)}),1e3*E.other.update_check_interval_seconds),function(){function e(){z((async e=>{const s=await e.collection("configs").findOne({category:"seeding_tracker"});if(!s)return;const t=s.config;"fixed_reset"==t.tracking_mode&&t.reset_seeding_time&&t.next_reset&&new Date>new Date(t.next_reset)&&(e.collection("players").updateMany({},{$set:{seeding_points:0}}),e.collection("configs").updateOne({category:"seeding_tracker"},{$set:{"config.next_reset":new Date((new Date).valueOf()+t.reset_seeding_time.value*t.reset_seeding_time.option).toISOString().split("T")[0]}}))}))}e(),setInterval(e,20)}(),U((async()=>{if(await async function(e=null){let s=null,t=[];console.log("Starting SquadJS WebSockets");for(let n in E.squadjs){subcomponent_status.squadjs[n]=!1;const i=E.squadjs[n];if(i.websocket&&""!=i.websocket.token&&""!=i.websocket.host){const r=setTimeout((()=>{console.error(` > Connection ${+n+1} timed out. Check your SquadJS WebSocket configuration.`),console.log(` > Proceding without SquadJS WebSocket ${+n+1}.`)}),1e4),a=(await k(i.websocket.host)).address;async function o(e,s=5e3){z((async t=>{const o=[{$match:{steamid64:e.player.steamID}},{$lookup:{from:"groups",localField:"id_group",foreignField:"_id",as:"group_full_data"}}];t.collection("whitelists").aggregate(o).toArray((async(o,i)=>{o?W(null,o):t.collection("players").findOne({steamid64:e.player.steamID},(async(o,i)=>{if(o)W(null,o);else{const o=(await t.collection("configs").findOne({category:"seeding_tracker"})).config,r=o.reward_needed_time.value*(o.reward_needed_time.option/1e3/60),a=Math.floor(100*i.seeding_points/r)||0;let l="Welcome "+e.player.name+"\n\n";if(subcomponent_status.squadjs){let s=(await B(e.player.steamID)).filter((e=>e.approved));if(s.length>0){l+="Groups:\n";for(let e of s)l+=` - ${e.name}`,e.expiration&&(l+=": "+(((e.expiration-new Date)/1e3/60/60).toFixed(1)+"h left")),l+="\n"}}if(subcomponent_status.discord_bot){let e="";if(i&&i.discord_user_id&&""!=i.discord_user_id){const s=await U.users.fetch(i.discord_user_id);e=s.username+(s.discriminator?"#"+s.discriminator:"")}"true"==o.reward_enabled&&(l+="\nSeeding Reward: "+a+"%"),l+="\nDiscord Username: "+(""!=e?e:"Not linked")}subcomponent_status.squadjs&&setTimeout((()=>{subcomponent_data.squadjs[n].socket.emit("rcon.warn",e.player.steamID,l,(e=>{})),console.log(l)}),s)}}))}))}))}subcomponent_data.squadjs[n]||(subcomponent_data.squadjs[n]={}),subcomponent_data.squadjs[n].socket=b(`ws://${a}:${i.websocket.port}`,{auth:{token:i.websocket.token},autoUnref:!0}),subcomponent_data.squadjs[n].socket.on("connect",(async()=>{clearTimeout(r),console.log(`SquadJS Websocket ${+n+1} Connected`),subcomponent_data.squadjs[n].socket.emit("rcon.warn","76561198419229279","Whitelister Test Connected",(()=>{})),clearInterval(s),subcomponent_status.squadjs[n]=!0,P.initDone||(P.initDone=!0)})),subcomponent_data.squadjs[n].socket.on("disconnect",(async()=>{subcomponent_status.squadjs[n]=!1,console.log("SquadJS WebSocket\n > Disconnected\n > Trying to reconnect"),s=setInterval((()=>{subcomponent_status.squadjs||subcomponent_data.squadjs[n].connect()}),1e4)})),subcomponent_data.squadjs[n].socket.on("PLAYER_CONNECTED",(async e=>{try{e&&e.player&&e.player.steamID&&(z((async s=>{s.collection("players").updateOne({steamid64:e.player.steamID},{$set:{username:e.player.name}},{upsert:!0})})),setTimeout((()=>{o(e)}),1e4))}catch(e){console.error("PLAYER_CONNECTED ERROR",e)}})),subcomponent_data.squadjs[n].socket.on("CHAT_MESSAGE",(async e=>{switch(e.message.toLowerCase().replace(/^(!|\/)/,"")){case"test":break;case"playerinfo":console.log(e);const s=await z(),t=await s.collection("players").findOne({steamid64:e.player.steamID},{projection:{_id:0,seeding_points:1}});console.log("olddata",t);break;case"profile":o(e,0);break;default:6!=e.message.length||e.message.includes(" ")||z((async s=>{s.collection("profilesLinking").findOne({code:e.message},(async(t,o)=>{if(t)W(null,t);else if(o)if(o.expiration>new Date){const t=await U.users.fetch(o.discordUserId),i=t.username+(t.discriminator?"#"+t.discriminator:""),r=await s.collection("players").findOne({steamid64:e.player.steamID},{projection:{_id:0,seeding_points:1}});s.collection("players").updateOne({discord_user_id:o.discordUserId},{$set:{steamid64:e.player.steamID,username:e.player.name,discord_user_id:o.discordUserId,discord_username:i,...r}},{upsert:!0},((r,a)=>{s.collection("players").deleteOne({steamid64:e.player.steamID,discord_user_id:{$exists:!1}},((r,a)=>{if(r)return W(null,r);s.collection("profilesLinking").deleteOne({_id:o._id}),r?W(null,r):(subcomponent_data.squadjs[n].socket.emit("rcon.warn",e.steamID,"Linked Discord profile: "+i,(e=>{})),t.send({embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Profile Linked").setDescription("Your Discord profile has been linked to a Steam profile").addFields({name:"Steam Username",value:e.name,inline:!0},{name:"SteamID",value:v.hyperlink(e.steamID,"https://steamcommunity.com/profiles/"+e.steamID),inline:!0})]}))}))}))}else s.collection("profilesLinking").deleteOne({_id:o._id})}))}))}}))}else console.log(` > ${+n+1} Not configured. Skipping.`),e&&e()}await Promise.all(t)}(),async function(){console.log("Seeding Tracker started");const e=1;let s=!0;async function t(){const e=await z(),t=await e.collection("configs").findOne({category:"seeding_tracker"}),o=t.config,n=o.reward_needed_time.value*(o.reward_needed_time.option/1e3/60),i=[],r=[];for(let e in subcomponent_data.squadjs){if(!subcomponent_status.squadjs[e])continue;const s=(await M(subcomponent_data.squadjs[e].socket,"rcon.getListPlayers",{})).map((s=>({...s,sqJsConnectionIndex:+e})));s&&s.length>=(o.seeding_start_player_count||2)&&(r[e]=!0),i.push(...s)}s=!1,r.includes(!0)&&z((async e=>{if(i&&i.length>0){if("incremental"==t.config.tracking_mode){let s=0;"point_minute"==t.config.time_deduction.option?s=t.config.time_deduction.value:"perc_minute"==t.config.time_deduction.option&&(s=t.config.time_deduction.value*n/100),await e.collection("players").updateMany({steamid64:{$nin:i.map((e=>e.steamID))},seeding_points:{$gt:s}},{$inc:{seeding_points:-s}})}if(i.length<=o.seeding_player_threshold)for(let s of i){if(!r[s.sqJsConnectionIndex])continue;const i=await e.collection("players").findOne({steamid64:s.steamID});e.collection("players").findOneAndUpdate({steamid64:s.steamID},{$set:{steamid64:s.steamID,username:s.name},$inc:{seeding_points:1}},{upsert:!0,returnDocument:"after"},(async(r,a)=>{if(r)W(null,r);else if("true"==o.reward_enabled){const r=Math.min(Math.floor(10*i?.seeding_points/n),10)||0,l=Math.min(Math.floor(10*a.value?.seeding_points/n),10)||0,c=10*l;if(l>0&&l>r)if(c<100){subcomponent_data.squadjs[s.sqJsConnectionIndex].socket.emit("rcon.warn",s.steamID,`Seeding Reward: \n\n${c}% completed`,(e=>{}));const e={embeds:[{color:v.resolveColor(E.app_personalization.accent_color),title:`${s.name}`,url:G(s.steamID),fields:[{name:"Score",value:c+"%",inline:!0},{name:"SteamID",value:v.hyperlink(s.steamID,G(s.steamID)),inline:!0},{name:"Discord User",value:a.value.discord_user_id?v.userMention(a.value.discord_user_id):"Not Linked",inline:!1}],footer:{text:new Array(10).fill("◼",0,l).fill("◻",l,10).join("")+` ${c}%`,icon_url:E.app_personalization.favicon||E.app_personalization.logo_url},thumbnail:{url:E.app_personalization.logo_url},timestamp:(new Date).toISOString()}],ephemeral:!1};U.channels.cache.get(o.discord_seeding_score_channel)?.send(e)}else if(100==c){const n=await e.collection("groups").findOne({_id:d(t.config.reward_group_id)});let i=`Seeding Reward Completed!\n\nYou have received: ${n.group_name}\n`;if("fixed_reset"==t.config.tracking_mode?i+=`Active until: ${new Date(t.config.next_reset).toLocaleDateString()}`:"incremental"==t.config.tracking_mode&&(i+="Don't drop below 100% to keep your reward!"),subcomponent_data.squadjs[s.sqJsConnectionIndex].socket.emit("rcon.warn",s.steamID,i,(e=>{})),subcomponent_status.discord_bot){const e=[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle(`${s.name} received the Seeding Reward!`).setURL(G(s.steamID)).addFields({name:"Username",value:s.name,inline:!0},{name:"SteamID",value:v.hyperlink(s.steamID,"https://steamcommunity.com/profiles/"+s.steamID),inline:!0},{name:"Discord User",value:a.value.discord_user_id?v.userMention(a.value.discord_user_id):"Not Linked",inline:!1},{name:"Reward Group",value:n.group_name,inline:!0}).setThumbnail(E.app_personalization.logo_url).setFooter({text:new Array(10).fill("◼",0,10).join("")+" 100%",iconURL:E.app_personalization.favicon||E.app_personalization.logo_url}).setTimestamp(new Date)];U.channels.cache.get(o.discord_seeding_reward_channel)?.send({embeds:e})}}}}))}}}))}t(),setInterval(t,60*e*1e3)}(),I){const t=__dirname+"/ALTERNATIVE PORTS.txt";s.removeSync(t);const i=["certificates/certificate.crt","certificates/fullchain.pem","certificates/default.crt"];let a=K(["certificates/certificate.key","certificates/privkey.pem","certificates/default.key"]),l=K(i);function e(e,o){if(e!=o){const n="!!! WARNING !!! Port "+e+" is not available! Closest free port found: "+o+"\n";console.log(n),s.writeFileSync(t,n,{flag:"a+"})}}Z(E.web_server.http_port,(t=>{Z(E.web_server.https_port,(n=>{if(t?R.http=r.listen(t,E.web_server.bind_ip,(function(){var s=R.http.address().address;console.log("HTTP server listening at http://%s:%s",s,t),R.configs.http.port=t,e(E.web_server.http_port,t)})):console.error("Couldn't start HTTP server"),a&&l&&("true"!==process.env.HTTPS_SERVER_DISABLED&&"1"!==process.env.HTTPS_SERVER_DISABLED||!process.env.HTTPS_SERVER_DISABLED)){console.log("Using Certificate:",l,a);const t={key:s.readFileSync(a),cert:s.readFileSync(l)};R.https=o.createServer(t,r),n?(r.set("forceSSLOptions",{httpsPort:n}),R.configs.https.port=n,R.https.listen(n),console.log("HTTPS server listening at https://%s:%s",E.web_server.bind_ip,n),e(E.web_server.https_port,n)):console.error("Couldn't start HTTPS server"),async function(){}()}}))})),setInterval(n,6e4)}}))})),r.use(m()),r.set("etag",!1),r.use("/",p.json()),r.use("/",p.urlencoded({extended:!0})),r.use(_()),r.use((function(e,s,t){if(!E.web_server.force_https)return t();y(e,s,t)})),r.use("/",(function(e,s,t=null){const o=e.cookies;null!=o.stok&&""!=o.stok?z((n=>{n.collection("sessions").findOne({token:o.stok},{projection:{_id:0}},((o,i)=>{o?s.sendStatus(500):null!=i&&i.session_expiration>new Date?(e.userSession=i,n.collection("users").findOne({_id:i.id_user},{projection:{_id:0}},((o,n)=>{null!=n?(e.userSession={...e.userSession,...n},t&&t()):s.send({status:"login_required"}).status(401)}))):t&&t()}))})):t()})),r.use((function(e,s,t){const o=e.get("host");j[o]?j[o]++:j[o]=1;t()})),r.post("/api/changepassword",((e,s,t)=>{const o=e.body;z((t=>{const n=u.createHash("sha512").update(o.new_password).digest("hex"),i=u.createHash("sha512").update(o.old_password).digest("hex");t.collection("users").updateOne({_id:e.userSession.id_user,password:i},{$set:{password:n}},((e,t)=>{e?W(500,e):s.send(t)}))}))})),r.post("/api/login",((e,s,t)=>{const o=e.body;z((e=>{let t=u.createHash("sha512").update(o.password).digest("hex");e.collection("users").findOne({$or:[{username_lower:o.username.toLowerCase()},{username:o.username}],password:t},((e,t)=>{if(e)s.sendStatus(500),console.error(e);else if(null==t)s.sendStatus(401);else{const e=60*E.web_server.session_duration_hours*60*1e3;let o,n={login_date:new Date,session_expiration:new Date(Date.now()+e),id_user:t._id};do{o=!1,n.token=V(128),z((e=>{e.collection("sessions").findOne({token:n.token},((t,i)=>{t?(s.sendStatus(500),console.error(t)):null==i?e.collection("sessions").insertOne(n,((e,t)=>{e?(s.sendStatus(500),console.error(e)):(s.cookie("stok",n.token,{expires:n.session_expiration}),s.cookie("uid",n.id_user,{expires:n.session_expiration}),s.send({status:"login_ok",userDt:n}))})):o=!0}))}))}while(o)}}))}))})),r.post("/api/signup",((e,s,t)=>{const o=e.body;let n,i={username:o.username,username_lower:o.username.toLowerCase(),password:u.createHash("sha512").update(o.password).digest("hex"),access_level:subcomponent_data.database.root_user_registered?100:0,clan_code:o.clan_code,registration_date:new Date,discord_username:o.discord_username};0!=i.access_level||subcomponent_data.database.root_user_registered||(subcomponent_data.database.root_user_registered=!0);const r=60*E.web_server.session_duration_hours*60*1e3;let a={...i};a.login_date=new Date,a.session_expiration=new Date(Date.now()+r),z((e=>{e.collection("users").findOne({username_lower:o.username.toLowerCase()},((t,o)=>{t?(s.sendStatus(500),console.error(t)):null==o?e.collection("users").insertOne(i,((e,t)=>{e?(s.sendStatus(500),console.error(e)):(console.log("\n\n\n\n\ninserted id=>",t.insertedId,"=>",i,"\n\n\n\n\n\n"),n=!1,a.token=V(128),s.redirect(307,"/api/login"))})):s.status(401).send({message:"Username already exists",field:"username"})}))}))})),r.use("/",(function(e,s,t){if(!R.logging.requests)return t();const o=Object.keys(e.query).length>0,n=o?e.query:e.body,i=l(e);console.log("\nREQ: "+i+"\nSESSION: ",e.userSession,"\nPARM "+(o?"GET":"POST")+": ",n),t()})),r.get("/api/getVersion",((s,t,o)=>{t.send(e)})),r.use("/",i.static(__dirname+"/dist")),r.get("/api/getAppPersonalization",(function(e,s,t){s.send(E.app_personalization)})),r.get("/api/getTabs",((e,s,t)=>{const o=[subcomponent_data.database.root_user_registered?{}:{name:"Root User Registration",order:0,type:"tab",max_access_level:null},{name:"Clans",order:5,type:"tab",max_access_level:5},{name:"Whitelist",order:10,type:"tab",max_access_level:100},{name:"Groups",order:15,type:"tab",max_access_level:100},{name:"Approvals",order:25,type:"tab",max_access_level:30},{name:"Seeding",order:27,type:"tab",max_access_level:30},{name:"Users and Roles",order:30,type:"tab",max_access_level:5},{name:"Configuration",order:40,type:"tab",max_access_level:5}];let n;n=subcomponent_data.updater.updating?[{name:"Updating",order:0,type:"tab",max_access_level:null}]:o.filter((s=>null==s.max_access_level&&!e.userSession||e.userSession&&s.max_access_level&&e.userSession.access_level<=s.max_access_level)),s.send({tabs:n})})),r.get("/api/getContextMenu",((e,s,t)=>{let o=[{name:"",action:"",url:"",method:"",order:0}];w(e)&&(o=o.concat([])),s.send(o)})),r.get("/:basePath/:clan_code?",((e,s,t)=>{n(null,null,(()=>{z((o=>{o.collection("lists").findOne({output_path:e.params.basePath},((n,i)=>{if(n)W(s,n);else if(null!=i){s.type("text/plain");let t=e.params.clan_code?{clan_code:e.params.clan_code}:{},n="",r=[],a=[],l=[],c=[],d=[];const u=null!=e.query.usernamesOnly,p=V(6);o.collection("clans").find(t).toArray(((t,_)=>{for(let e of _)a[e._id.toString()]=e,l.push(e._id);o.collection("groups").find().sort({group_name:1}).toArray(((t,_)=>{for(let e of _)r[e._id.toString()]=e;r[p]={group_name:p,group_permissions:["reserve"]};const m=[{$match:{approved:!0,id_clan:{$in:l},id_list:i._id}},{$lookup:{from:"players",let:{steamid64:"$steamid64"},pipeline:[{$match:{$expr:{$eq:["$steamid64","$$steamid64"]},discord_user_id:{$exists:!0}}}],as:"serverPlayerData"}},{$sort:{id_clan:1,id_group:1,username_l:1}}];o.collection("whitelists").aggregate(m).toArray(((t,i)=>{if(t)W(s,t);else if(null!=i){for(let _ of i){let m=(_.serverPlayerData&&_.serverPlayerData[0]?_.serverPlayerData[0].discord_username:null)||_.discord_username||"";""==m||m.startsWith("@")||(m="@"+m),d.push({username:_.username,steamid64:_.steamid64,groupId:_.id_group,clanTag:a[_.id_clan].tag,discordUsername:m})}if(e.params.clan_code)l();else{const g=[{$match:{steamid64:{$ne:null},discord_roles_ids:{$exists:!0}}},{$lookup:{from:"lists",let:{pl_roles:"$discord_roles_ids"},pipeline:[{$match:{output_path:e.params.basePath}},{$addFields:{int_r:{$setIntersection:["$discord_roles","$$pl_roles"]}}},{$match:{int_r:{$ne:[]}}}],as:"lists"}},{$lookup:{from:"groups",let:{pl_roles:"$discord_roles_ids"},pipeline:[{$addFields:{int_r:{$setIntersection:["$discord_roles","$$pl_roles"]}}},{$match:{int_r:{$ne:[]}}}],as:"groups"}},{$project:{discord_roles_ids:0,"groups.discord_roles":0,"groups.intersection_roles":0,"groups.int_r":0,"groups.require_appr":0}},{$match:{lists:{$ne:[]}}}];o.collection("players").aggregate(g).toArray(((e,t)=>{if(e)s.sendStatus(500),console.error(e);else for(let e of t)if(u)n+=e.username+"\n";else for(let s of e.groups)d.push({username:e.username,steamid64:e.steamid64,groupId:s._id,clanTag:"Discord Role",discordUsername:null!=e.discord_username?e.discord_username:""});o.collection("configs").findOne({category:"seeding_tracker"},((e,t)=>{if(t&&t.config.reward_enabled&&"true"==t.config.reward_enabled){const e=t.config,n=e.reward_needed_time.value*e.reward_needed_time.option/1e3/60;o.collection("players").find({steamid64:{$ne:null},seeding_points:{$gte:n}}).toArray(((t,o)=>{t&&W(s,t);const n=o.map((s=>({username:s.username,steamid64:s.steamid64,groupId:e.reward_group_id,clanTag:"Seeder",discordUsername:null!=s.discord_username?s.discord_username:""})));d.push(...n),l()}))}else l()}))}))}function l(){!E.other.whitelist_developers||u||e.params.clan_code||d.push({username:"JetDave",steamid64:"76561198419229279",groupId:p,clanTag:"SQUAD Whitelister Developer",discordUsername:"@=BIA=JetDave#1001"},{username:"Radix",steamid64:"76561198301315305",groupId:p,clanTag:"SQUAD Whitelister Developer",discordUsername:""},{username:"Phil_Caine",steamid64:"76561198272599483",groupId:p,clanTag:"SQUAD Whitelister Developer",discordUsername:""}),function(){for(let e of d)r[e.groupId]?(e.groupId=`${e.groupId}`,n+=`Admin=${e.steamid64}:${r[e.groupId].group_name} // [${e.clanTag}] ${e.username} ${e.discordUsername}\n`,c.includes(e.groupId)||c.push(e.groupId)):(console.log("Could not find group with id",e.groupId,r[e.groupId]),o.collection("whitelists").deleteMany({id_group:e.groupId}));n="\n"+n;for(let e of c){const s=r[e];n=`Group=${s.group_name}:${s.group_permissions.join(",")}\n`+n}}(),s.send(n)}}else s.send("")}))}))}))}else t()}))}))}))})),r.get("/dsTest",((e,s,t)=>{s.type("text/plain");const o=[{$match:{steamid64:{$ne:null},discord_roles_ids:{$exists:!0}}},{$lookup:{from:"groups",let:{pl_roles:"$discord_roles_ids"},pipeline:[{$addFields:{int_r:{$setIntersection:["$discord_roles","$$pl_roles"]}}},{$match:{int_r:{$ne:[]}}}],as:"groups"}},{$project:{discord_roles_ids:0,"groups.discord_roles":0,"groups.intersection_roles":0,"groups.int_r":0,"groups.require_appr":0}}];z((e=>{e.collection("players").aggregate(o).toArray(((e,t)=>{if(e)s.sendStatus(500),console.error(e);else{let e="";for(let s of t)for(let t of s.groups)e+="Admin="+s.steamid64+":"+t.group_name+" // [Discord Role] "+s.username+(null!=s.discord_username?" "+s.discord_username:"")+"\n";s.send(e)}}))}))})),r.get("/api/checkSession",((e,s,t)=>{e.userSession?s.send({status:"session_valid",userSession:e.userSession}):s.send({status:"login_required"}).status(401)})),r.use("/",(function(e,s,t=null){Object.keys(e.query).length>0?e.query:e.body,l(e);e.userSession?t():s.send({status:"login_required"}).status(401)})),r.use("/api/restart",((e,s,t)=>{s.send({status:"restarting"}),restartProcess(0,0,h)})),r.use("/api/logout",((e,s,t)=>{s.clearCookie("stok"),s.clearCookie("uid"),z((t=>{t.collection("sessions").deleteOne({token:e.userSession.token},((e,t)=>{e?W(s,e):s.send({status:"logout_ok"})}))}))})),r.use("/api/users/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/users/read/getAll",((e,s,t)=>{e.query;z((t=>{const o=[{$match:{...e.userSession.access_level>=100?{clan_code:e.userSession.clan_code}:{}}},{$lookup:{from:"clans",localField:"clan_code",foreignField:"clan_code",as:"clan_data"}},{$sort:{username:1}}];t.collection("users").aggregate(o).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.post("/api/users/write/remove",((e,s,t)=>{const o=e.body,n=h.demo?{username:{$ne:"demoadmin"}}:{};z((e=>{e.collection("users").deleteOne({_id:d(o._id),...n,access_level:{$gt:1}},((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.post("/api/users/write/updateAccessLevel",((e,s,t)=>{const o=e.body,n=h.demo?{username:{$ne:"demoadmin"}}:{};console.log("\nFilter\n",n),e.userSession.access_level<=parseInt(o.upd)&&z((e=>{e.collection("users").updateOne({_id:d(o._id),...n,access_level:{$gt:1}},{$set:{access_level:parseInt(o.upd)}},((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.use("/api/roles/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/roles/read/getAll",((e,s,t)=>{s.send({0:{name:"Root",access_level:0},5:{name:"Admin",access_level:5},30:{name:"Approver",access_level:30},100:{name:"User",access_level:100}})})),r.use("/api/api_keys/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/api_keys/read/getAll",((e,s,t)=>{s.send([])})),r.post("/api/api_keys/write/create",((e,s,t)=>null)),r.get("/api/subcomponent/read/:subComp/status",(async(e,s,t)=>{s.send(subcomponent_status[e.params.subComp])})),r.use("/api/config/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/config/read/getFull",(async(e,s,t)=>{let o={...E};if(h.demo&&e.userSession.access_level>0&&(o.discord_bot.token="hidden"),E.app_personalization.favicon=E.app_personalization.favicon||E.app_personalization.logo_url,process.env.HIDDEN_CONFIG_TABS)for(let e of process.env.HIDDEN_CONFIG_TABS.split(";"))try{delete o[e]}catch(e){}s.send(o)})),r.use("/api/config/write",((e,s,t)=>{h.demo&&0!=e.userSession.access_level?s.sendStatus(403):t()})),r.post("/api/config/write/update",(async(e,t,o)=>{const n=e.body;let i={};process.env.HIDDEN_CONFIG_TABS&&process.env.HIDDEN_CONFIG_TABS.split(";").includes(n.category)?i.status="config_rejected":(E[n.category]=n.config,s.writeFileSync(O+".bak",s.readFileSync(O)),s.writeFileSync(O,JSON.stringify(E,null,"\t")),i.status="config_updated"),i.action="reload",t.send(i),restartProcess(0,0,h)})),r.use("/api/dbconfig/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=5&&t()})),r.get("/api/dbconfig/read/getFull",(async(e,s,t)=>{e.body;z((e=>{e.collection("configs").find({config:{$exists:!0,$ne:null},category:{$exists:!0,$ne:null}}).toArray(((e,t)=>{e?W(s,e):t&&s.send(t)}))}))})),r.get("/api/dbconfig/read/:category",(async(e,s,t)=>{e.body;z((t=>{t.collection("configs").findOne({category:e.params.category},((e,t)=>{e?W(s,e):t&&s.send(t.config)}))}))})),r.use("/api/dbconfig/write",((e,s,t)=>{h.demo&&0!=e.userSession.access_level?s.sendStatus(403):t()})),r.post("/api/dbconfig/write/update",(async(e,s,t)=>{const o=e.body;z((e=>{e.collection("configs").updateOne({category:o.category},{$set:{config:o.config}},{upsert:!0},((e,t)=>{e?W(s,e):s.send({status:"config_updated",action:"reload"})}))}))})),r.use("/api/lists/read/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=100&&t()})),r.get("/api/lists/read/getAll",((e,s,t)=>{z((t=>{let o=e.userSession.access_level<100?{}:{hidden_managers:!1};t.collection("lists").find(o).toArray(((e,t)=>{e?W(s,e):s.send(t)}))}))})),r.use("/api/lists/write/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=10?t():s.sendStatus(401)})),r.use("/api/lists/write/checkPerm",(async(e,s,t)=>{s.send({status:"permission_granted"})})),r.post("/api/lists/write/addNewList",((e,s,t)=>{const o=e.body;z((e=>{const t={title:o.title,output_path:o.output_path,hidden_managers:o.hidden_managers,require_appr:o.require_appr,discord_roles:o.discord_roles};e.collection("lists").insertOne(t,((e,t)=>{e?W(s,e):s.send({status:"inserted_new_list",...t})}))}))})),r.post("/api/lists/write/deleteList",((e,s,t)=>{const o=e.body;z((e=>{e.collection("whitelists").deleteMany({id_list:d(o.sel_list_id)},((t,n)=>{t?W(s,t):e.collection("lists").deleteMany({_id:d(o.sel_list_id)},((e,t)=>{e?W(s,e):s.send({status:"removed_list",...t})}))}))}))})),r.post("/api/lists/write/editList",((e,s,t)=>{const o=e.body;z((e=>{const t={title:o.title,output_path:o.output_path,hidden_managers:o.hidden_managers,require_appr:o.require_appr,discord_roles:o.discord_roles};e.collection("lists").updateOne({_id:d(o.sel_list_id)},{$set:t},((e,t)=>{e?W(s,e):s.send({status:"edited_list",...t})}))}))})),r.use("/api/whitelist/read/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<=100&&t()})),r.get("/api/whitelist/read/getAllClans",((e,s,t)=>{e.query;z((t=>{const o=[{$match:e.userSession.access_level>=100?{clan_code:e.userSession.clan_code}:{}},{$lookup:{from:"whitelists",localField:"_id",foreignField:"id_clan",as:"clan_whitelist"}},{$addFields:{player_count:{$size:"$clan_whitelist"}}},{$project:{clan_whitelist:0}},{$sort:{full_name:1}}];t.collection("clans").aggregate(o).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.get("/api/whitelist/read/getAll",((e,s,t)=>{const o=e.query;z((e=>{let t=o.sel_clan_id?{id_clan:d(o.sel_clan_id)}:{};const n=[{$match:{id_list:d(o.sel_list_id),...t}},{$lookup:{from:"groups",localField:"id_group",foreignField:"_id",as:"group_full_data"}},{$lookup:{from:"users",localField:"inserted_by",foreignField:"_id",as:"inserted_by"}},{$lookup:{from:"players",localField:"steamid64",foreignField:"steamid64",as:"serverData"}},{$sort:{id_clan:1,approved:-1,id_group:1,username:1}}];e.collection("whitelists").aggregate(n).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):s.send(t)}))}))})),r.get("/api/whitelist/read/getPendingApprovalClans",((e,s,t)=>{e.query;z((t=>{const o=[{$match:e.userSession.access_level>=100?{clan_code:e.userSession.clan_code}:{}},{$lookup:{from:"whitelists",let:{id_clan:"$_id"},pipeline:[{$match:{$expr:{$eq:["$id_clan","$$id_clan"]},approved:!1}}],as:"whitelists"}},{$sort:{whitelists_data:-1}},{$match:{whitelists:{$exists:!0,$ne:[]}}}];t.collection("clans").aggregate(o).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):(console.log(t),s.send(t))}))}))})),r.get("/api/whitelist/read/getPendingApproval",((e,s,t)=>{const o=e.query;z((e=>{const t=[{$lookup:{from:"whitelists",let:{id_list:"$_id"},pipeline:[{$match:{$expr:{$eq:["$id_list","$$id_list"]},approved:!1,id_clan:d(o.sel_clan_id)}},{$lookup:{from:"groups",localField:"id_group",foreignField:"_id",as:"group_full_data"}},{$lookup:{from:"users",localField:"inserted_by",foreignField:"_id",as:"inserted_by"}},{$sort:{id_clan:1,approved:-1,id_group:1,username:1}}],as:"wl_data"}},{$match:{wl_data:{$exists:!0,$ne:[]}}}];e.collection("lists").aggregate(t).toArray(((e,t)=>{e?(s.sendStatus(500),console.error(e)):(s.send(t),console.log("\n\n\n",t,"\n\n\n"))}))}))})),r.use("/api/whitelist/write/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<30?t():z(((o,n)=>{let i=e.userSession.access_level>=100?{clan_code:e.userSession.clan_code,admins:e.userSession.id_user.toString()}:{};o.collection("clans").findOne(i,((o,n)=>{o?W(s,o):null!=n?(console.log("authorizing",e.userSession.username,"=>",n),t()):(console.log("blocking",n),s.sendStatus(401))}))}))})),r.use("/api/whitelist/write/checkPerm",(async(e,s,t)=>{s.send({status:"permission_granted"})})),r.post("/api/whitelist/write/addPlayer",((e,s,t)=>{const o=e.body;z((t=>{const n=[{$match:e.userSession.access_level>=100?{clan_code:e.userSession.clan_code,admins:e.userSession.id_user.toString()}:{_id:d(o.sel_clan_id)}},{$lookup:{from:"whitelists",localField:"_id",foreignField:"id_clan",as:"clan_whitelist"}},{$addFields:{player_count:{$size:"$clan_whitelist"}}},{$project:{clan_whitelist:0}}];t.collection("clans").aggregate(n).toArray(((n,i)=>{console.log("====>",i);let r=i[0];if(n)console.log("error",n);else if(null!=r)if(""==r.player_limit||r.player_count{o?W(s,o):e.userSession.access_level<100||!a.hidden_managers?t.collection("groups").findOne(n.id_group,((o,l)=>{o?console.log("error",o):null!=l?(n.approved=!(l.require_appr||r.confirmation_ovrd||a.require_appr)||e.userSession.access_level<=30,t.collection("whitelists").insertOne(n,((t,o)=>{if(t)console.log("ERR",t);else if(s.send({status:"inserted_new_player",player:{...n,inserted_by:[{username:e.userSession.username}]},...o}),subcomponent_status.discord_bot){let s,t=[];n.approved?s={}:(s=(new v.ActionRowBuilder).addComponents((new v.ButtonBuilder).setCustomId("approval:approve:"+n._id).setLabel("Approve").setStyle(v.ButtonStyle.Success),(new v.ButtonBuilder).setCustomId("approval:reject:"+n._id).setLabel("Reject").setStyle(v.ButtonStyle.Danger)),t.push(s));const o=[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Whitelist Update").addFields({name:"Username",value:n.username,inline:!0},{name:"SteamID",value:v.hyperlink(n.steamid64,"https://steamcommunity.com/profiles/"+n.steamid64),inline:!0},{name:"Clan",value:i[0].full_name},{name:"Group",value:l.group_name,inline:!0})];n.expiration&&o[0].addFields({name:"Expiration",value:v.time(n.expiration,"R"),inline:!0}),o[0].addFields({name:"Manager",value:e.userSession.username},{name:"List",value:a.title},{name:"Approval",value:n.approved?":white_check_mark: Approved":":hourglass: Pending",inline:!0}),U.channels.cache.get(E.discord_bot.whitelist_updates_channel_id)?.send({embeds:o,components:t})}}))):s.send({status:"not_inserted",reason:"could find corresponding id"})})):s.sendStatus(402)}))}else s.send({status:"not_inserted",reason:"Player limit reached"});else s.sendStatus(401)}))}))})),r.post("/api/whitelist/write/removePlayer",((e,s,t)=>{const o=e.body;z((e=>{e.collection("whitelists").deleteOne({_id:d(o._id)},((e,t)=>{e?W(s,e):s.send({status:"removing_ok",...t})}))}))})),r.post("/api/whitelist/write/clearList",((e,s,t)=>{const o=e.body;z((e=>{e.collection("whitelists").deleteMany({id_clan:d(o.sel_clan_id),id_list:d(o.sel_list_id)},((e,t)=>{e?W(s,e):s.send({status:"clearing_ok",...t})}))}))})),r.use("/api/seeding/read/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<30?t():s.sendStatus(401)})),r.get("/api/seeding/read/getPlayers",((e,s,t)=>{z((e=>{e.collection("players").find({seeding_points:{$gte:1},username:{$ne:null}}).toArray(((e,t)=>{e?W(s,e):s.send(t)}))}))})),r.use("/api/players/read/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<30?t():s.sendStatus(401)})),r.get("/api/players/read/from/steamId/:id",((e,s,t)=>{z((t=>{t.collection("players").findOne({steamid64:e.params.id},((e,t)=>{e?W(s,e):s.send(t)}))}))})),r.get("/api/players/read/from/discordUserId/:id",((e,s,t)=>{z((t=>{t.collection("players").findOne({discord_user_id:e.params.id},((e,t)=>{e?W(s,e):s.send(t)}))}))})),r.use("/api/approval/write/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<30?t():s.sendStatus(401)})),r.use("/api/approval/write/setApprovedStatus",((e,s,t)=>{Y(e.body,s)})),r.use("/api/gameGroups/write/*",((e,s,t)=>{e.userSession&&e.userSession.access_level<10?t():s.sendStatus(401)})),r.use("/api/gameGroups/write/checkPerm",(async(e,s,t)=>{s.send({status:"permission_granted"})})),r.post("/api/gameGroups/write/newGroup",((e,s,t)=>{const o=e.body;z((e=>{e.collection("groups").insertOne(o,((e,t)=>{e?W(s,e):s.send({status:"group_created",data:o,dbRes:{...t}})}))}))})),r.post("/api/gameGroups/write/editGroup",((e,s,t)=>{let o={...e.body};delete o._id,z((t=>{t.collection("groups").updateOne({_id:d(e.body._id)},{$set:o},((o,n)=>{o?W(s,o):t.collection("groups").findOne({_id:d(e.body._id)},((e,t)=>{s.send({status:"edit_ok",...t})}))}))}))})),r.post("/api/gameGroups/write/remove",((e,s,t)=>{z((t=>{t.collection("groups").deleteOne({_id:d(e.body._id)},((e,t)=>{e?W(s,e):s.send({status:"removing_ok",...t})}))}))})),r.get("/api/gameGroups/read/getAllGroups",((e,s,t)=>{z((t=>{let o={};function n(){t.collection("groups").find(o).sort({group_name:1}).toArray(((e,t)=>{e?W(s,e):s.send(t)}))}e.userSession&&e.userSession.access_level>=100?t.collection("clans").findOne({clan_code:e.userSession.clan_code},((e,t)=>{if(e)W(s,e);else{let e=[];if(t)for(let s of t.available_groups)e.push(d(s));o={_id:{$in:e}},n()}})):n()}))})),r.use("/api/discord/*",((...e)=>{Q(30,...e)})),r.use("/api/discord/write",((...e)=>{Q(10,...e)})),r.get("/api/discord/read/getStatus",((e,s,t)=>{s.send(subcomponent_status.discord_bot)})),r.get("/api/discord/read/getRoles",((e,s,t)=>{e.query;if(subcomponent_status.discord_bot){const e=U.guilds.cache.find((e=>e.id==E.discord_bot.server_id));let t=[];for(let s of e.roles.cache)"@everyone"!==s[1].name.toLowerCase()&&t.push({id:s[1].id,name:s[1].name});s.send(t)}else s.sendStatus(404)})),r.get("/api/discord/read/getServers",((e,s,t)=>{e.query;if(subcomponent_status.discord_bot){let e=[];for(let s of U.guilds.cache)e.push({id:s[1].id,name:s[1].name});s.send(e)}else s.sendStatus(404)})),r.get("/api/discord/read/getChannels",(async(e,s,t)=>{e.query;if(subcomponent_status.discord_bot){s.send((await U.guilds.fetch(E.discord_bot.server_id)).channels.cache.sort(((e,s)=>e.rawPosition-s.rawPosition)).filter((e=>4!=e.type)))}else s.sendStatus(404)})),r.get("/api/discord/read/inviteLink",(async(e,s,t)=>{s.send({url:subcomponent_data.discord_bot.invite_link})})),r.use("/api/clans*",((e,s,t)=>{e.userSession&&e.userSession.access_level<10&&t()})),r.get("/api/clans/getAllClans",((e,s,t)=>{z((e=>{e.collection("clans").find().sort({full_name:1,tag:1}).toArray(((e,t)=>{s.send(t)}))}))})),r.post("/api/clans/removeClan",((e,s,t)=>{z((t=>{t.collection("clans").deleteOne({_id:d(e.body._id)},((e,t)=>{e?W(s,e):s.send({status:"removing_ok",...t})}))}))})),r.post("/api/clans/editClan",((e,s,t)=>{let o={...e.body};delete o._id,z((t=>{t.collection("clans").updateOne({_id:d(e.body._id)},{$set:o},((o,n)=>{o?W(s,o):t.collection("clans").findOne({_id:d(e.body._id)},((e,t)=>{s.send({status:"edit_ok",...t})}))}))}))})),r.get("/api/clans/getClanUsers",((e,s,t)=>{const o=e.query;z((e=>{e.collection("users").find({clan_code:o.clan_code}).toArray(((e,t)=>{s.send(t)}))}))})),r.get("/api/clans/getClanAdmins",((e,s,t)=>{const o=e.query;z((e=>{e.collection("clans").findOne({_id:d(o._id)},{projection:{admins:1}},((e,t)=>{if(e)W(s,e);else{let e=t.admins?t.admins:[];s.send(e)}}))}))})),r.post("/api/clans/editClanAdmins",((e,s,t)=>{const o=e.body;z((t=>{t.collection("clans").updateOne({_id:d(e.body._id)},{$set:{admins:o.clan_admins}},((e,t)=>{s.send({status:"edit_ok",...t})}))}))})),r.post("/api/clans/newClan",((e,s,t)=>{const o=e.body;let n;do{let e=V(8);n=!1,z((t=>{t.collection("clans").findOne({full_name_lower:o.full_name.toLowerCase()},((i,r)=>{let a={...o,clan_code:e};a.full_name_lower=o.full_name.toLowerCase(),i?(s.sendStatus(500),console.error(i)):null==r?t.collection("clans").findOne({clan_code:e},((e,o)=>{e?(s.sendStatus(500),console.error(e)):null==o?t.collection("clans").insertOne(a,((e,t)=>{e?(s.sendStatus(500),console.error(e)):(s.send({status:"clan_created",clan_data:a}),console.log("New clan created:",a))})):n=!0})):(s.send({status:"clan_already_registered"}).status(409),console.log("Trying to register an already registered clan:",a))}))}))}while(n)})),r.use("/admin*",c),r.use("/admin",(function(e,s,t){i.static("admin")(e,s,t)})),r.use("/api/admin*",c),r.get("/api/admin",((e,s,t)=>{s.send({status:"Ok"})})),r.get("/api/admin/getConfig",((e,s,t)=>{s.send(E)})),r.get("/api/admin/checkInstallUpdate",((e,s,t)=>{s.send({status:"Ok"}),g(!0)})),r.get("/api/admin/restartApplication",((e,s,t)=>{s.send({status:"Ok"}),restartProcess(e.query.delay?e.query.delay:0,0,h)})),r.use(((e,s,t)=>{s.redirect(404,"/")}))}function U(e=null){if(console.log("Discord BOT"),E.discord_bot&&""!=E.discord_bot.token){const o=new v.Client({intents:[v.GatewayIntentBits.Guilds,v.GatewayIntentBits.GuildMessages,v.GatewayIntentBits.GuildMembers]});o.login(E.discord_bot.token);const n=setTimeout((()=>{console.error(" > Connection timed out. Check your discord_bot configuration."),console.log(" > Proceding without discord bot."),e()}),1e4),i=[{name:"ping",description:"Replies with Pong!"},{name:"listclans",description:"Gives a full list of clans with corresponding info"},{name:"topseed",description:"Gives a list of seeders"},{name:"profile",description:"Links the Discord profile to the Steam profile",options:[{name:"user",description:"Leave empty to get info of yourself, or fill to get info of a specific user",type:6,required:!1}]}];async function s(e){try{const s=await o.guilds.cache.get(E.discord_bot.server_id).members.cache.find((s=>s.id==e));if(s){const t=s.user,o=s._roles;try{z((s=>{s.collection("players").updateOne({discord_user_id:e},{$set:{discord_user_id:e,discord_username:t.username+"#"+t.discriminator,discord_roles_ids:o}},{upsert:!0})}))}catch(e){console.error(e)}}}catch(e){console.error(e)}}async function t(e,s,t=0){e.id;z((async e=>{let o=await e.collection("players").find({seeding_points:{$gte:1}}).skip(10*t).limit(11).sort({seeding_points:-1}).toArray();const n=(await e.collection("configs").findOne({category:"seeding_tracker"})).config,i=n.reward_needed_time.value*(n.reward_needed_time.option/1e3/60),r=o.splice(0,10).map(((e,s)=>[`**${10*t+s+1})**`,`${v.hyperlink(e.username,G(e.steamid64))}`,e.discord_user_id?v.userMention(e.discord_user_id):null,`*${Math.floor(100*(e.seeding_points||0)/i)}%*`].filter((e=>null!=e)).join(" "))),a={embeds:[{color:v.resolveColor(E.app_personalization.accent_color),title:"Top 10 Seeders",description:r.join("\n")}],components:[(new v.ActionRowBuilder).addComponents((new v.ButtonBuilder).setCustomId("topseed:page:"+(+t-1)).setLabel("⮜").setStyle(v.ButtonStyle.Success).setDisabled(t-1<0),(new v.ButtonBuilder).setCustomId("topseed:page:"+(+t+1)).setLabel("⮞").setStyle(v.ButtonStyle.Success).setDisabled(0==o.length))],ephemeral:!1};s.isButton()?(await s.deferUpdate(),await s.message.edit(a)):await s.reply(a)}))}o.on("ready",(async()=>{clearTimeout(n),U=new Proxy(o,{});subcomponent_data.discord_bot.invite_link=`https://discord.com/api/oauth2/authorize?client_id=${o.user.id}&permissions=268564544&scope=bot%20applications.commands`,console.log(" > Logged-in!"),console.log(` > Tag: ${o.user.tag}`),console.log(` > ID: ${o.user.id}`),console.log(` > Invite: ${subcomponent_data.discord_bot.invite_link}`),e();new v.REST({version:"10"}).setToken(E.discord_bot.token).put(v.Routes.applicationCommands(o.user.id),{body:i});let t=[];if(o.guilds)for(let e of o.guilds.cache)t.push({id:e[1].id,name:e[1].name});function r(){z((e=>{e.collection("players").find({discord_user_id:{$exists:!0}}).toArray(((e,t)=>{for(let e of t)s(e.discord_user_id)}))}))}""==E.discord_bot.server_id&&t.length>0&&(E.discord_bot.server_id=t[0].id),""!=E.discord_bot.server_id&&(subcomponent_status.discord_bot=!0,r(),setInterval(r,3e5))})),o.on("raw",(e=>{if("GUILD_MEMBER_UPDATE"===e.t){const s=e.d.user.id;let t=e.d.roles;z((o=>{o.collection("players").updateOne({discord_user_id:s},{$set:{discord_user_id:s,discord_username:e.d.user.username+"#"+e.d.user.discriminator,discord_roles_ids:t}},{upsert:!0})}))}})),o.on("interactionCreate",(async e=>{const n=e.member?e.member.user:e.user,i=`${n.id}`;if(e.isChatInputCommand()){switch(e.commandName){case"ping":await e.deferReply(),await e.followUp("Pong!");break;case"listclans":await e.deferReply(),z((s=>{s.collection("lists").find().toArray(((t,n)=>{s.collection("clans").aggregate([{$lookup:{from:"whitelists",localField:"_id",foreignField:"id_clan",as:"clan_whitelist"}},{$addFields:{uniqueSteamids:{$setUnion:"$clan_whitelist.steamid64"}}},{$addFields:{unique_players:{$size:"$uniqueSteamids"}}},{$project:{_id:0,admins:0,available_groups:0,clan_whitelist:0,uniqueSteamids:0}}]).toArray(((s,t)=>{s&&console.error(s);let r=[],a=!0;for(let s of t){let t=[];for(let e of["tag","clan_code","player_limit","unique_players"])"player_limit"==e&&""==s[e]&&(s[e]="ထ"),t.push({name:H(e.replace(/\_/g," ")),value:s[e].toString(),inline:"full_name"!=e});const l=j.sort(),c=Object.keys(l)[0];if(l[c]){let e=[];for(let t of n){const o="https://"+c+":"+R.configs.https.port+"/"+t.output_path+"/"+s.clan_code;e.push(v.hyperlink(t.title,o))}t.push({name:"Whitelist",value:e.join(" - "),inline:!1})}r.push((new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle(H(s.full_name.replace(/\_/g," "))).addFields(...t)),r.length%10==0&&(a?(a=!1,e.reply({content:v.userMention(i),embeds:r})):o.channels.cache.get(e.channelId).send({embeds:r}),r=[])}r.length>0&&o.channels.cache.get(e.channelId).followUp({embeds:r})}))}))}));break;case"profile":let s=null!=e.options.getUser("user"),r=s?e.options.getUser("user"):n;const a=!s;await e.deferReply({ephemeral:a}),z((t=>{t.collection("players").findOne({discord_user_id:s?r.id:i},(async(s,o)=>{if(s)W(null,s);else{let s=[{name:"Steam "+(o&&o.steamid64?"Username ":""),value:o&&o.steamid64?o.username:"*Not linked*",inline:!0}];if(o&&o.steamid64){s.push({name:"SteamID",value:v.hyperlink(o.steamid64,"https://steamcommunity.com/profiles/"+o.steamid64),inline:!0});const e=(await t.collection("configs").findOne({category:"seeding_tracker"})).config,n=e.reward_needed_time.value*(e.reward_needed_time.option/1e3/60),i=Math.floor(100*(o.seeding_points||0)/n);"true"==e.reward_enabled&&s.push({name:"Seeding Reward",value:`${i}%`,inline:!1});const r=await B(o.steamid64);for(let e of r.filter((e=>e.approved))){let t="Unlimited";e.expiration&&(t=`Expired ${v.time(e.expiration,"R")}`),s.push({name:e.name,value:t,inline:!0})}}let n=await e.followUp({content:v.userMention(r.id),embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setAuthor({name:r.username,iconURL:r.avatarURL()}).setTitle("Linked Profiles").addFields(...s)],components:[(new v.ActionRowBuilder).addComponents(o&&o.steamid64?(new v.ButtonBuilder).setCustomId("profilelink:steam:unlink").setLabel("Unlink Steam").setStyle(v.ButtonStyle.Danger):(new v.ButtonBuilder).setCustomId("profilelink:steam:link").setLabel("Link Steam").setStyle(v.ButtonStyle.Success))],ephemeral:a});n.interaction.ephemeral||setTimeout((async()=>{try{const e=await(n?.interaction?.webhook?.fetchMessage());e&&e.edit({components:[]})}catch(e){console.error(e)}}),3e4)}}))}));break;case"topseed":t(n,e)}s(i)}else if(e.isButton()){const s=e.customId.split(":");switch(s[0]){case"approval":const o="approve"==s[1];Y({_id:s[2],approve_update:o}),e.reply({content:"Done",ephemeral:!0}),e.message.edit({components:[]});let r=e.message.embeds[0];r.fields.filter((e=>"Approval"==e.name))[0].value=o?":white_check_mark: Approved":":x: Rejected",r.fields.push({name:(o?"Approved":"Rejected")+" by",value:v.userMention(n.id),inline:!0}),e.message.edit({embeds:[r]});break;case"profilelink":if(e.message.mentions.users.find((e=>e.id==i))||e.message.ephemeral){if("steam"===s[1])switch(s[2]){case"link":let t;do{let s=V(6);t=!1;const o=3e5,n=new Date(Date.now()+o);z((r=>{r.collection("profilesLinking").deleteOne({discordUserId:i},((a,l)=>{r.collection("profilesLinking").findOne({code:s},((a,l)=>{a?(res.sendStatus(500),console.error(a)):null==l?r.collection("profilesLinking").insertOne({source:"Discord",discordUserId:i,code:s,expiration:n},((t,a)=>{t?(res.sendStatus(500),console.error(t)):(e.reply({content:v.userMention(i),embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Link Steam Profile").setDescription("Join our Squad server and send in any chat the following code (case-sensitive)").addFields({name:"Linking Code",value:s,inline:!1},{name:"Expiration",value:v.time(n,"R"),inline:!1})],ephemeral:!0}),setInterval((async()=>{r.collection("profilesLinking").deleteOne({_id:a.insertedId})}),o))})):t=!0}))}))}))}while(t);break;case"unlink":if(s[3]){if("confirm"===s[3])try{z((s=>{s.collection("players").updateOne({discord_user_id:i},{$unset:{discord_user_id:1}},((s,t)=>{s?W(null,s):(console.log(t),1==t.modifiedCount?e.reply({content:v.userMention(i)+"\nYour Steam account has been unlinked",ephemeral:!0}):e.reply({content:v.userMention(i)+"\nYou don't have a Steam account to unlink",ephemeral:!0}))}))}))}catch(s){e.reply({content:"An error occurred and this interaction cannot be completed",ephemeral:!0}),console.error(s)}}else await e.reply({content:v.userMention(i),embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Unlink Steam").setDescription("Do you really want to unlink your steam profile?")],components:[(new v.ActionRowBuilder).addComponents((new v.ButtonBuilder).setCustomId("profilelink:steam:unlink:confirm").setLabel("Confirm").setStyle(v.ButtonStyle.Danger))],ephemeral:!0})}}else e.reply({content:v.userMention(i),embeds:[(new v.EmbedBuilder).setColor(E.app_personalization.accent_color).setTitle("Unauthorized").setDescription("Only the owner of the profile can use this action")],ephemeral:!0});break;case"topseed":"page"==s[1]&&t(n,e,s[2])}}else e.isModalSubmit()&&e.reply({content:"Modal received",ephemeral:!0})}))}else console.log(" > Not configured. Skipping."),e()}function M(e,s,t){return new Promise(((o,n)=>{e.emit(s,t,(e=>{e.error?n(new Error(e.error)):o(e)}))}))}async function B(e){const s=await z(),t=await s.collection("groups").find().toArray(),o=await s.collection("configs").findOne({category:"seeding_tracker"}),n=o.config,i=n.reward_needed_time.value*(n.reward_needed_time.option/1e3/60);let r;console.log("CREATING objIdRewardGroup from value:",o.config.reward_group_id);try{r=d(o.config.reward_group_id)}catch(e){r=null,console.log("FAILED TO CREATE objIdRewardGroup from value:",o.config.reward_group_id,"\n",n)}let a,l=[];r&&(a=o.config.reward_group_id?await s.collection("groups").findOne({_id:r}):null),l.push(...(await s.collection("whitelists").find({steamid64:e}).toArray()).map((e=>{const s=t.find((s=>s._id.toString()==e.id_group.toString()));let o={};return o.id=e.id_group.toString(),o.name=s.group_name,o.expiration=e.expiration,o.approved=e.approved,o.source="Whitelists",o})));const c=[{$match:{steamid64:e,discord_roles_ids:{$exists:!0}}},{$lookup:{from:"groups",let:{pl_roles:"$discord_roles_ids"},pipeline:[{$addFields:{int_r:{$setIntersection:["$discord_roles","$$pl_roles"]}}},{$match:{int_r:{$ne:[]}}}],as:"groups"}},{$project:{discord_roles_ids:0,"groups.discord_roles":0,"groups.intersection_roles":0,"groups.int_r":0,"groups.require_appr":0}},{$match:{lists:{$ne:[]}}}],u=await s.collection("players").aggregate(c).toArray();for(let e of u){Math.round(100*e.seeding_points/i)>=100&&a&&l.push({id:a._id?.toString(),name:a.group_name,expiration:"fixed_reset"==n.tracking_mode&&new Date(n.next_reset),approved:"true"==n.reward_enabled,source:"Seeding"});for(let s of e.groups)l.push({id:s._id,name:s.group_name,expiration:!1,approved:!0,source:"Discord"})}return l}function G(e){return"https://steamcommunity.com/profiles/"+e}async function z(e=(()=>{}),s=!1){if(L&&!s)return e(N),N;{let s,t;process.env.MONGODB_CONNECTION_STRING?s=process.env.MONGODB_CONNECTION_STRING:(s=E.database.mongo.host.includes("://")?E.database.mongo.host:"mongodb://"+E.database.mongo.host+":"+E.database.mongo.port,t=E.database.mongo.database);c.connect(s,(function(s,o){s&&console.error(s);var n=t?o.db(t):o.db();return e(n),n}))}}function W(e,s){e&&e.sendStatus(500),console.error(s)}function H(e){return e.charAt(0).toUpperCase()+e.slice(1)}function J(e,s){console.log("upgrading conf");for(let t in s)if(void 0!==e[t])if(Array.isArray(s[t])){Array.isArray(e[t])||(e[t]=[e[t]]);for(let o=0;o=e[t].length?e[t].push(JSON.parse(JSON.stringify(s[t][o]))):J(e[t][o],s[t][o])}else"object"==typeof s[t]?("object"==typeof e[t]&&null!==e[t]||(e[t]={}),J(e[t],s[t])):typeof e[t]!=typeof s[t]&&(e[t]=s[t]);else e[t]=JSON.parse(JSON.stringify(s[t]))}function Y(e,s=null){z((t=>{!e.approve_update||1!=e.approve_update&&"true"!=e.approve_update?t.collection("whitelists").deleteOne({_id:d(e._id)},((e,t)=>{e?W(s,e):s&&s.send({status:"rejected",...t})})):t.collection("whitelists").updateOne({_id:d(e._id)},{$set:{approved:!0}},((e,t)=>{e?W(s,e):s&&s.send({status:"approved",...t})}))}))}function V(e=64){const s=u.randomBytes(e).toString("base64").slice(0,e);return s.match(/^[a-zA-Z\d]{1,}$/)?s:V(e)}function Q(e,s,t,o){s.userSession&&s.userSession.access_level<=e?o():t.sendStatus(401)}function K(e,t=0){if(t>=e.length)return null;return s.existsSync(e[t])?e[t]:K(e,++t)}function Z(e,s=(()=>{}),t=15){console.log("Looking for free port close to "+e);try{w(e,E.web_server.bind_ip,(async function r(a,l){i.push(l);let c=l,d=n.createServer(),u=!1;await d.listen(c,E.web_server.bind_ip).on("error",(s=>{console.error(" > Failed",s.port),u=!0;let n=(443==e?4443:8080)+100*o;if(++o Couldn't find a free port.\n > Terminating process..."),process.exit(1)})),d.close(),u||(console.log(" > Found free port: "+c),s(c))}))}catch(e){}let o=0,i=[]}!function(e){console.log("Current dir: ",__dirname),console.log(`Configuration file path: ${O}`);let t={web_server:{bind_ip:"0.0.0.0",http_port:80,https_port:443,force_https:!1,session_duration_hours:168},database:{mongo:{host:process.env.MONGODB_CONNECTION_STRING||"127.0.0.1",port:27017,database:"Whitelister"}},app_personalization:{name:"Whitelister",favicon:"",accent_color:"#ffc40b",logo_url:"https://joinsquad.com/wp-content/themes/squad/img/logo.png",logo_border_radius:"10",title_hidden_in_header:!1},discord_bot:{token:"",server_id:"",whitelist_updates_channel_id:""},squadjs:[{websocket:{host:"",port:3e3,token:""}}],other:{automatic_updates:!0,update_check_interval_seconds:3600,whitelist_developers:!0,install_beta_versions:!1,logs_max_file_count:10}};if(s.existsSync(O)){var o={...JSON.parse(s.readFileSync(O,"utf-8").toString())};J(o,t),s.writeFileSync(O,JSON.stringify(o,null,"\t")),e()}else Z(t.web_server.http_port,(function(o){t.web_server.http_port=o,Z(t.web_server.https_port,(function(o){t.web_server.https_port=o,console.log('Configuration file created, set your parameters and run again "node server".\nTerminating execution...'),s.writeFileSync(O,JSON.stringify(t,null,"\t")),process.env.PROCESS_MANAGER_TYPE&&"DOCKER"==process.env.PROCESS_MANAGER_TYPE.toUpperCase()?e():process.exit(0)}))}))}((()=>{console.log=(...e)=>{C(...e),T.trace(...e)},console.error=(...e)=>{x(...e),T.error(...e)},s.readdir(a.join(__dirname,"logs"),{withFileTypes:!0},((e,t)=>{(E&&E.other&&t.length>E.other.logs_max_file_count||t.length>10)&&(t=t.slice(0,t.length-E.other.logs_max_file_count)).forEach((e=>{s.remove(a.join(__dirname,"logs",e.name))}))})),console.log("ARGS:",h),console.log("ENV:",process.env),s.watchFile(O,((e,t)=>{console.log("Reloading configuration");let o,n=!1;try{o=JSON.parse(s.readFileSync(O,"utf-8").toString())}catch(e){console.log("Error found in conf.json file. Couldn't reload configuration."),n=!0}n||(E=o,console.log("Reloaded configuration.",E))})),E=JSON.parse(s.readFileSync(O,"utf-8").toString()),console.log(E),function(e=null){if(L){console.log("MongoDB connection");const s=setTimeout((()=>{console.error(" > Connection failed. Check your Database configuration."),restartProcess(0,1,h)}),1e4);z((t=>{N=t,console.log(" > Successfully connected"),clearTimeout(s),e&&e()}),!0)}}((()=>{!function(e){const s={title:"Main",output_path:"wl",hidden_managers:!1,require_appr:!1,discord_roles:[]},t={category:"seeding_tracker",config:{reset_seeding_time:{value:1,option:864e5},reward_needed_time:{value:0,option:36e5},reward_group_id:"",next_reset:"",seeding_player_threshold:50,seeding_start_player_count:2,reward_enabled:"false",discord_seeding_reward_channel:"",discord_seeding_score_channel:"",tracking_mode:"incremental",time_deduction:{value:1,option:"perc_minute"}}};z((async o=>{function n(e){o.listCollections({name:"lists"}).next(((t,n)=>{null==n?o.collection("lists").insertOne(s,((s,t)=>{s?W(res,s):(console.log("Collection 'lists' created.\n",t),o.collection("whitelists").updateMany({id_list:{$exists:!1}},{$set:{id_list:t.insertedId}},((s,t)=>{s?W(res,s):(console.log("Updated references"),e())})))})):e()}))}async function i(e){let t=!1;const n=Object.keys(s);async function i(r){const a=n[r];await o.collection("lists").updateMany({[a]:{$exists:!1}},{$set:{[a]:s[a]}},(async(s,o)=>{s?console.error(s):(o.modifiedCount>0&&(t||(t=!0,console.log("Repairing Lists format"))),r{})){let s=!1;const n=Object.keys(t.config);async function i(r){const a=`config.${n[r]}`;await o.collection("configs").updateOne({category:"seeding_tracker",[a]:{$exists:!1}},{$set:{[a]:t.config[n[r]]}},(async(t,o)=>{t?console.error(t):(o.modifiedCount>0&&(s||(s=!0,console.log("Repairing SD Config format"))),r Syncing collection "${s}"`);for(let t of e[s]){let e=!1;await o.collection(s).createIndex({[t]:1}).catch((s=>{e=!0,console.log(` > "${t}": Fail. Error: ${s}`)})),e||console.log(` > "${t}": Success`)}}}subcomponent_data.database.root_user_registered=!!await o.collection("users").findOne({access_level:0}),h.demo&&o.collection("users").updateOne({username:"demoadmin"},{$set:{password:u.createHash("sha512").update("demo").digest("hex"),access_level:5}},{upsert:!0}),await a(),o.collection("players").deleteMany({discord_user_id:{$exists:!0},steamid64:{$exists:!1}}),await o.collection("configs").findOne({category:"seeding_tracker",config:{$exists:!0}})||o.collection("configs").updateOne({category:"seeding_tracker"},{$set:{config:{tracking_mode:"incremental"}}},{upsert:!0}),await r(),n((()=>{i(e)}))}))}(F)}))})),process.on("uncaughtException",(function(e){console.error("Uncaught Exception",e.message,e.stack),++D>=(h["self-pm"],5)&&(console.error("Too many errors occurred during the current run. Terminating execution..."),restartProcess(0,1,h))}))}function restartProcess(e=0,s=0,t=null,o=!1){function n(e){e&&e()}t&&t["self-pm"]&&1==t["self-pm"]||o?(process.on("exit",(function(){console.log("Process terminated\nStarting new process"),require("child_process").spawn(process.argv.shift(),process.argv,{cwd:process.cwd(),detached:!0,stdio:"inherit"})})),setTimeout((()=>{n((()=>{process.exit(s)}))}),e)):setTimeout((()=>{console.log("Terminating execution. Process manager will restart me."),n((()=>{process.exit(s)}))}),e)}function terminateAndSpawnChildProcess(e=0,s=0){process.on("exit",(function(){console.log("Process terminated\nStarting new process"),require("child_process").spawn(process.argv.shift(),process.argv,{cwd:process.cwd(),detached:!0,stdio:"inherit"})})),setTimeout((()=>{process.exit(e)}),s)}init(); \ No newline at end of file diff --git a/server.js b/server.js index e75e855..03b5886 100644 --- a/server.js +++ b/server.js @@ -3293,7 +3293,7 @@ async function init() { function get_free_port(checkPort, callback = () => { }, max_port_tries = 15) { console.log("Looking for free port close to " + checkPort); try { - fp(checkPort, _tryStart) + fp(checkPort, config.web_server.bind_ip, _tryStart) } catch (err) { } let tries = 0; @@ -3304,7 +3304,7 @@ async function init() { let tmpSrv = http.createServer(); let error = false; - await tmpSrv.listen(port).on("error", (e) => { + await tmpSrv.listen(port, config.web_server.bind_ip).on("error", (e) => { console.error(" > Failed", e.port); error = true; let new_try_port = (checkPort == 443 ? 4443 : 8080) + (tries * 100);