├── .gitignore ├── Api ├── launcher.js └── vbucks.js ├── CalderaService ├── calderaservice.js ├── guids.json └── tokencreator.js ├── CloudStorage ├── DefaultEngine.ini ├── DefaultGame.ini ├── DefaultInput.ini └── DefaultRuntimeOptions.ini ├── Config ├── DefaultProfiles │ ├── allathena.json │ ├── athena.json │ ├── campaign.json │ ├── collection_book_people0.json │ ├── collection_book_schematics0.json │ ├── collections.json │ ├── common_core.json │ ├── common_public.json │ ├── creative.json │ ├── metadata.json │ ├── outpost0.json │ ├── profile0.json │ └── theater0.json ├── catalog_config.json └── config.json ├── DiscordBot ├── commands │ ├── Admin │ │ ├── addall.js │ │ ├── additem.js │ │ ├── addvbucks.js │ │ ├── ban.js │ │ ├── createcustommatchcode.js │ │ ├── createhostaccount.js │ │ ├── createsac.js │ │ ├── custommatchcodelist.js │ │ ├── delete.js │ │ ├── deletediscord.js │ │ ├── deletesac.js │ │ ├── kick.js │ │ ├── removeall.js │ │ ├── removeitem.js │ │ ├── removevbucks.js │ │ └── unban.js │ └── User │ │ ├── change-email.js │ │ ├── change-password.js │ │ ├── change-username.js │ │ ├── claimvbucks.js │ │ ├── create.js │ │ ├── details.js │ │ ├── exchange-code.js │ │ ├── giftvbucks.js │ │ ├── lookup.js │ │ ├── sign-out-of-all-sessions.js │ │ └── vbucksamount.js └── index.js ├── LICENSE ├── README.md ├── Website ├── Data │ ├── Images │ │ └── reload.ico │ ├── css │ │ ├── accountExists.css │ │ └── styles.css │ ├── html │ │ ├── accountExists.html │ │ └── register.html │ └── js │ │ ├── accountExists.js │ │ ├── oauthCallback.js │ │ └── registerUser.js └── website.js ├── index.js ├── install_packages.bat ├── matchmaker └── matchmaker.js ├── model ├── friends.js ├── mmcodes.js ├── profiles.js ├── saccodes.js ├── user.js └── userstats.js ├── package-lock.json ├── package.json ├── responses ├── Athena │ └── BattlePass │ │ ├── Season10.json │ │ ├── Season11.json │ │ ├── Season12.json │ │ ├── Season13.json │ │ ├── Season14.json │ │ ├── Season15.json │ │ ├── Season16.json │ │ ├── Season19.json │ │ ├── Season2.json │ │ ├── Season20.json │ │ ├── Season3.json │ │ ├── Season4.json │ │ ├── Season5.json │ │ ├── Season6.json │ │ ├── Season7.json │ │ ├── Season8.json │ │ └── Season9.json ├── CloudDir │ ├── Full.ini │ ├── LawinServer.chunk │ └── LawinServer.manifest ├── Discovery │ ├── discovery_api_assets.json │ └── discovery_frontend.json ├── audio │ └── JamTracks │ │ └── OGRemix │ │ ├── init_0.mp4 │ │ ├── segment_0_1.m4s │ │ ├── segment_0_10.m4s │ │ ├── segment_0_11.m4s │ │ ├── segment_0_12.m4s │ │ ├── segment_0_13.m4s │ │ ├── segment_0_14.m4s │ │ ├── segment_0_15.m4s │ │ ├── segment_0_16.m4s │ │ ├── segment_0_2.m4s │ │ ├── segment_0_3.m4s │ │ ├── segment_0_4.m4s │ │ ├── segment_0_5.m4s │ │ ├── segment_0_6.m4s │ │ ├── segment_0_7.m4s │ │ ├── segment_0_8.m4s │ │ └── segment_0_9.m4s ├── catalog.json ├── contentpages.json ├── epic-settings.json ├── eula │ └── SharedAgreements.json ├── keychain.json ├── motdTarget.json ├── quests.json ├── sdkv1.json ├── sparkTracks.json ├── variants.json └── winterfestRewards.json ├── routes ├── affiliate.js ├── auth.js ├── cloudstorage.js ├── contentpages.js ├── discovery.js ├── friends.js ├── leaderboards.js ├── legal.js ├── lightswitch.js ├── main.js ├── matchmaking.js ├── mcp.js ├── party.js ├── privacy.js ├── reports.js ├── storefront.js ├── timeline.js ├── user.js └── version.js ├── ssl ├── example_ca_bundle.crt ├── example_certificate.crt └── example_private.key ├── start.bat ├── structs ├── autobackendrestart.js ├── autorotate.js ├── checkforupdate.js ├── error.js ├── friend.js ├── functions.js ├── kv.js ├── log.js └── profile.js ├── tokenManager ├── tokenCreation.js ├── tokenVerify.js └── tokens.json └── xmpp └── xmpp.js /.gitignore: -------------------------------------------------------------------------------- 1 | ClientSettings/ 2 | *.Sav 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /Api/launcher.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | const User = require("../model/user.js"); 4 | const log = require("../structs/log.js"); 5 | const bcrypt = require("bcrypt"); 6 | 7 | //Api for launcher login (If u want a POST requesto just replace "app.get" to "app.post" and "req.query" to "req.body") 8 | app.get("/api/launcher/login", async (req, res) => { 9 | const { email, password } = req.query; 10 | 11 | if (!email) return res.status(400).send('The email was not entered.'); 12 | if (!password) return res.status(400).send('The password was not entered.'); 13 | 14 | try { 15 | const user = await User.findOne({ email: email }); 16 | if (!user) return res.status(404).send('User not found.'); 17 | 18 | const passwordMatch = await bcrypt.compare(password, user.password); 19 | 20 | if (passwordMatch) { 21 | const username = user.username; 22 | 23 | return res.status(200).json({ 24 | username: username, 25 | }); 26 | } else { 27 | return res.status(400).send('Error!'); 28 | } 29 | } catch (err) { 30 | log.error('Launcher Api Error:', err); 31 | return res.status(500).send('Error encountered, look at the console'); 32 | } 33 | }); 34 | 35 | module.exports = app; -------------------------------------------------------------------------------- /Api/vbucks.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | const User = require("../model/user.js"); 4 | const Profile = require("../model/profiles.js"); 5 | const log = require("../structs/log.js"); 6 | const fs = require("fs"); 7 | const uuid = require("uuid"); 8 | const config = JSON.parse(fs.readFileSync("./Config/config.json").toString()); 9 | 10 | app.get("/api/reload/vbucks", async (req, res) => { 11 | const { apikey, username, reason } = req.query; 12 | 13 | if (!apikey || apikey !== config.Api.bApiKey) { 14 | return res.status(401).json({ code: "401", error: "Invalid or missing API key." }); 15 | } 16 | if (!username) { 17 | return res.status(400).json({ code: "400", error: "Missing username." }); 18 | } 19 | if (!reason) { 20 | return res.status(400).json({ code: "400", error: "Missing reason." }); 21 | } 22 | 23 | const validReasons = config.Api.reasons; 24 | const addValue = validReasons[reason]; 25 | 26 | if (addValue === undefined) { 27 | return res.status(400).json({ code: "400", error: `Invalid reason. Allowed values: ${Object.keys(validReasons).join(", ")}.` }); 28 | } 29 | 30 | const apiusername = username.trim().toLowerCase(); 31 | 32 | try { 33 | const user = await User.findOne({ username_lower: apiusername }); 34 | 35 | if (!user) { 36 | return res.status(200).json({ message: "User not found." }); 37 | } 38 | 39 | const filter = { accountId: user.accountId }; 40 | const updateCommonCore = { $inc: { 'profiles.common_core.items.Currency:MtxPurchased.quantity': addValue } }; 41 | const updateProfile0 = { $inc: { 'profiles.profile0.items.Currency:MtxPurchased.quantity': addValue } }; 42 | const options = { new: true }; 43 | 44 | const updatedProfile = await Profile.findOneAndUpdate(filter, updateCommonCore, options); 45 | 46 | if (!updatedProfile) { 47 | return res.status(404).json({ code: "404", error: "Profile not found or V-Bucks item missing." }); 48 | } 49 | 50 | await Profile.updateOne(filter, updateProfile0); 51 | 52 | const common_core = updatedProfile.profiles.common_core; 53 | const newQuantityCommonCore = common_core.items['Currency:MtxPurchased'].quantity; 54 | const profile0 = updatedProfile.profiles.profile0; 55 | const newQuantityProfile0 = profile0.items['Currency:MtxPurchased'].quantity + addValue; 56 | 57 | const purchaseId = uuid.v4(); 58 | const lootList = [{ 59 | "itemType": "Currency:MtxGiveaway", 60 | "itemGuid": "Currency:MtxGiveaway", 61 | "quantity": addValue 62 | }]; 63 | 64 | common_core.items[purchaseId] = { 65 | "templateId": `GiftBox:GB_MakeGood`, 66 | "attributes": { 67 | "fromAccountId": `[Administrator]`, 68 | "lootList": lootList, 69 | "params": { 70 | "userMessage": `Thanks For Using Reload Backend!` 71 | }, 72 | "giftedOn": new Date().toISOString() 73 | }, 74 | "quantity": 1 75 | }; 76 | 77 | let ApplyProfileChanges = [ 78 | { 79 | "changeType": "itemQuantityChanged", 80 | "itemId": "Currency:MtxPurchased", 81 | "quantity": newQuantityCommonCore 82 | }, 83 | { // for s1, s2 and s3 84 | "changeType": "itemQuantityChanged", 85 | "itemId": "Currency:MtxPurchased", 86 | "quantity": newQuantityProfile0 87 | }, 88 | { 89 | "changeType": "itemAdded", 90 | "itemId": purchaseId, 91 | "templateId": "GiftBox:GB_MakeGood" 92 | } 93 | ]; 94 | 95 | common_core.rvn += 1; 96 | common_core.commandRevision += 1; 97 | await Profile.updateOne(filter, { $set: { 'profiles.common_core': common_core, 'profiles.profile0.items.Currency:MtxPurchased.quantity': newQuantityProfile0 } }); 98 | 99 | return res.status(200).json({ 100 | profileRevision: common_core.rvn, 101 | profileCommandRevision: common_core.commandRevision, 102 | profileChanges: ApplyProfileChanges, 103 | newQuantityCommonCore, 104 | newQuantityProfile0 105 | }); 106 | 107 | } catch (err) { 108 | log.error("Server error:", err); 109 | return res.status(500).json({ code: "500", error: "Server error. Check console logs for more details." }); 110 | } 111 | }); 112 | 113 | module.exports = app; 114 | -------------------------------------------------------------------------------- /CalderaService/calderaservice.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | const uuid = require("uuid"); 3 | 4 | const tokencreator = require("./tokencreator.js"); 5 | const log = require("../structs/log.js"); 6 | 7 | global.JWT_SECRET = uuid.v4(); 8 | 9 | function createCalderaService() { 10 | const server = http.createServer(function (req, res) { 11 | let body = '' 12 | let caldera; 13 | 14 | req.on('data', (chunk) => { 15 | body += chunk 16 | }) 17 | 18 | req.on('end', function() { 19 | try{ 20 | var json = JSON.parse(body) 21 | caldera = tokencreator.createCaldera(json.account_id) 22 | res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'CF-Ray': '82f8464e681c2c23-FRA', 'CF-Cache-Status': 'DYNAMIC', 'Set-Cookie': '__cf_bm=d7KJ3VLrSpJ6p5u7Kez7f7Upt6y96YxyzfA77RbFfPw-1701569441-0-AcH82zsl/ZSkMYUKBaLaighoB2r37hOiFcOxJ3DYORt+N0d/8vjZorbosjsz0wHB81iZf5OpvYp7MiTHsAIqKbQ=; path=/; expires=Sun, 03-Dec-23 02:40:41 GMT; domain=.caldera-service-prod.ecosec.on.epicgames.com; HttpOnly; Secure; SameSite=None', 'X-Epic-Correlation-Id': '6b93fc2b-d4c2-40a6-a77c-0fa8f2a0f7ba'}) 23 | let payload = { 24 | "provider": "none", 25 | "jwt": caldera 26 | } 27 | res.end(JSON.stringify(payload)) 28 | 29 | caldera = ""; 30 | body = ""; 31 | } catch (error) { 32 | log.error(error) 33 | res.statusCode(400); 34 | res.end(); 35 | } 36 | }); 37 | }) 38 | 39 | return server; 40 | } 41 | 42 | module.exports = createCalderaService; -------------------------------------------------------------------------------- /CalderaService/guids.json: -------------------------------------------------------------------------------- 1 | { 2 | "19.00": "84487dfc-0c14-4a52-afb8-4f5ec9c42284", 3 | "19.01": "84487dfc-0c14-4a52-afb8-4f5ec9c42284", 4 | "19.10": "84487dfc-0c14-4a52-afb8-4f5ec9c42284", 5 | "19.20": "84487dfc-0c14-4a52-afb8-4f5ec9c42284", 6 | "19.30": "84487dfc-0c14-4a52-afb8-4f5ec9c42284", 7 | "19.40": "84487dfc-0c14-4a52-afb8-4f5ec9c42284", 8 | "20.00": "ba02a2eb-ee65-4691-b0ab-65031484a147", 9 | "20.10": "ba02a2eb-ee65-4691-b0ab-65031484a147", 10 | "20.20": "ba02a2eb-ee65-4691-b0ab-65031484a147", 11 | "20.30": "ba02a2eb-ee65-4691-b0ab-65031484a147", 12 | "20.40": "d5ec82d3-296e-4233-bbb5-e056eaefd7f0", 13 | "21.00": "7cc284ff-2c72-48f8-b8e7-a7522617a873", 14 | "21.30": "28bb5a2c-8c24-4fbd-8090-0856a235cf70", 15 | "22.30": "8ad912df-e703-46ba-a6f4-9c7da86337af", 16 | "27.11": "d12ebf86-b8b0-4ff4-ae74-d4b55e59343c", 17 | "28.01": "60344e0a-6112-4164-9b37-33b058de8a94", 18 | "???": "d516ffa4-8dfe-4f75-83bc-3627ef44a5d0" 19 | } -------------------------------------------------------------------------------- /CalderaService/tokencreator.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const fs = require("fs"); 3 | 4 | const guids = require("./guids.json") 5 | 6 | const config = JSON.parse(fs.readFileSync("./Config/config.json").toString()); 7 | 8 | var CalderaGenerated = 0; 9 | 10 | function createCaldera(accountId) { 11 | let CalderaToken = jwt.sign({ 12 | "account_id": accountId, 13 | "generated": CalderaGenerated =+ 1, 14 | "calderaGuid": guids[config.bGameVersion], 15 | "acProvider": "none", 16 | "notes": "", 17 | "fallback": false 18 | }, global.JWT_SECRET ); 19 | 20 | return CalderaToken 21 | } 22 | 23 | module.exports = { createCaldera } -------------------------------------------------------------------------------- /CloudStorage/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [OnlineSubsystemMcp.Xmpp] 2 | bUseSSL=false 3 | ServerAddr="ws://127.0.0.1" 4 | ServerPort=80 5 | 6 | [OnlineSubsystemMcp.Xmpp Prod] 7 | bUseSSL=false 8 | ServerAddr="ws://127.0.0.1" 9 | ServerPort=80 10 | 11 | [OnlineSubsystemMcp.OnlineWaitingRoomMcp] 12 | bEnabled=false 13 | ServiceName="waitingroom" 14 | GracePeriod=300 15 | RetryConfigUrl="https://s3-us-west-1.amazonaws.com/launcher-resources/waitingroom" 16 | 17 | [Voice] 18 | bEnabled=false 19 | 20 | [OnlineSubsystem] 21 | bHasVoiceEnabled=false 22 | 23 | [OnlineSubsystemMcp] 24 | bUsePartySystemV2=false 25 | 26 | [OnlineSubsystemMcp.OnlinePartySystemMcpAdapter] 27 | bUsePartySystemV2=false 28 | 29 | [CrashContextProperties] 30 | CrashReportClientRichText=NSLOCTEXT("FortGlobals", "FortniteCrashReportClientText", "Fortnite has crashed.") 31 | 32 | [/Script/Qos.QosRegionManager] 33 | NumTestsPerRegion=1 34 | PingTimeout=3.0 35 | !RegionDefinitions=ClearArray 36 | +RegionDefinitions=(DisplayName="Auto", RegionId="AutoRegion", bEnabled=true, bVisible=true, bAutoAssignable=true) 37 | +RegionDefinitions=(DisplayName="Na-East", RegionId="NAE", bEnabled=true, bVisible=true, bAutoAssignable=false) 38 | +RegionDefinitions=(DisplayName="Na-West", RegionId="NAW", bEnabled=true, bVisible=true, bAutoAssignable=false) 39 | +RegionDefinitions=(DisplayName="Oceania", RegionId="OCE", bEnabled=true, bVisible=true, bAutoAssignable=false) 40 | +RegionDefinitions=(DisplayName="Europe", RegionId="EU", bEnabled=true, bVisible=true, bAutoAssignable=false) 41 | +RegionDefinitions=(DisplayName="Me", RegionId="ME", bEnabled=true, bVisible=true, bAutoAssignable=false) 42 | +RegionDefinitions=(DisplayName="Brazil", RegionId="BR", bEnabled=true, bVisible=true, bAutoAssignable=false) 43 | 44 | [XMPP] 45 | bEnableWebsockets=true 46 | 47 | [LwsWebSocket] 48 | bDisableCertValidation=true 49 | 50 | [/Script/Engine.NetworkSettings] 51 | n.VerifyPeer=false 52 | 53 | [/Script/Qos.QosRegionManager] 54 | NumTestsPerRegion=1 55 | PingTimeout=3.0 56 | 57 | # Double Pump 58 | ;[ConsoleVariables] 59 | ;Weapon.TryToFireRestrictedByTypeCooldowns=0 60 | 61 | ;Glider redeploy? Not working??? 62 | ;Fort.GliderRedeployRequiresJump=1 63 | ;Fort.GliderRedeployUseWindowOfTime=0 64 | ;Fort.GliderRedeployWindowLength=5.0 65 | ;Fort.GliderRedeployPreventSkydiving=1 66 | 67 | ;Not sure yet, appears to change graphics somehow 68 | ;[/Script/FortniteGame.FortWorldSettings] 69 | ;DefaultWorldTimeOfDayManager=/Game/TimeOfDay/TODM/MASTER_TODM.MASTER_TODM_C 70 | 71 | ;Disable first shot accuracy 72 | ;[/Script/FortniteGame.FortGlobals] 73 | ;bFirstShotAccuracyDisabled=false -------------------------------------------------------------------------------- /CloudStorage/DefaultInput.ini: -------------------------------------------------------------------------------- 1 | [/Script/Engine.InputSettings] 2 | +ConsoleKeys=Tilde 3 | +ConsoleKeys=F8 -------------------------------------------------------------------------------- /CloudStorage/DefaultRuntimeOptions.ini: -------------------------------------------------------------------------------- 1 | [/Script/FortniteGame.FortRuntimeOptions] 2 | ;!DisabledFrontendNavigationTabs=ClearArray ; Remove the ; before "!DisabledFrontendNavigationTabs" if u want to disable Store, Career and Challenges tab 3 | ;+DisabledFrontendNavigationTabs=(TabName="AthenaStore",TabState=EFortRuntimeOptionTabState::Hidden) ; Remove the ; before "+DisabledFrontendNavigationTabs" if u want to disable Store tab 4 | ;+DisabledFrontendNavigationTabs=(TabName="AthenaChallenges",TabState=EFortRuntimeOptionTabState::Hidden) ; Remove the ; before "+DisabledFrontendNavigationTabs" if u want to disable Challenges tab 5 | ;+DisabledFrontendNavigationTabs=(TabName="AthenaCareer",TabState=EFortRuntimeOptionTabState::Hidden) ; Remove the ; before "+DisabledFrontendNavigationTabs" if u want to disable Career tab 6 | ;+DisabledFrontendNavigationTabs=(TabName="AthenaDirectAcquisition",TabState=EFortRuntimeOptionTabState::Hidden) ; Remove the ; before "+DisabledFrontendNavigationTabs" if u want to disable Direct Acquisition 7 | bEnableGlobalChat=false 8 | bDisableGifting=false 9 | bDisableGiftingPC=false 10 | bDisableGiftingPS4=false 11 | bDisableGiftingXB=false 12 | DaysOfFriendshipBeforeCanGift=3 13 | bEnableMexiCola=false # Enable the new friends tab (v19.00+). 14 | bEnableSocialTab=false # Enable the Rift Tour frontend section (v17.30). 15 | bEnableClientSettingsSaveToDisk=true 16 | bEnableClientSettingsSaveToCloud=true 17 | bCanShowSTWUpsellInBR=false 18 | bHidePlusOnVbucksButton=false 19 | bLoadDirectlyIntoLobby=false 20 | bMOTDSameNewsForCreative=true 21 | bShowVoiceChatSettings=false 22 | bEnableFriendSuggestions=false 23 | bSkipInternetCheck=true 24 | bSkipTrailerMovie=true 25 | bAlwaysPlayTrailerMovie=false 26 | bForceBRMode=true 27 | bSkipSubgameSelect=true 28 | bShowStoreBanner=true 29 | bAllowMimicingEmotes=true 30 | bEnableBattlePass=true ; Change "true" to "false" if u want disable Battle Pass tab 31 | bEnableShowdown=true ; Change "true" to "false" if u want disable Compete tab 32 | MinimumAccountLevelForTournamentPlay=0 33 | TournamentRefreshEventsMaxRateSeconds=60 34 | TournamentRefreshPayoutMaxRateSeconds=60 35 | EventServicePayoutRefreshRateSeconds=30 36 | EventServicePayoutRefreshSpreadSeconds=15 37 | bEnableEventServicePayouts=true 38 | bSubgameSelectBRFirstOption=true 39 | bIsCreativeMultiSelectEnabled=false 40 | bLoginErebusDisabled=true 41 | bLoginXBLDisabled=true 42 | bLoginPSNDisabled=true 43 | bLoginEpicWeb=true 44 | 45 | ;SupportURL="https://discord.gg/YWdpkk62M9" ; Remove the ; before "SupportURL" if u want to use custom link for this function. 46 | ;CheckStatusURL="https://discord.gg/YWdpkk62M9" ; Remove the ; before "CheckStatusURL" if u want to use custom link for this function. 47 | 48 | !AthenaStarterGameMode=ClearArray 49 | !AthenaStarterGameModeB=ClearArray 50 | 51 | +ExperimentalBucketPercentList=(ExperimentNum=604,Name="Sidebar",BucketPercents=(0,100),WinningBucketIndex=-1) 52 | 53 | !SocialRTInfo=ClearArray 54 | +SocialRTInfo=(SlotId=1,StartsAtUTC=9999.08.06-22.00.00) 55 | +SocialRTInfo=(SlotId=2,StartsAtUTC=9999.08.07-18.00.00) 56 | +SocialRTInfo=(SlotId=3,StartsAtUTC=9999.08.08-04.00.00) 57 | +SocialRTInfo=(SlotId=4,StartsAtUTC=9999.08.08-14.00.00) 58 | +SocialRTInfo=(SlotId=5,StartsAtUTC=9999.08.08-22.00.00) 59 | !ExperimentalCohortPercent=ClearArray 60 | +ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=20) -------------------------------------------------------------------------------- /Config/DefaultProfiles/campaign.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "campaign", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | }, 17 | "2b375f6f-badf-45a2-a530-2b745a321d20": { 18 | "templateId": "Quest:stonewoodquest_joelkarolina_savesurvivors", 19 | "attributes": { 20 | "completion_stonewoodquest_joelkarolina_savesurvivors": 100, 21 | "level": -1, 22 | "item_seen": true, 23 | "sent_new_notification": true, 24 | "challenge_bundle_id": "", 25 | "xp_reward_scalar": 1, 26 | "challenge_linked_quest_given": "", 27 | "quest_pool": "", 28 | "quest_state": "Claimed", 29 | "last_state_change_time": "2020-01-17T13:28:02.887Z", 30 | "challenge_linked_quest_parent": "", 31 | "max_level_bonus": 0, 32 | "xp": 0, 33 | "favorite": false 34 | }, 35 | "quantity": 1 36 | }, 37 | "60becd3f-e281-4f91-94e0-acb9376b1555": { 38 | "templateId": "Quest:achievement_savesurvivors", 39 | "attributes": { 40 | "level": -1, 41 | "item_seen": true, 42 | "completion_questcollect_survivoritemdata": 1049, 43 | "sent_new_notification": true, 44 | "challenge_bundle_id": "", 45 | "xp_reward_scalar": 1, 46 | "challenge_linked_quest_given": "", 47 | "quest_pool": "", 48 | "quest_state": "Active", 49 | "last_state_change_time": "2020-01-25T18:55:36.621Z", 50 | "challenge_linked_quest_parent": "", 51 | "max_level_bonus": 0, 52 | "xp": 0, 53 | "favorite": false 54 | }, 55 | "quantity": 1 56 | } 57 | }, 58 | "stats": { 59 | "attributes": { 60 | "inventory_limit_bonus": 0 61 | } 62 | }, 63 | "commandRevision": 0 64 | } -------------------------------------------------------------------------------- /Config/DefaultProfiles/collection_book_people0.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "collection_book_people0", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | } 17 | }, 18 | "stats": { 19 | "attributes": { 20 | "inventory_limit_bonus": 0 21 | } 22 | }, 23 | "commandRevision": 0 24 | } -------------------------------------------------------------------------------- /Config/DefaultProfiles/collection_book_schematics0.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "collection_book_schematics0", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | } 17 | }, 18 | "stats": { 19 | "attributes": { 20 | "inventory_limit_bonus": 0 21 | } 22 | }, 23 | "commandRevision": 0 24 | } -------------------------------------------------------------------------------- /Config/DefaultProfiles/collections.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "collections", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | } 17 | }, 18 | "stats": { 19 | "attributes": { 20 | "inventory_limit_bonus": 0 21 | } 22 | }, 23 | "commandRevision": 0 24 | } -------------------------------------------------------------------------------- /Config/DefaultProfiles/common_public.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "common_public", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | } 17 | }, 18 | "stats": { 19 | "attributes": { 20 | "inventory_limit_bonus": 0 21 | } 22 | }, 23 | "commandRevision": 0 24 | } -------------------------------------------------------------------------------- /Config/DefaultProfiles/creative.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "creative", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | } 17 | }, 18 | "stats": { 19 | "attributes": { 20 | "inventory_limit_bonus": 0 21 | } 22 | }, 23 | "commandRevision": 0 24 | } -------------------------------------------------------------------------------- /Config/DefaultProfiles/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "metadata", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | } 17 | }, 18 | "stats": { 19 | "attributes": { 20 | "inventory_limit_bonus": 0 21 | } 22 | }, 23 | "commandRevision": 0 24 | } -------------------------------------------------------------------------------- /Config/DefaultProfiles/outpost0.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "outpost0", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | } 17 | }, 18 | "stats": { 19 | "attributes": { 20 | "inventory_limit_bonus": 0 21 | } 22 | }, 23 | "commandRevision": 0 24 | } -------------------------------------------------------------------------------- /Config/DefaultProfiles/theater0.json: -------------------------------------------------------------------------------- 1 | { 2 | "created": "", 3 | "updated": "", 4 | "rvn": 1, 5 | "wipeNumber": 1, 6 | "accountId": "", 7 | "profileId": "theater0", 8 | "version": "no_version", 9 | "items": { 10 | "Token:reloadbackend": { 11 | "templateId": "Token:reloadbackend", 12 | "attributes": { 13 | "item_seen": true 14 | }, 15 | "quantity": 1 16 | } 17 | }, 18 | "stats": { 19 | "attributes": { 20 | "inventory_limit_bonus": 0 21 | } 22 | }, 23 | "commandRevision": 0 24 | } -------------------------------------------------------------------------------- /Config/catalog_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": "BR Item Shop Config", 3 | "daily1": { 4 | "itemGrants": [""], 5 | "price": 0 6 | }, 7 | "daily2": { 8 | "itemGrants": [""], 9 | "price": 0 10 | }, 11 | "daily3": { 12 | "itemGrants": [""], 13 | "price": 0 14 | }, 15 | "daily4": { 16 | "itemGrants": [""], 17 | "price": 0 18 | }, 19 | "daily5": { 20 | "itemGrants": [""], 21 | "price": 0 22 | }, 23 | "daily6": { 24 | "itemGrants": [""], 25 | "price": 0 26 | }, 27 | "featured1": { 28 | "itemGrants": [""], 29 | "price": 0 30 | }, 31 | "featured2": { 32 | "itemGrants": [""], 33 | "price": 0 34 | } 35 | } -------------------------------------------------------------------------------- /Config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "moderators": ["discordId", "discordId2"], 3 | "discord": { 4 | "bUseDiscordBot": true, 5 | "bot_token": "", 6 | "bEnableInGamePlayerCount": true 7 | }, 8 | "mongodb": { 9 | "database": "mongodb://127.0.0.1/Reload" 10 | }, 11 | "chat": { 12 | "//": "Disabling global chat can help the server to perform faster as less resources are used.", 13 | "EnableGlobalChat": false 14 | }, 15 | 16 | "bEnableDebugLogs": false, 17 | "bEnableFormattedLogs": false, 18 | "bEnableRebootUser": true, 19 | "bEnableCrossBans": false, 20 | 21 | "//": "If you want to use the backend on reboot, leave 3551 as the port", 22 | "port": 3551, 23 | 24 | "Api": { 25 | "bApiKey": "ur-api-key", 26 | "reasons": { 27 | "Kill": 25, 28 | "Win": 50 29 | } 30 | }, 31 | 32 | "Website": { 33 | "bUseWebsite": false, 34 | "clientId": "your-client-id-here", 35 | "clientSecret": "your-client-secret-here", 36 | "redirectUri": "http://127.0.0.1:${websiteport}/oauth2/callback", 37 | "websiteport": 100 38 | }, 39 | 40 | "//": "to add more gameserver on 'gameServerIP' add ip:port:playlist", 41 | "matchmakerIP": "127.0.0.1:80", 42 | "gameServerIP": ["127.0.0.1:7777:playlist_defaultsolo"], 43 | 44 | "//": "For 'BattlePassSeason' enter only the number of your season (from 2 to 20)", 45 | "bEnableBattlepass": false, 46 | "bBattlePassSeason": 2, 47 | 48 | "//": "For 'bVersionJoinable' you can set a season that is also `9.10` and not just `9`", 49 | "bEnableOnlyOneVersionJoinable": false, 50 | "bVersionJoinable": 2, 51 | 52 | "//": "If 'bUseAutoRotate' is enabled it will automatically rotate the item shop using the time you put in 'bRotateTime', you can select the season limit with 'bSeasonLimit' (please only do up to season 10, it might be a bit broken after!), 'bRotateTime' means what time the shop rotates (it is UTC and uses the 24 hour clock, not 12 hour), for 'bItemShopWebhook' you have to generate a webhook to your item shop channel and put the link here, you can select how many cosmetics are in daily: 'bDailyItemsAmount' and featured: 'bFeaturedItemsAmount'", 53 | "bUseAutoRotate": false, 54 | "bEnableAutoRotateDebugLogs": false, 55 | "bEnableDiscordWebhook": false, 56 | "bChapterlimit": "1", 57 | "bSeasonlimit": "10", 58 | "bRotateTime": "00:00", 59 | "bExcludedItems": ["CID_VIP_Athena_Commando_M_GalileoGondola_SG", "CID_636_Athena_Commando_M_GalileoGondola_78MFZ", "CID_637_Athena_Commando_M_GalileoOutrigger_7Q0YU", "CID_VIP_Athena_Commando_M_GalileoFerry_SG", "CID_VIP_Athena_Commando_F_GalileoRocket_SG", "CID_568_Athena_Commando_M_RebirthSoldier"], 60 | "bItemShopWebhook": "", 61 | "bDailyItemsAmount": 6, 62 | "bFeaturedItemsAmount": 2, 63 | 64 | "//": "By enabling 'bEnableReports' in-game reports will work, a player will not be able to spam reports", 65 | "bEnableReports": false, 66 | "bReportChannelId": "your-discord-channel-id-here", 67 | 68 | "//": "If bCompletedSeasonalQuests is set to 'true' it will set all quests except daily quests as completed", 69 | "bCompletedSeasonalQuests": false, 70 | 71 | "//": "If 'bEnableSACRewards' is enabled it will give the person who supports a creator a percentage of vbucks based on how much percentage you put in 'bPercentageSACRewards' it will give in vbucks, so if you set '25%' and there is a 1000 item in the shop vbucks will give 250 vbucks to the one who supports a creator", 72 | "bEnableSACRewards": false, 73 | "bPercentageSACRewards": 0, 74 | 75 | "//": "For 'bRestartTime' use this format: [number][y, M, w, d, h, m, s] so 5m, for Month u need to add the big M not the m", 76 | "bEnableAutoBackendRestart": false, 77 | "bRestartTime": "", 78 | 79 | "//": "If active it will send a message in the channel when the backend goes online", 80 | "bEnableBackendStatus": false, 81 | "bBackendStatusChannelId": "", 82 | 83 | "//": "Use HTTPS (For VPS Side Only, Or If You Were To Setup A Type Record To Your Own Public Adress From Your Domain) (You Also Need To Change The Examples In SSl Folder!)", 84 | "bEnableHTTPS": false, "//": "Only Enable if You Have SSL Certificate In The ssl Folder", 85 | "ssl": { 86 | "cert": "./ssl/example_certificate.crt", 87 | "//ca//": "./ssl/example_ca_bundle.crt", "//": "Optional (Only If You Have A CA Bundle from your ssl certificate)", 88 | "key": "./ssl/example_private.key" 89 | }, 90 | 91 | "//": "The Caldera Service is used for newer versions of the game! For see what season are now supported look CalderaService/guids.json and add the version on 'bGameVersion'", 92 | "bEnableCalderaService": false, 93 | "bGameVersion": "27.11", 94 | "bCalderaServicePort": 5000, 95 | 96 | "//": "These are all the events you can add to the game, like the rift in the sky!", 97 | "bEnableGeodeEvent": false, 98 | "geodeEventStartDate": "2020-01-01T00:00:00.000Z", 99 | "bEnableCrackInTheSky": false, 100 | "bEnableS4OddityPrecursor": false, 101 | "bEnableS4OddityExecution": false, 102 | "S4OddityEventStartDate": "2020-01-01T00:00:00.000Z", 103 | "S4OddityEventsInterval": 0, 104 | "bEnableS5OddityPrecursor": false, 105 | "S5OddityPrecursorDate": "2020-01-01T00:00:00.000Z", 106 | "bEnableS5OddityExecution": false, 107 | "S5OddityExecutionDate": "2020-01-01T00:00:00.000Z", 108 | "bEnableCubeLightning": false, 109 | "cubeSpawnDate": "2020-01-01T00:00:00.000Z", 110 | "bEnableBlockbusterRiskyEvent": false, 111 | "bEnableCubeLake": false, 112 | "cubeLakeDate": "2020-01-01T00:00:00.000Z" 113 | } -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/addall.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const Users = require('../../../model/user.js'); 5 | const Profiles = require('../../../model/profiles.js'); 6 | const log = require("../../../structs/log.js"); 7 | const destr = require("destr"); 8 | const config = require('../../../Config/config.json') 9 | 10 | module.exports = { 11 | commandInfo: { 12 | name: "addall", 13 | description: "Allows you to give a user all cosmetics. Note: This will reset all your lockers to default", 14 | options: [ 15 | { 16 | name: "user", 17 | description: "The user you want to give the cosmetic to", 18 | required: true, 19 | type: 6 20 | } 21 | ] 22 | }, 23 | execute: async (interaction) => { 24 | 25 | if (!config.moderators.includes(interaction.user.id)) { 26 | return interaction.reply({ content: "You do not have moderator permissions.", ephemeral: true }); 27 | } 28 | 29 | await interaction.deferReply({ ephemeral: true }); 30 | 31 | const selectedUser = interaction.options.getUser('user'); 32 | const selectedUserId = selectedUser?.id; 33 | try { 34 | const targetUser = await Users.findOne({ discordId: selectedUserId }); 35 | if (!targetUser) { 36 | return interaction.editReply({ content: "That user does not own an account" }); 37 | } 38 | 39 | const profile = await Profiles.findOne({ accountId: targetUser.accountId }); 40 | if (!profile) { 41 | return interaction.editReply({ content: "That user does not have a profile" }); 42 | } 43 | 44 | const allItems = destr(fs.readFileSync(path.join(__dirname, "../../../Config/DefaultProfiles/allathena.json"), 'utf8')); 45 | if (!allItems) { 46 | return interaction.editReply({ content: "Failed to parse allathena.json" }); 47 | } 48 | 49 | Profiles.findOneAndUpdate({ accountId: targetUser.accountId }, { $set: { "profiles.athena.items": allItems.items } }, { new: true }, (err, doc) => { 50 | if (err) { 51 | return interaction.editReply({ content: "There was an error updating the profile." }); 52 | } 53 | }); 54 | 55 | const embed = new MessageEmbed() 56 | .setTitle("Full Locker Added") 57 | .setDescription("Successfully added all skins (Full Locker) to the selected account") 58 | .setColor("GREEN") 59 | .setFooter({ 60 | text: "Reload Backend", 61 | iconURL: "https://i.imgur.com/2RImwlb.png" 62 | }) 63 | .setTimestamp(); 64 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 65 | } catch (error) { 66 | log.error("An error occurred:", error); 67 | interaction.editReply({ content: "An error occurred while processing the request." }); 68 | } 69 | } 70 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/addvbucks.js: -------------------------------------------------------------------------------- 1 | const Users = require('../../../model/user'); 2 | const Profiles = require('../../../model/profiles'); 3 | const config = require('../../../Config/config.json'); 4 | const uuid = require("uuid"); 5 | const { MessageEmbed } = require("discord.js"); 6 | 7 | module.exports = { 8 | commandInfo: { 9 | name: "addvbucks", 10 | description: "Lets you change a user's amount of V-Bucks", 11 | options: [ 12 | { 13 | name: "user", 14 | description: "The user you want to change the V-Bucks of", 15 | required: true, 16 | type: 6 17 | }, 18 | { 19 | name: "vbucks", 20 | description: "The amount of V-Bucks you want to give (Can be negative to deduct V-Bucks)", 21 | required: true, 22 | type: 4 23 | } 24 | ] 25 | }, 26 | execute: async (interaction) => { 27 | await interaction.deferReply({ ephemeral: true }); 28 | 29 | if (!config.moderators.includes(interaction.user.id)) { 30 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 31 | } 32 | 33 | const selectedUser = interaction.options.getUser('user'); 34 | const selectedUserId = selectedUser?.id; 35 | const user = await Users.findOne({ discordId: selectedUserId }); 36 | 37 | if (!user) { 38 | return interaction.editReply({ content: "That user does not own an account", ephemeral: true }); 39 | } 40 | 41 | const vbucks = parseInt(interaction.options.getInteger('vbucks')); 42 | if (isNaN(vbucks) || vbucks === 0) { 43 | return interaction.editReply({ content: "Invalid V-Bucks amount specified.", ephemeral: true }); 44 | } 45 | 46 | const filter = { accountId: user.accountId }; 47 | const updateCommonCore = { $inc: { 'profiles.common_core.items.Currency:MtxPurchased.quantity': vbucks } }; 48 | const updateProfile0 = { $inc: { 'profiles.profile0.items.Currency:MtxPurchased.quantity': vbucks } }; 49 | const options = { new: true }; 50 | 51 | const updatedProfile = await Profiles.findOneAndUpdate(filter, updateCommonCore, options); 52 | if (!updatedProfile) { 53 | return interaction.editReply({ content: "That user does not own an account", ephemeral: true }); 54 | } 55 | 56 | await Profiles.updateOne(filter, updateProfile0); 57 | 58 | const common_core = updatedProfile.profiles["common_core"]; 59 | const profile0 = updatedProfile.profiles["profile0"]; 60 | 61 | const newQuantityCommonCore = common_core.items['Currency:MtxPurchased'].quantity; 62 | const newQuantityProfile0 = profile0.items['Currency:MtxPurchased'].quantity + vbucks; 63 | 64 | if (newQuantityCommonCore < 0 || newQuantityCommonCore >= 1000000) { 65 | return interaction.editReply({ 66 | content: "V-Bucks amount is out of valid range after the update.", 67 | ephemeral: true 68 | }); 69 | } 70 | 71 | const purchaseId = uuid.v4(); 72 | const lootList = [{ 73 | "itemType": "Currency:MtxGiveaway", 74 | "itemGuid": "Currency:MtxGiveaway", 75 | "quantity": vbucks 76 | }]; 77 | 78 | common_core.items[purchaseId] = { 79 | "templateId": `GiftBox:GB_MakeGood`, 80 | "attributes": { 81 | "fromAccountId": `[Administrator]`, 82 | "lootList": lootList, 83 | "params": { 84 | "userMessage": `Thanks For Using Reload Backend!` 85 | }, 86 | "giftedOn": new Date().toISOString() 87 | }, 88 | "quantity": 1 89 | }; 90 | 91 | let ApplyProfileChanges = [ 92 | { 93 | "changeType": "itemQuantityChanged", 94 | "itemId": "Currency:MtxPurchased", 95 | "quantity": newQuantityCommonCore 96 | }, 97 | { // for s1, s2 and s3 98 | "changeType": "itemQuantityChanged", 99 | "itemId": "Currency:MtxPurchased", 100 | "quantity": newQuantityProfile0 101 | }, 102 | { 103 | "changeType": "itemAdded", 104 | "itemId": purchaseId, 105 | "templateId": "GiftBox:GB_MakeGood" 106 | } 107 | ]; 108 | 109 | common_core.rvn += 1; 110 | common_core.commandRevision += 1; 111 | common_core.updated = new Date().toISOString(); 112 | 113 | await Profiles.updateOne(filter, { 114 | $set: { 115 | 'profiles.common_core': common_core, 116 | 'profiles.profile0.items.Currency:MtxPurchased.quantity': newQuantityProfile0 117 | } 118 | }); 119 | 120 | const embed = new MessageEmbed() 121 | .setTitle("V-Bucks Updated") 122 | .setDescription(`Successfully added **${vbucks}** V-Bucks to <@${selectedUserId}> with a GiftBox`) 123 | .setThumbnail("https://i.imgur.com/yLbihQa.png") 124 | .setColor("GREEN") 125 | .setFooter({ 126 | text: "Reload Backend", 127 | iconURL: "https://i.imgur.com/2RImwlb.png" 128 | }) 129 | .setTimestamp(); 130 | 131 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 132 | 133 | return { 134 | profileRevision: common_core.rvn, 135 | profileCommandRevision: common_core.commandRevision, 136 | profileChanges: ApplyProfileChanges, 137 | newQuantityCommonCore, 138 | newQuantityProfile0 139 | }; 140 | } 141 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/ban.js: -------------------------------------------------------------------------------- 1 | const User = require("../../../model/user.js"); 2 | const functions = require("../../../structs/functions.js"); 3 | const fs = require("fs"); 4 | const config = JSON.parse(fs.readFileSync("./Config/config.json").toString()); 5 | 6 | module.exports = { 7 | commandInfo: { 8 | name: "ban", 9 | description: "Ban a user from the backend by their username.", 10 | options: [ 11 | { 12 | name: "username", 13 | description: "Target username.", 14 | required: true, 15 | type: 3 16 | } 17 | ] 18 | }, 19 | execute: async (interaction) => { 20 | await interaction.deferReply({ ephemeral: true }); 21 | 22 | if (!config.moderators.includes(interaction.user.id)) { 23 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 24 | } 25 | 26 | const { options } = interaction; 27 | const targetUser = await User.findOne({ username_lower: (options.get("username").value).toLowerCase() }); 28 | 29 | if (!targetUser) return interaction.editReply({ content: "The account username you entered does not exist.", ephemeral: true }); 30 | else if (targetUser.banned) return interaction.editReply({ content: "This account is already banned.", ephemeral: true }); 31 | 32 | await targetUser.updateOne({ $set: { banned: true } }); 33 | 34 | let refreshToken = global.refreshTokens.findIndex(i => i.accountId == targetUser.accountId); 35 | if (refreshToken != -1) global.refreshTokens.splice(refreshToken, 1); 36 | 37 | let accessToken = global.accessTokens.findIndex(i => i.accountId == targetUser.accountId); 38 | if (accessToken != -1) { 39 | global.accessTokens.splice(accessToken, 1); 40 | 41 | let xmppClient = global.Clients.find(client => client.accountId == targetUser.accountId); 42 | if (xmppClient) xmppClient.client.close(); 43 | } 44 | 45 | if (accessToken != -1 || refreshToken != -1) functions.UpdateTokens(); 46 | 47 | interaction.editReply({ content: `Successfully banned **${targetUser.username}**`, ephemeral: true }); 48 | } 49 | } -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/createcustommatchcode.js: -------------------------------------------------------------------------------- 1 | const MMCodes = require("../../../model/mmcodes.js"); 2 | const { MessageEmbed } = require("discord.js"); 3 | const log = require("../../../structs/log.js"); 4 | const config = require('../../../Config/config.json') 5 | 6 | module.exports = { 7 | commandInfo: { 8 | name: "create-custom-match-code", 9 | description: "Create a custom matchmaking code.", 10 | options: [ 11 | { 12 | name: "code", 13 | description: "The matchmaking code you want.", 14 | required: true, 15 | type: 3 16 | }, 17 | { 18 | name: "ip", 19 | description: "The ip of your gameserver.", 20 | required: true, 21 | type: 3 22 | }, 23 | { 24 | name: "port", 25 | description: "The port of your gameserver.", 26 | required: true, 27 | type: 4 28 | } 29 | ] 30 | }, 31 | execute: async (interaction) => { 32 | if (!config.moderators.includes(interaction.user.id)) { 33 | return interaction.reply({ content: "You do not have moderator permissions.", ephemeral: true }); 34 | } 35 | 36 | try { 37 | const code = interaction.options.getString('code'); 38 | const ip = interaction.options.getString('ip'); 39 | const port = interaction.options.getInteger('port'); 40 | 41 | if (code.length > 24) return interaction.reply({ content: "Your code can't be longer than 24 characters.", ephemeral: true }); 42 | if (code.length < 4) return interaction.reply({ content: "Your code has to be at least 4 characters long.", ephemeral: true }); 43 | if (code.includes(" ")) return interaction.reply({ content: "Your code can't contain spaces", ephemeral: true }); 44 | if (/[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g.test(code)) return interaction.reply({ content: "Your code can't contain any special characters", ephemeral: true }); 45 | 46 | const ipExp = new RegExp("^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$"); 47 | if (!ipExp.test(ip)) return interaction.reply({ content: "You provided an invalid IP address", ephemeral: true }); 48 | 49 | if (port < 1 || port > 65535) { 50 | return interaction.reply({ content: "The port must be a number between 1 and 65535.", ephemeral: true }); 51 | } 52 | 53 | const codeExists = await MMCodes.findOne({ code_lower: code.toLowerCase() }); 54 | if (codeExists) return interaction.reply({ content: "This code already exists", ephemeral: true }); 55 | 56 | const newCode = await MMCodes.create({ 57 | created: new Date(), 58 | code: code, 59 | code_lower: code.toLowerCase(), 60 | ip: ip, 61 | port: port 62 | }); 63 | await newCode.save(); 64 | 65 | const embed = new MessageEmbed() 66 | .setTitle("Successfully Created Custom Game Code!") 67 | .setDescription("Your code has been created. You can now use it to host custom games.") 68 | .setColor("GREEN") 69 | .addFields([ 70 | { 71 | name: "Code", 72 | value: code, 73 | inline: true 74 | }, 75 | { 76 | name: "IP", 77 | value: ip, 78 | inline: true 79 | }, 80 | { 81 | name: "Port", 82 | value: port?.toString(), 83 | inline: true 84 | } 85 | ]) 86 | .setTimestamp() 87 | .setThumbnail("https://i.imgur.com/2RImwlb.png") 88 | .setFooter({ 89 | text: "Reload Backend", 90 | iconURL: "https://i.imgur.com/2RImwlb.png" 91 | }); 92 | 93 | await interaction.reply({ embeds: [embed], ephemeral: true }); 94 | 95 | } catch (error) { 96 | log.error(error); 97 | return interaction.reply({ content: "An error occurred while processing your request.", ephemeral: true }); 98 | } 99 | } 100 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/createhostaccount.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const functions = require("../../../structs/functions.js"); 3 | const User = require("../../../model/user.js"); 4 | const log = require("../../../structs/log.js"); 5 | 6 | module.exports = { 7 | commandInfo: { 8 | name: "createhostaccount", 9 | description: "Creates a host account for Reload Backend." 10 | }, 11 | execute: async (interaction) => { 12 | await interaction.deferReply({ ephemeral: true }); 13 | 14 | const serverOwnerId = interaction.guild.ownerId; 15 | 16 | if (interaction.user.id !== serverOwnerId) { 17 | return interaction.editReply({ 18 | content: "Only the server owner can execute this command.", 19 | ephemeral: true 20 | }); 21 | } 22 | 23 | const existingHostAccount = await User.findOne({ email: "hostaccount@reloadbackend.com" }); 24 | 25 | if (existingHostAccount) { 26 | return interaction.editReply({ 27 | content: "A host account has already been created.", 28 | ephemeral: true 29 | }); 30 | } 31 | 32 | const username = "reloadbackendhostaccount"; 33 | const email = "hostaccount@reloadbackend.com"; 34 | const password = generateRandomPassword(12); 35 | 36 | try { 37 | await functions.registerUser(null, username, email, password).then(async (resp) => { 38 | let embed = new MessageEmbed() 39 | .setColor(resp.status >= 400 ? "#ff0000" : "#56ff00") 40 | .addFields( 41 | { name: "Message", value: resp.message }, 42 | { name: "Username", value: `\`\`\`${username}\`\`\`` }, 43 | { name: "Email", value: `\`\`\`${email}\`\`\`` }, 44 | { name: "Password", value: `\`\`\`${password}\`\`\`` }, 45 | ) 46 | .setTimestamp() 47 | .setFooter({ 48 | text: "Reload Backend", 49 | iconURL: "https://i.imgur.com/2RImwlb.png" 50 | }); 51 | 52 | if (resp.status >= 400) { 53 | return interaction.editReply({ embeds: [embed], ephemeral: true }); 54 | } 55 | 56 | await interaction.editReply({ 57 | embeds: [embed], 58 | ephemeral: true 59 | }); 60 | }); 61 | } catch (error) { 62 | log.error(error); 63 | return interaction.editReply({ 64 | content: "An error occurred while creating the host account.", 65 | ephemeral: true 66 | }); 67 | } 68 | } 69 | }; 70 | 71 | function generateRandomPassword(length) { 72 | const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+<>?"; 73 | let password = ""; 74 | for (let i = 0; i < length; i++) { 75 | const randomIndex = Math.floor(Math.random() * charset.length); 76 | password += charset[randomIndex]; 77 | } 78 | return password; 79 | } -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/createsac.js: -------------------------------------------------------------------------------- 1 | const functions = require("../../../structs/functions.js"); 2 | const config = require("../../../Config/config.json"); 3 | 4 | module.exports = { 5 | commandInfo: { 6 | name: "createsac", 7 | description: "Creates a Support A Creator Code.", 8 | options: [ 9 | { 10 | name: "code", 11 | description: "The Support A Creator Code.", 12 | required: true, 13 | type: 3 14 | }, 15 | { 16 | name: "ingame-username", 17 | description: "In-Game Name of the codes owner.", 18 | required: true, 19 | type: 3 20 | }, 21 | ], 22 | }, 23 | execute: async (interaction) => { 24 | await interaction.deferReply({ ephemeral: true }); 25 | 26 | if (!config.moderators.includes(interaction.user.id)) { 27 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 28 | } 29 | 30 | const { options } = interaction; 31 | 32 | const code = options.get("code").value; 33 | const username = options.get("ingame-username").value; 34 | const creator = interaction.user.id; 35 | await functions.createSAC(code, username, creator).then(resp => { 36 | 37 | if (resp.message == undefined) return interaction.editReply({ content: "There was an unknown error!", ephemeral: true}) 38 | 39 | if (resp.status >= 400) return interaction.editReply({ content: resp.message, ephemeral: true }); 40 | 41 | interaction.editReply({ content: resp.message, ephemeral: true }); 42 | }); 43 | } 44 | } -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/custommatchcodelist.js: -------------------------------------------------------------------------------- 1 | const MMCodes = require("../../../model/mmcodes.js"); 2 | const { MessageEmbed } = require("discord.js"); 3 | const log = require("../../../structs/log.js"); 4 | const config = require('../../../Config/config.json') 5 | 6 | module.exports = { 7 | commandInfo: { 8 | name: "custom-match-code-list", 9 | description: "Lists all custom matchmaking codes.", 10 | }, 11 | execute: async (interaction) => { 12 | if (!config.moderators.includes(interaction.user.id)) { 13 | return interaction.reply({ content: "You do not have moderator permissions.", ephemeral: true }); 14 | } 15 | 16 | try { 17 | const codes = await MMCodes.find({}); 18 | 19 | if (codes.length === 0) { 20 | return interaction.reply({ content: "No custom matchmaking codes found.", ephemeral: true }); 21 | } 22 | 23 | const embed = new MessageEmbed() 24 | .setTitle("Custom Matchmaking Codes") 25 | .setDescription("Here is the list of all custom matchmaking codes:") 26 | .setColor("GREEN") 27 | .setTimestamp() 28 | .setThumbnail("https://i.imgur.com/2RImwlb.png") 29 | .setFooter({ 30 | text: "Reload Backend", 31 | iconURL: "https://i.imgur.com/2RImwlb.png" 32 | }); 33 | 34 | codes.forEach(code => { 35 | embed.addFields([ 36 | { name: "Code", value: code.code, inline: true }, 37 | { name: "IP", value: code.ip, inline: true }, 38 | { name: "Port", value: code.port.toString(), inline: true } 39 | ]); 40 | }); 41 | 42 | await interaction.reply({ embeds: [embed], ephemeral: true }); 43 | } catch (error) { 44 | log.error(error); 45 | return interaction.reply({ content: "An error occurred while fetching the codes.", ephemeral: true }); 46 | } 47 | } 48 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/delete.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const Users = require('../../../model/user.js'); 5 | const Profiles = require('../../../model/profiles.js'); 6 | const SACCodes = require('../../../model/saccodes.js'); 7 | const Friends = require('../../../model/friends.js'); 8 | const log = require("../../../structs/log.js"); 9 | const config = require('../../../Config/config.json'); 10 | 11 | module.exports = { 12 | commandInfo: { 13 | name: "delete", 14 | description: "Deletes a user's account", 15 | options: [ 16 | { 17 | name: "username", 18 | description: "Target username.", 19 | required: true, 20 | type: 3 21 | } 22 | ] 23 | }, 24 | execute: async (interaction) => { 25 | await interaction.deferReply({ ephemeral: true }); 26 | 27 | if (!config.moderators.includes(interaction.user.id)) { 28 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 29 | } 30 | 31 | const username = interaction.options.getString('username'); 32 | const deleteAccount = await Users.findOne({ username: username }); 33 | 34 | if (!deleteAccount) { 35 | await interaction.editReply({ content: "The selected user does not have **an account**", ephemeral: true }); 36 | return; 37 | } 38 | 39 | const accountId = deleteAccount.accountId; 40 | let somethingDeleted = false; 41 | 42 | await Users.deleteOne({ username: username }).then(() => { 43 | somethingDeleted = true; 44 | }).catch(error => { 45 | log.error('Error deleting from Users:', error); 46 | }); 47 | 48 | await Profiles.deleteOne({ accountId: accountId }).then(() => { 49 | somethingDeleted = true; 50 | }).catch(error => { 51 | log.error('Error deleting from Profiles:', error); 52 | }); 53 | 54 | await Friends.deleteOne({ accountId: accountId }).then(() => { 55 | somethingDeleted = true; 56 | }).catch(error => { 57 | log.error('Error deleting from Friends:', error); 58 | }); 59 | 60 | await SACCodes.deleteOne({ owneraccountId: accountId }).then(() => { 61 | somethingDeleted = true; 62 | }).catch(error => { 63 | log.error('Error deleting from SACCodes:', error); 64 | }); 65 | 66 | const clientSettingsPath = path.join(__dirname, '../../../ClientSettings', accountId); 67 | if (fs.existsSync(clientSettingsPath)) { 68 | fs.rmSync(clientSettingsPath, { recursive: true, force: true }); 69 | somethingDeleted = true; 70 | } 71 | 72 | if (!somethingDeleted) { 73 | await interaction.editReply({ content: `No data found to delete for **${username}**.`, ephemeral: true }); 74 | return; 75 | } 76 | 77 | const embed = new MessageEmbed() 78 | .setTitle("Account deleted") 79 | .setDescription(`The account for **${username}** has been **deleted**`) 80 | .setColor("GREEN") 81 | .setFooter({ 82 | text: "Reload Backend", 83 | iconURL: "https://i.imgur.com/2RImwlb.png" 84 | }) 85 | .setTimestamp(); 86 | 87 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 88 | 89 | try { 90 | const user = await interaction.client.users.fetch(deleteAccount.discordId); 91 | if (user) { 92 | await user.send({ content: `Your account has been deleted by <@${interaction.user.id}>` }); 93 | } 94 | } catch (error) { 95 | log.error('Could not send DM:', error); 96 | } 97 | } 98 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/deletediscord.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const Users = require('../../../model/user.js'); 5 | const Profiles = require('../../../model/profiles.js'); 6 | const SACCodes = require('../../../model/saccodes.js'); 7 | const Friends = require('../../../model/friends.js'); 8 | const log = require("../../../structs/log.js"); 9 | const config = require('../../../Config/config.json'); 10 | 11 | module.exports = { 12 | commandInfo: { 13 | name: "deletediscord", 14 | description: "Deletes a user's account", 15 | options: [ 16 | { 17 | name: "username", 18 | description: "Target username.", 19 | required: true, 20 | type: 6 21 | } 22 | ] 23 | }, 24 | execute: async (interaction) => { 25 | await interaction.deferReply({ ephemeral: true }); 26 | 27 | if (!config.moderators.includes(interaction.user.id)) { 28 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 29 | } 30 | 31 | const discordId = interaction.options.getUser('username').id; 32 | const user = interaction.options.getUser('username'); 33 | const deleteAccount = await Users.findOne({ discordId: discordId }); 34 | 35 | if (!deleteAccount) { 36 | await interaction.editReply({ content: "The selected user does not have **an account**", ephemeral: true }); 37 | return; 38 | } 39 | 40 | const accountId = deleteAccount.accountId; 41 | let somethingDeleted = false; 42 | 43 | await Users.deleteOne({ discordId: discordId }).then(() => { 44 | somethingDeleted = true; 45 | }).catch(error => { 46 | log.error('Error deleting from Users:', error); 47 | }); 48 | 49 | await Profiles.deleteOne({ accountId: accountId }).then(() => { 50 | somethingDeleted = true; 51 | }).catch(error => { 52 | log.error('Error deleting from Profiles:', error); 53 | }); 54 | 55 | await Friends.deleteOne({ accountId: accountId }).then(() => { 56 | somethingDeleted = true; 57 | }).catch(error => { 58 | log.error('Error deleting from Friends:', error); 59 | }); 60 | 61 | await SACCodes.deleteOne({ owneraccountId: accountId }).then(() => { 62 | somethingDeleted = true; 63 | }).catch(error => { 64 | log.error('Error deleting from SACCodes:', error); 65 | }); 66 | 67 | const clientSettingsPath = path.join(__dirname, '../../../ClientSettings', accountId); 68 | if (fs.existsSync(clientSettingsPath)) { 69 | fs.rmSync(clientSettingsPath, { recursive: true, force: true }); 70 | somethingDeleted = true; 71 | } 72 | 73 | if (!somethingDeleted) { 74 | await interaction.editReply({ content: `No data found to delete for **${deleteAccount.username}**.`, ephemeral: true }); 75 | return; 76 | } 77 | 78 | const embed = new MessageEmbed() 79 | .setTitle("Account deleted") 80 | .setDescription("The account has been **deleted**") 81 | .setColor("GREEN") 82 | .setFooter({ 83 | text: "Reload Backend", 84 | iconURL: "https://i.imgur.com/2RImwlb.png" 85 | }) 86 | .setTimestamp(); 87 | 88 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 89 | 90 | try { 91 | await user.send({ content: `Your account has been deleted by <@${interaction.user.id}>` }); 92 | } catch (error) { 93 | log.error('Could not send DM:', error); 94 | } 95 | } 96 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/deletesac.js: -------------------------------------------------------------------------------- 1 | const config = require("../../../Config/config.json"); 2 | const SACCodes = require("../../../model/saccodes.js"); 3 | 4 | module.exports = { 5 | commandInfo: { 6 | name: "deletesac", 7 | description: "Deletes a Support A Creator Code.", 8 | options: [ 9 | { 10 | name: "code", 11 | description: "The Support A Creator Code to delete.", 12 | required: true, 13 | type: 3 14 | } 15 | ], 16 | }, 17 | execute: async (interaction) => { 18 | await interaction.deferReply({ ephemeral: true }); 19 | 20 | if (!config.moderators.includes(interaction.user.id)) { 21 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 22 | } 23 | 24 | const { options } = interaction; 25 | const code = options.get("code").value; 26 | 27 | try { 28 | const sacCode = await SACCodes.findOne({ code_lower: code.toLowerCase() }); 29 | 30 | if (!sacCode) { 31 | return interaction.editReply({ content: `No **Support A Creator** code found for **${code}**.`, ephemeral: true }); 32 | } 33 | 34 | await SACCodes.deleteOne({ _id: sacCode._id }); 35 | 36 | return interaction.editReply({ content: `**Support A Creator** code **${code}** has been successfully deleted.`, ephemeral: true }); 37 | 38 | } catch (error) { 39 | return interaction.editReply({ content: "There was an error while deleting the **SAC code.** Please try again later.", ephemeral: true }); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/kick.js: -------------------------------------------------------------------------------- 1 | const User = require("../../../model/user.js"); 2 | const functions = require("../../../structs/functions.js"); 3 | const fs = require("fs"); 4 | const config = JSON.parse(fs.readFileSync("./Config/config.json").toString()); 5 | 6 | module.exports = { 7 | commandInfo: { 8 | name: "kick", 9 | description: "Kick someone out of their current session by their username.", 10 | options: [ 11 | { 12 | name: "username", 13 | description: "Target username.", 14 | required: true, 15 | type: 3 16 | } 17 | ] 18 | }, 19 | execute: async (interaction) => { 20 | await interaction.deferReply({ ephemeral: true }); 21 | 22 | if (!config.moderators.includes(interaction.user.id)) { 23 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 24 | } 25 | 26 | const { options } = interaction; 27 | const targetUser = await User.findOne({ username_lower: (options.get("username").value).toLowerCase() }); 28 | 29 | if (!targetUser) return interaction.editReply({ content: "The account username you entered does not exist.", ephemeral: true }); 30 | 31 | let refreshToken = global.refreshTokens.findIndex(i => i.accountId == targetUser.accountId); 32 | if (refreshToken != -1) global.refreshTokens.splice(refreshToken, 1); 33 | 34 | let accessToken = global.accessTokens.findIndex(i => i.accountId == targetUser.accountId); 35 | if (accessToken != -1) { 36 | global.accessTokens.splice(accessToken, 1); 37 | 38 | let xmppClient = global.Clients.find(client => client.accountId == targetUser.accountId); 39 | if (xmppClient) xmppClient.client.close(); 40 | } 41 | 42 | if (accessToken != -1 || refreshToken != -1) { 43 | functions.UpdateTokens(); 44 | 45 | return interaction.editReply({ content: `Successfully kicked ${targetUser.username}`, ephemeral: true }); 46 | } 47 | 48 | interaction.editReply({ content: `There are no current active sessions by ${targetUser.username}`, ephemeral: true }); 49 | } 50 | } -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/removeall.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const Users = require('../../../model/user.js'); 5 | const Profiles = require('../../../model/profiles.js'); 6 | const log = require("../../../structs/log.js"); 7 | const destr = require("destr"); 8 | const config = require('../../../Config/config.json') 9 | 10 | module.exports = { 11 | commandInfo: { 12 | name: "removeall", 13 | description: "Allows you to remove all cosmetics from a user. Note: This will reset all your lockers to default", 14 | options: [ 15 | { 16 | name: "user", 17 | description: "The user you want to give the cosmetic to", 18 | required: true, 19 | type: 6 20 | } 21 | ] 22 | }, 23 | execute: async (interaction) => { 24 | 25 | if (!config.moderators.includes(interaction.user.id)) { 26 | return interaction.reply({ content: "You do not have moderator permissions.", ephemeral: true }); 27 | } 28 | 29 | await interaction.deferReply({ ephemeral: true }); 30 | 31 | const selectedUser = interaction.options.getUser('user'); 32 | const selectedUserId = selectedUser?.id; 33 | try { 34 | const targetUser = await Users.findOne({ discordId: selectedUserId }); 35 | if (!targetUser) { 36 | return interaction.editReply({ content: "That user does not own an account" }); 37 | } 38 | 39 | const profile = await Profiles.findOne({ accountId: targetUser.accountId }); 40 | if (!profile) { 41 | return interaction.editReply({ content: "That user does not have a profile" }); 42 | } 43 | 44 | const allItems = destr(fs.readFileSync(path.join(__dirname, "../../../Config/DefaultProfiles/athena.json"), 'utf8')); 45 | if (!allItems) { 46 | return interaction.editReply({ content: "Failed to parse athena.json" }); 47 | } 48 | 49 | Profiles.findOneAndUpdate({ accountId: targetUser.accountId }, { $set: { "profiles.athena.items": allItems.items } }, { new: true }, (err, doc) => { 50 | if (err) { 51 | return interaction.editReply({ content: "There was an error updating the profile." }); 52 | } 53 | }); 54 | 55 | const embed = new MessageEmbed() 56 | .setTitle("Full Locker Removed") 57 | .setDescription("Successfully removed all skins to the selected account") 58 | .setColor("GREEN") 59 | .setFooter({ 60 | text: "Reload Backend", 61 | iconURL: "https://i.imgur.com/2RImwlb.png" 62 | }) 63 | .setTimestamp(); 64 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 65 | } catch (error) { 66 | log.error("An error occurred:", error); 67 | interaction.editReply({ content: "An error occurred while processing the request." }); 68 | } 69 | } 70 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/removeitem.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const Users = require('../../../model/user.js'); 3 | const Profiles = require('../../../model/profiles.js'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const destr = require('destr'); 7 | const log = require("../../../structs/log.js"); 8 | const config = require('../../../Config/config.json'); 9 | 10 | module.exports = { 11 | commandInfo: { 12 | name: "removeitem", 13 | description: "Allows you to remove a cosmetic (skin, pickaxe, glider, etc.) from a user", 14 | options: [ 15 | { 16 | name: "user", 17 | description: "The user you want to remove the cosmetic from", 18 | required: true, 19 | type: 6 20 | }, 21 | { 22 | name: "cosmeticname", 23 | description: "The name of the cosmetic you want to remove", 24 | required: true, 25 | type: 3 26 | } 27 | ] 28 | }, 29 | execute: async (interaction) => { 30 | 31 | if (!config.moderators.includes(interaction.user.id)) { 32 | return interaction.reply({ content: "You do not have moderator permissions.", ephemeral: true }); 33 | } 34 | 35 | await interaction.deferReply({ ephemeral: true }); 36 | 37 | const selectedUser = interaction.options.getUser('user'); 38 | const selectedUserId = selectedUser?.id; 39 | 40 | const user = await Users.findOne({ discordId: selectedUserId }); 41 | if (!user) { 42 | return interaction.editReply({ content: "That user does not own an account" }); 43 | } 44 | 45 | const profile = await Profiles.findOne({ accountId: user.accountId }); 46 | if (!profile) { 47 | return interaction.editReply({ content: "That user does not own an account" }); 48 | } 49 | 50 | const cosmeticname = interaction.options.getString('cosmeticname'); 51 | 52 | try { 53 | const response = await fetch(`https://fortnite-api.com/v2/cosmetics/br/search?name=${cosmeticname}`); 54 | const json = await response.json(); 55 | const cosmeticFromAPI = json.data; 56 | 57 | if (!cosmeticFromAPI) { 58 | return interaction.editReply({ content: "Could not find the cosmetic" }); 59 | } 60 | 61 | const cosmeticimage = cosmeticFromAPI.images.icon; 62 | 63 | const regex = /^(?:[A-Z][a-z]*\b\s*)+$/; 64 | if (!regex.test(cosmeticname)) { 65 | return interaction.editReply({ content: "Please check for correct casing. E.g 'renegade raider' is wrong, but 'Renegade Raider' is correct." }); 66 | } 67 | 68 | const file = fs.readFileSync(path.join(__dirname, "../../../Config/DefaultProfiles/allathena.json")); 69 | const jsonFile = destr(file.toString()); 70 | const items = jsonFile.items; 71 | let foundcosmeticname = ""; 72 | let found = false; 73 | 74 | for (const key of Object.keys(items)) { 75 | const [type, id] = key.split(":"); 76 | if (id === cosmeticFromAPI.id) { 77 | foundcosmeticname = key; 78 | if (!profile.profiles.athena.items[key]) { 79 | return interaction.editReply({ content: "That user does not have that cosmetic" }); 80 | } 81 | found = true; 82 | break; 83 | } 84 | } 85 | 86 | if (!found) { 87 | return interaction.editReply({ content: `Could not find the cosmetic ${cosmeticname} in user's profile` }); 88 | } 89 | 90 | const update = { $unset: {} }; 91 | update.$unset[`profiles.athena.items.${foundcosmeticname}`] = ""; 92 | 93 | await Profiles.findOneAndUpdate( 94 | { accountId: user.accountId }, 95 | update, 96 | { new: true } 97 | ).catch(async (err) => { 98 | return interaction.editReply({ content: "An error occurred while removing the cosmetic" }); 99 | }); 100 | 101 | const embed = new MessageEmbed() 102 | .setTitle("Cosmetic Removed") 103 | .setDescription(`Successfully removed for ${selectedUser} the cosmetic **` + cosmeticname + `**`) 104 | .setThumbnail(cosmeticimage) 105 | .setColor("GREEN") 106 | .setFooter({ 107 | text: "Reload Backend", 108 | iconURL: "https://i.imgur.com/2RImwlb.png" 109 | }) 110 | .setTimestamp(); 111 | await interaction.editReply({ embeds: [embed] }); 112 | } catch (err) { 113 | log.error("An error occurred:", err); 114 | interaction.editReply({ content: "An error occurred. Please try again later." }); 115 | } 116 | } 117 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/removevbucks.js: -------------------------------------------------------------------------------- 1 | const Users = require('../../../model/user'); 2 | const Profiles = require('../../../model/profiles'); 3 | const config = require('../../../Config/config.json'); 4 | const { MessageEmbed } = require('discord.js'); 5 | 6 | module.exports = { 7 | commandInfo: { 8 | name: "removevbucks", 9 | description: "Lets you change a user's amount of V-Bucks", 10 | options: [ 11 | { 12 | name: "user", 13 | description: "The user you want to change the V-Bucks of", 14 | required: true, 15 | type: 6 16 | }, 17 | { 18 | name: "vbucks", 19 | description: "The amount of V-Bucks you want to remove (Can be a negative number to add V-Bucks)", 20 | required: true, 21 | type: 4 22 | } 23 | ] 24 | }, 25 | execute: async (interaction) => { 26 | await interaction.deferReply({ ephemeral: true }); 27 | 28 | if (!config.moderators.includes(interaction.user.id)) { 29 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 30 | } 31 | 32 | const selectedUser = interaction.options.getUser('user'); 33 | const selectedUserId = selectedUser?.id; 34 | const user = await Users.findOne({ discordId: selectedUserId }); 35 | 36 | if (!user) { 37 | return interaction.editReply({ content: "That user does not own an account", ephemeral: true }); 38 | } 39 | 40 | const vbucks = parseInt(interaction.options.getInteger('vbucks')); 41 | if (isNaN(vbucks) || vbucks === 0) { 42 | return interaction.editReply({ content: "Invalid V-Bucks amount specified.", ephemeral: true }); 43 | } 44 | 45 | const filter = { accountId: user.accountId }; 46 | const updateCommonCore = { $inc: { 'profiles.common_core.items.Currency:MtxPurchased.quantity': -vbucks } }; 47 | const updateProfile0 = { $inc: { 'profiles.profile0.items.Currency:MtxPurchased.quantity': -vbucks } }; 48 | 49 | const updatedProfile = await Profiles.findOneAndUpdate(filter, updateCommonCore, { new: true }); 50 | if (!updatedProfile) { 51 | return interaction.editReply({ content: "That user does not own an account", ephemeral: true }); 52 | } 53 | 54 | await Profiles.updateOne(filter, updateProfile0); 55 | 56 | const profile0 = updatedProfile.profiles["profile0"]; 57 | const common_core = updatedProfile.profiles["common_core"]; 58 | 59 | const newQuantityCommonCore = common_core.items['Currency:MtxPurchased'].quantity; 60 | const newQuantityProfile0 = profile0.items['Currency:MtxPurchased'].quantity; 61 | 62 | common_core.rvn += 1; 63 | common_core.commandRevision += 1; 64 | 65 | await Profiles.updateOne(filter, { 66 | $set: { 67 | 'profiles.common_core': common_core, 68 | 'profiles.profile0.items.Currency:MtxPurchased.quantity': newQuantityProfile0 69 | } 70 | }); 71 | 72 | if (newQuantityCommonCore < 0 || newQuantityCommonCore >= 1000000) { 73 | return interaction.editReply({ 74 | content: "V-Bucks amount is out of valid range after the update.", 75 | ephemeral: true 76 | }); 77 | } 78 | 79 | const embed = new MessageEmbed() 80 | .setTitle("V-Bucks Updated") 81 | .setDescription(`Successfully removed **${vbucks}** V-Bucks from <@${selectedUserId}>`) 82 | .setThumbnail("https://i.imgur.com/yLbihQa.png") 83 | .setColor("GREEN") 84 | .setFooter({ 85 | text: "Reload Backend", 86 | iconURL: "https://i.imgur.com/2RImwlb.png" 87 | }) 88 | .setTimestamp(); 89 | 90 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 91 | 92 | return { 93 | profileRevision: common_core.rvn, 94 | profileCommandRevision: common_core.commandRevision, 95 | newQuantityCommonCore, 96 | newQuantityProfile0 97 | }; 98 | } 99 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/Admin/unban.js: -------------------------------------------------------------------------------- 1 | const User = require("../../../model/user.js"); 2 | const fs = require("fs"); 3 | const config = JSON.parse(fs.readFileSync("./Config/config.json").toString()); 4 | 5 | module.exports = { 6 | commandInfo: { 7 | name: "unban", 8 | description: "Unban a user from the backend by their username.", 9 | options: [ 10 | { 11 | name: "username", 12 | description: "Target username.", 13 | required: true, 14 | type: 3 15 | } 16 | ] 17 | }, 18 | execute: async (interaction) => { 19 | await interaction.deferReply({ ephemeral: true }); 20 | 21 | if (!config.moderators.includes(interaction.user.id)) { 22 | return interaction.editReply({ content: "You do not have moderator permissions.", ephemeral: true }); 23 | } 24 | 25 | const { options } = interaction; 26 | const targetUser = await User.findOne({ username_lower: (options.get("username").value).toLowerCase() }); 27 | 28 | if (!targetUser) return interaction.editReply({ content: "The account username you entered does not exist.", ephemeral: true }); 29 | else if (!targetUser.banned) return interaction.editReply({ content: "This account is already unbanned.", ephemeral: true }); 30 | 31 | await targetUser.updateOne({ $set: { banned: false } }); 32 | 33 | interaction.editReply({ content: `Successfully unbanned ${targetUser.username}`, ephemeral: true }); 34 | } 35 | } -------------------------------------------------------------------------------- /DiscordBot/commands/User/change-email.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const Users = require('../../../model/user.js'); 3 | const functions = require("../../../structs/functions.js"); 4 | 5 | module.exports = { 6 | commandInfo: { 7 | name: "change-email", 8 | description: "Allows you to change your email", 9 | options: [ 10 | { 11 | name: "email", 12 | description: "Your desired email.", 13 | required: true, 14 | type: 3 15 | } 16 | ] 17 | }, 18 | execute: async (interaction) => { 19 | await interaction.deferReply({ ephemeral: true }); 20 | 21 | const user = await Users.findOne({ discordId: interaction.user.id }); 22 | if (!user) { 23 | return interaction.editReply({ content: "You are not registered!", ephemeral: true }); 24 | } 25 | 26 | const plainEmail = interaction.options.getString('email'); 27 | 28 | const emailFilter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; 29 | if (!emailFilter.test(plainEmail)) { 30 | return interaction.editReply({ content: "You did not provide a valid email address!", ephemeral: true }); 31 | } 32 | 33 | const existingUser = await Users.findOne({ email: plainEmail }); 34 | if (existingUser) { 35 | return interaction.editReply({ content: "Email is already in use, please choose another one.", ephemeral: true }); 36 | } 37 | 38 | await user.updateOne({ $set: { email: plainEmail } }); 39 | 40 | const refreshTokenIndex = global.refreshTokens.findIndex(i => i.accountId == user.accountId); 41 | if (refreshTokenIndex != -1) global.refreshTokens.splice(refreshTokenIndex, 1); 42 | 43 | const accessTokenIndex = global.accessTokens.findIndex(i => i.accountId == user.accountId); 44 | if (accessTokenIndex != -1) { 45 | global.accessTokens.splice(accessTokenIndex, 1); 46 | 47 | const xmppClient = global.Clients.find(client => client.accountId == user.accountId); 48 | if (xmppClient) xmppClient.client.close(); 49 | } 50 | 51 | if (accessTokenIndex != -1 || refreshTokenIndex != -1) { 52 | await functions.UpdateTokens(); 53 | } 54 | 55 | const embed = new MessageEmbed() 56 | .setTitle("Email changed") 57 | .setDescription("Your account email has been changed.") 58 | .setColor("GREEN") 59 | .setFooter({ 60 | text: "Reload Backend", 61 | iconURL: "https://i.imgur.com/2RImwlb.png", 62 | }) 63 | .setTimestamp(); 64 | 65 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 66 | } 67 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/User/change-password.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const User = require("../../../model/user.js"); 3 | const bcrypt = require("bcrypt"); 4 | const functions = require("../../../structs/functions.js"); 5 | 6 | module.exports = { 7 | commandInfo: { 8 | name: "change-password", 9 | description: "Change your password.", 10 | options: [ 11 | { 12 | name: "password", 13 | description: "Your new password.", 14 | required: true, 15 | type: 3 16 | } 17 | ] 18 | }, 19 | execute: async (interaction) => { 20 | await interaction.deferReply({ ephemeral: true }); 21 | 22 | const user = await User.findOne({ discordId: interaction.user.id }); 23 | if (!user) return interaction.editReply({ content: "You do not have a registered account!", ephemeral: true }); 24 | 25 | const plainPassword = interaction.options.get("password").value; 26 | 27 | if (plainPassword.length >= 128) { 28 | return interaction.editReply({ content: "Your password must be less than 128 characters long.", ephemeral: true }); 29 | } 30 | if (plainPassword.length < 4) { 31 | return interaction.editReply({ content: "Your password must be at least 4 characters long.", ephemeral: true }); 32 | } 33 | 34 | const hashedPassword = await bcrypt.hash(plainPassword, 10); 35 | await user.updateOne({ $set: { password: hashedPassword } }); 36 | 37 | const refreshTokenIndex = global.refreshTokens.findIndex(i => i.accountId == user.accountId); 38 | if (refreshTokenIndex != -1) global.refreshTokens.splice(refreshTokenIndex, 1); 39 | 40 | const accessTokenIndex = global.accessTokens.findIndex(i => i.accountId == user.accountId); 41 | if (accessTokenIndex != -1) { 42 | global.accessTokens.splice(accessTokenIndex, 1); 43 | 44 | const xmppClient = global.Clients.find(client => client.accountId == user.accountId); 45 | if (xmppClient) xmppClient.client.close(); 46 | } 47 | 48 | if (accessTokenIndex != -1 || refreshTokenIndex != -1) { 49 | await functions.UpdateTokens(); 50 | } 51 | 52 | const embed = new MessageEmbed() 53 | .setTitle("Password changed") 54 | .setDescription("Your account password has been changed.") 55 | .setColor("GREEN") 56 | .setFooter({ 57 | text: "Reload Backend", 58 | iconURL: "https://i.imgur.com/2RImwlb.png", 59 | }) 60 | .setTimestamp(); 61 | 62 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /DiscordBot/commands/User/change-username.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const User = require("../../../model/user.js"); 3 | const Badwords = require("bad-words"); 4 | const functions = require("../../../structs/functions.js"); 5 | 6 | const badwords = new Badwords(); 7 | 8 | module.exports = { 9 | commandInfo: { 10 | name: "change-username", 11 | description: "Change your username.", 12 | options: [ 13 | { 14 | name: "username", 15 | description: "Your new username.", 16 | required: true, 17 | type: 3 18 | } 19 | ] 20 | }, 21 | execute: async (interaction) => { 22 | await interaction.deferReply({ ephemeral: true }); 23 | 24 | const user = await User.findOne({ discordId: interaction.user.id }); 25 | if (!user) 26 | return interaction.editReply({ content: "You are not registered!", ephemeral: true }); 27 | 28 | const username = interaction.options.getString('username'); 29 | if (badwords.isProfane(username)) { 30 | return interaction.editReply({ content: "Invalid username. Username must not contain inappropriate language." }); 31 | } 32 | 33 | const existingUser = await User.findOne({ username: username }); 34 | if (existingUser) { 35 | return interaction.editReply({ content: "Username already exists. Please choose a different one.", ephemeral: true }); 36 | } 37 | if (username.length >= 25) { 38 | return interaction.editReply({ content: "Your username must be less than 25 characters long.", ephemeral: true }); 39 | } 40 | if (username.length < 3) { 41 | return interaction.editReply({ content: "Your username must be at least 3 characters long.", ephemeral: true }); 42 | } 43 | 44 | await user.updateOne({ $set: { username: username, username_lower: username.toLowerCase() } }); 45 | 46 | const refreshTokenIndex = global.refreshTokens.findIndex(i => i.accountId == user.accountId); 47 | if (refreshTokenIndex != -1) global.refreshTokens.splice(refreshTokenIndex, 1); 48 | 49 | const accessTokenIndex = global.accessTokens.findIndex(i => i.accountId == user.accountId); 50 | if (accessTokenIndex != -1) { 51 | global.accessTokens.splice(accessTokenIndex, 1); 52 | 53 | const xmppClient = global.Clients.find(client => client.accountId == user.accountId); 54 | if (xmppClient) xmppClient.client.close(); 55 | } 56 | 57 | if (accessTokenIndex != -1 || refreshTokenIndex != -1) { 58 | await functions.UpdateTokens(); 59 | } 60 | 61 | const embed = new MessageEmbed() 62 | .setTitle("Username changed") 63 | .setDescription(`Your account username has been changed to **${username}**.`) 64 | .setColor("GREEN") 65 | .setFooter({ 66 | text: "Reload Backend", 67 | iconURL: "https://i.imgur.com/2RImwlb.png", 68 | }) 69 | .setTimestamp(); 70 | 71 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 72 | } 73 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/User/claimvbucks.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const Users = require('../../../model/user.js'); 3 | const Profiles = require('../../../model/profiles.js'); 4 | const log = require("../../../structs/log.js"); 5 | 6 | module.exports = { 7 | commandInfo: { 8 | name: "claimvbucks", 9 | description: "Claim your daily 250 V-Bucks" 10 | }, 11 | async execute(interaction) { 12 | try { 13 | await interaction.deferReply({ ephemeral: true }); 14 | 15 | const user = await Users.findOne({ discordId: interaction.user.id }); 16 | if (!user) { 17 | return interaction.followUp({ content: "You are not registered", ephemeral: true }); 18 | } 19 | 20 | const userProfile = await Profiles.findOne({ accountId: user?.accountId }); 21 | 22 | const lastClaimed = userProfile?.profiles?.lastVbucksClaim; 23 | if (lastClaimed && (Date.now() - new Date(lastClaimed).getTime() < 24 * 60 * 60 * 1000)) { 24 | const timeLeft = 24 - Math.floor((Date.now() - new Date(lastClaimed).getTime()) / (1000 * 60 * 60)); 25 | return interaction.followUp({ 26 | content: `You have already claimed your daily **V-Bucks.** Please wait the remainder: **${timeLeft} hours.**`, 27 | ephemeral: true 28 | }); 29 | } 30 | 31 | const filter = { accountId: user?.accountId }; 32 | const updateCommonCore = { $inc: { 'profiles.common_core.items.Currency:MtxPurchased.quantity': 250 } }; //250 is vbucks for day but u can change it 33 | const updateProfile0 = { $inc: { 'profiles.profile0.items.Currency:MtxPurchased.quantity': 250 } }; //250 is vbucks for day but u can change it 34 | 35 | const userUpdatedProfile = await Profiles.findOneAndUpdate( 36 | filter, 37 | { 38 | ...updateCommonCore, 39 | $set: { 'profiles.lastVbucksClaim': Date.now() } 40 | }, 41 | { new: true } 42 | ); 43 | 44 | await Profiles.updateOne(filter, updateProfile0); 45 | 46 | const common_core = userUpdatedProfile.profiles["common_core"]; 47 | const profile0 = userUpdatedProfile.profiles["profile0"]; 48 | 49 | const newQuantityCommonCore = common_core.items['Currency:MtxPurchased'].quantity; 50 | const newQuantityProfile0 = profile0.items['Currency:MtxPurchased'].quantity; 51 | 52 | common_core.rvn += 1; 53 | common_core.commandRevision += 1; 54 | 55 | await Profiles.updateOne(filter, { 56 | $set: { 57 | 'profiles.common_core': common_core, 58 | 'profiles.profile0.items.Currency:MtxPurchased.quantity': newQuantityProfile0 59 | } 60 | }); 61 | 62 | const embed = new MessageEmbed() 63 | .setTitle("Daily V-Bucks Claimed!") 64 | .setDescription(`You have claimed your daily **250 V-Bucks**!`) 65 | .setThumbnail("https://i.imgur.com/yLbihQa.png") 66 | .setColor("#1eff00") 67 | .setFooter({ 68 | text: "Reload Backend", 69 | iconURL: "https://i.imgur.com/2RImwlb.png" 70 | }); 71 | 72 | await interaction.followUp({ 73 | embeds: [embed], 74 | ephemeral: true 75 | }); 76 | 77 | return { 78 | profileRevision: common_core.rvn, 79 | profileCommandRevision: common_core.commandRevision, 80 | newQuantityCommonCore, 81 | newQuantityProfile0 82 | }; 83 | 84 | } catch (error) { 85 | log.error(error); 86 | } 87 | } 88 | }; -------------------------------------------------------------------------------- /DiscordBot/commands/User/create.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const User = require("../../../model/user.js") 3 | const functions = require("../../../structs/functions.js"); 4 | 5 | module.exports = { 6 | commandInfo: { 7 | name: "create", 8 | description: "Creates an account on Reload Backend.", 9 | options: [ 10 | { 11 | name: "email", 12 | description: "Your email.", 13 | required: true, 14 | type: 3 15 | }, 16 | { 17 | name: "username", 18 | description: "Your username.", 19 | required: true, 20 | type: 3 21 | }, 22 | { 23 | name: "password", 24 | description: "Your password.", 25 | required: true, 26 | type: 3 27 | } 28 | ], 29 | }, 30 | execute: async (interaction) => { 31 | await interaction.deferReply({ ephemeral: true }); 32 | 33 | const { options } = interaction; 34 | 35 | const discordId = interaction.user.id; 36 | const email = options.get("email").value; 37 | const username = options.get("username").value; 38 | const password = options.get("password").value; 39 | 40 | const plainEmail = options.get('email').value; 41 | const plainUsername = options.get('username').value; 42 | 43 | const existingEmail = await User.findOne({ email: plainEmail }); 44 | const existingUser = await User.findOne({ username: plainUsername }); 45 | 46 | const emailFilter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; 47 | if (!emailFilter.test(email)) { 48 | return interaction.editReply({ content: "You did not provide a valid email address!", ephemeral: true }); 49 | } 50 | if (existingEmail) { 51 | return interaction.editReply({ content: "Email is already in use, please choose another one.", ephemeral: true }); 52 | } 53 | if (existingUser) { 54 | return interaction.editReply({ content: "Username already exists. Please choose a different one.", ephemeral: true }); 55 | } 56 | if (username.length >= 25) { 57 | return interaction.editReply({ content: "Your username must be less than 25 characters long.", ephemeral: true }); 58 | } 59 | if (username.length < 3) { 60 | return interaction.editReply({ content: "Your username must be at least 3 characters long.", ephemeral: true }); 61 | } 62 | if (password.length >= 128) { 63 | return interaction.editReply({ content: "Your password must be less than 128 characters long.", ephemeral: true }); 64 | } 65 | if (password.length < 4) { 66 | return interaction.editReply({ content: "Your password must be at least 4 characters long.", ephemeral: true }); 67 | } 68 | 69 | await functions.registerUser(discordId, username, email, password).then(resp => { 70 | let embed = new MessageEmbed() 71 | .setColor(resp.status >= 400 ? "#ff0000" : "#56ff00") 72 | .setThumbnail(interaction.user.avatarURL({ format: 'png', dynamic: true, size: 256 })) 73 | .addFields({ 74 | name: "Message", 75 | value: "Successfully created an account.", 76 | }, { 77 | name: "Username", 78 | value: username, 79 | }, { 80 | name: "Discord Tag", 81 | value: interaction.user.tag, 82 | }) 83 | .setTimestamp() 84 | .setFooter({ 85 | text: "Reload Backend", 86 | iconURL: "https://i.imgur.com/2RImwlb.png" 87 | }) 88 | 89 | if (resp.status >= 400) return interaction.editReply({ embeds: [embed], ephemeral: true }); 90 | 91 | (interaction.channel ? interaction.channel : interaction.user).send({ embeds: [embed] }); 92 | interaction.editReply({ content: "You successfully created an account!", ephemeral: true }); 93 | }); 94 | } 95 | } -------------------------------------------------------------------------------- /DiscordBot/commands/User/details.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const User = require("../../../model/user.js"); 3 | const Profiles = require('../../../model/profiles.js'); 4 | 5 | module.exports = { 6 | commandInfo: { 7 | name: "details", 8 | description: "Retrieves your account info." 9 | }, 10 | execute: async (interaction) => { 11 | await interaction.deferReply({ ephemeral: true }); 12 | 13 | const user = await User.findOne({ discordId: interaction.user.id }).lean(); 14 | const vbucksamount = await Profiles.findOne({ accountId: user?.accountId }); 15 | const currency = vbucksamount?.profiles.common_core.items["Currency:MtxPurchased"].quantity; 16 | if (!user) return interaction.editReply({ content: "You do not have a registered account!", ephemeral: true }); 17 | 18 | let onlineStatus = global.Clients.some(i => i.accountId == user.accountId); 19 | 20 | let embed = new MessageEmbed() 21 | .setColor("GREEN") 22 | .setDescription("These are your account details") 23 | .setFields( 24 | { name: 'Username:', value: user.username }, 25 | { name: 'Email:', value: `${user.email}` }, 26 | { name: "Online:", value: `${onlineStatus ? "Yes" : "No"}` }, 27 | { name: "Banned:", value: `${user.banned ? "Yes" : "No"}` }, 28 | { name: 'V-Bucks:', value: `${currency} V-Bucks` }, 29 | { name: "Account ID:", value: user.accountId }) 30 | .setTimestamp() 31 | .setThumbnail(interaction.user.avatarURL()) 32 | .setFooter({ 33 | text: "Reload Backend", 34 | iconURL: "https://i.imgur.com/2RImwlb.png" 35 | }) 36 | 37 | interaction.editReply({ embeds: [embed], ephemeral: true }); 38 | } 39 | } -------------------------------------------------------------------------------- /DiscordBot/commands/User/exchange-code.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const User = require("../../../model/user.js"); 3 | const functions = require("../../../structs/functions.js"); 4 | 5 | module.exports = { 6 | commandInfo: { 7 | name: "exchange-code", 8 | description: "Generates an exchange code for login. (One time use and expires after 5 mins if unused)." 9 | }, 10 | execute: async (interaction) => { 11 | await interaction.deferReply({ ephemeral: true }); 12 | 13 | const user = await User.findOne({ discordId: interaction.user.id }).lean(); 14 | if (!user) return interaction.editReply({ content: "You do not have a registered account!", ephemeral: true }); 15 | 16 | let exchange_code = functions.MakeID().replace(/-/ig, ""); 17 | 18 | global.exchangeCodes.push({ 19 | accountId: user.accountId, 20 | exchange_code: exchange_code, 21 | creatingClientId: "" 22 | }); 23 | 24 | setTimeout(() => { 25 | let exchangeCode = global.exchangeCodes.findIndex(i => i.exchange_code == exchange_code); 26 | 27 | if (exchangeCode != -1) global.exchangeCodes.splice(exchangeCode, 1); 28 | }, 300000) // remove exchange code in 5 minutes if unused 29 | 30 | let embed = new MessageEmbed() 31 | .setColor("#56ff00") 32 | .setAuthor({ name: interaction.user.tag, iconURL: interaction.user.avatarURL() }) 33 | .setFields( 34 | { name: "Exchange Code", value: exchange_code } 35 | ) 36 | .setTimestamp() 37 | .setFooter({ 38 | text: "Reload Backend", 39 | iconURL: "https://i.imgur.com/2RImwlb.png" 40 | }) 41 | 42 | interaction.editReply({ content: "Successfully generated an exchange code.", embeds: [embed], ephemeral: true }); 43 | } 44 | } -------------------------------------------------------------------------------- /DiscordBot/commands/User/lookup.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const Users = require("../../../model/user.js"); 3 | 4 | module.exports = { 5 | commandInfo: { 6 | name: "lookup", 7 | description: "Search for a Discord user\'s ID by providing their in-game username.", 8 | options: [ 9 | { 10 | name: "user", 11 | description: "Target username.", 12 | required: true, 13 | type: 3 14 | } 15 | ] 16 | }, 17 | execute: async (interaction) => { 18 | 19 | await interaction.deferReply({ ephemeral: true }); 20 | 21 | const { options } = interaction; 22 | 23 | const user = await Users.findOne({ username_lower: (options.get("user").value).toLowerCase() }).lean(); 24 | if (!user) return interaction.editReply({ content: "The account username you entered does not exist.", ephemeral: true }); 25 | 26 | let onlineStatus = global.Clients.some(i => i.accountId == user.accountId); 27 | 28 | let embed = new MessageEmbed() 29 | .setColor("GREEN") 30 | .setDescription(`**User Information:**\n- **Discord User:** <@${user.discordId}>\n- **DiscordID:** ${user.discordId}\n- **In-Game Username:** ${user.username}\n- **Banned:** ${user.banned ? "Yes" : "No"}\n- **Online:** ${onlineStatus ? "Yes" : "No"}`) 31 | .setFooter({ 32 | text: "Reload Backend", 33 | iconURL: "https://i.imgur.com/2RImwlb.png" 34 | }) 35 | 36 | interaction.editReply({ embeds: [embed], ephemeral: true }); 37 | } 38 | } -------------------------------------------------------------------------------- /DiscordBot/commands/User/sign-out-of-all-sessions.js: -------------------------------------------------------------------------------- 1 | const User = require("../../../model/user.js"); 2 | const functions = require("../../../structs/functions.js"); 3 | 4 | module.exports = { 5 | commandInfo: { 6 | name: "sign-out-of-all-sessions", 7 | description: "Signs you out if you have an active session." 8 | }, 9 | execute: async (interaction) => { 10 | await interaction.deferReply({ ephemeral: true }); 11 | 12 | const targetUser = await User.findOne({ discordId: interaction.user.id }).lean(); 13 | if (!targetUser) return interaction.editReply({ content: "You do not have a registered account!", ephemeral: true }); 14 | 15 | let refreshToken = global.refreshTokens.findIndex(i => i.accountId == targetUser.accountId); 16 | if (refreshToken != -1) global.refreshTokens.splice(refreshToken, 1); 17 | 18 | let accessToken = global.accessTokens.findIndex(i => i.accountId == targetUser.accountId); 19 | if (accessToken != -1) { 20 | global.accessTokens.splice(accessToken, 1); 21 | 22 | let xmppClient = global.Clients.find(client => client.accountId == targetUser.accountId); 23 | if (xmppClient) xmppClient.client.close(); 24 | } 25 | 26 | if (accessToken != -1 || refreshToken != -1) { 27 | functions.UpdateTokens(); 28 | 29 | return interaction.editReply({ content: `Successfully signed out of all sessions!`, ephemeral: true }); 30 | } 31 | 32 | interaction.editReply({ content: `You have no current active sessions.`, ephemeral: true }); 33 | } 34 | } -------------------------------------------------------------------------------- /DiscordBot/commands/User/vbucksamount.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const Profiles = require('../../../model/profiles.js'); 3 | const Users = require('../../../model/user.js'); 4 | 5 | module.exports = { 6 | commandInfo: { 7 | name: "vbucksamount", 8 | description: "Displays your current V-Bucks balance.", 9 | }, 10 | execute: async (interaction) => { 11 | 12 | await interaction.deferReply({ ephemeral: true }) 13 | 14 | const currentuser = await Users.findOne({ discordId: interaction.user.id }); 15 | const vbucksamount = await Profiles.findOne({ accountId: currentuser?.accountId }); 16 | const currency = vbucksamount?.profiles.common_core.items["Currency:MtxPurchased"].quantity; 17 | if (!currentuser) 18 | { 19 | return interaction.editReply({ content: "You are not registered!", ephemeral: true }); 20 | } 21 | const embed = new MessageEmbed() 22 | .setTitle("V-Bucks Count:") 23 | .setDescription(`You currently have **` + currency + " V-Bucks** in your Account!") 24 | .setTimestamp() 25 | .setThumbnail('https://i.imgur.com/yLbihQa.png') 26 | .setFooter({ 27 | text: "Reload Backend", 28 | iconURL: "https://i.imgur.com/2RImwlb.png" 29 | }) 30 | .setColor("WHITE") 31 | await interaction.editReply({ embeds: [embed], ephemeral: true }); 32 | } 33 | } -------------------------------------------------------------------------------- /DiscordBot/index.js: -------------------------------------------------------------------------------- 1 | const { Client, Intents, MessageEmbed } = require("discord.js"); 2 | const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MEMBERS, Intents.FLAGS.GUILD_BANS] }); 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const config = JSON.parse(fs.readFileSync("./Config/config.json").toString()); 6 | const log = require("../structs/log.js"); 7 | const Users = require("../model/user.js"); 8 | 9 | client.once("ready", () => { 10 | log.bot("Bot is up and running!"); 11 | 12 | if (config.bEnableBackendStatus) { 13 | if (!config.bBackendStatusChannelId || config.bBackendStatusChannelId.trim() === "") { 14 | log.error("The channel ID has not been set in config.json for bEnableBackendStatus."); 15 | } else { 16 | const channel = client.channels.cache.get(config.bBackendStatusChannelId); 17 | if (!channel) { 18 | log.error(`Cannot find the channel with ID ${config.bBackendStatusChannelId}`); 19 | } else { 20 | const embed = new MessageEmbed() 21 | .setTitle("Backend Online") 22 | .setDescription("Reload Backend is now online") 23 | .setColor("GREEN") 24 | .setThumbnail("https://i.imgur.com/2RImwlb.png") 25 | .setFooter({ 26 | text: "Reload Backend", 27 | iconURL: "https://i.imgur.com/2RImwlb.png", 28 | }) 29 | .setTimestamp(); 30 | 31 | channel.send({ embeds: [embed] }).catch(err => { 32 | log.error(err); 33 | }); 34 | } 35 | } 36 | } 37 | 38 | if (config.discord.bEnableInGamePlayerCount) { 39 | function updateBotStatus() { 40 | if (global.Clients && Array.isArray(global.Clients)) { 41 | client.user.setActivity(`${global.Clients.length} player(s)`, { type: "WATCHING" }); 42 | } 43 | } 44 | 45 | updateBotStatus(); 46 | setInterval(updateBotStatus, 10000); 47 | } 48 | 49 | let commands = client.application.commands; 50 | 51 | const loadCommands = (dir) => { 52 | fs.readdirSync(dir).forEach(file => { 53 | const filePath = path.join(dir, file); 54 | if (fs.lstatSync(filePath).isDirectory()) { 55 | loadCommands(filePath); 56 | } else if (file.endsWith(".js")) { 57 | const command = require(filePath); 58 | commands.create(command.commandInfo); 59 | } 60 | }); 61 | }; 62 | 63 | loadCommands(path.join(__dirname, "commands")); 64 | }); 65 | 66 | client.on("interactionCreate", async interaction => { 67 | if (!interaction.isApplicationCommand()) return; 68 | 69 | const executeCommand = (dir, commandName) => { 70 | const commandPath = path.join(dir, commandName + ".js"); 71 | if (fs.existsSync(commandPath)) { 72 | require(commandPath).execute(interaction); 73 | return true; 74 | } 75 | const subdirectories = fs.readdirSync(dir).filter(subdir => fs.lstatSync(path.join(dir, subdir)).isDirectory()); 76 | for (const subdir of subdirectories) { 77 | if (executeCommand(path.join(dir, subdir), commandName)) { 78 | return true; 79 | } 80 | } 81 | return false; 82 | }; 83 | 84 | executeCommand(path.join(__dirname, "commands"), interaction.commandName); 85 | }); 86 | 87 | client.on("guildBanAdd", async (ban) => { 88 | if (!config.bEnableCrossBans) 89 | return; 90 | 91 | const memberBan = await ban.fetch(); 92 | 93 | if (memberBan.user.bot) 94 | return; 95 | 96 | const userData = await Users.findOne({ discordId: memberBan.user.id }); 97 | 98 | if (userData && userData.banned !== true) { 99 | await userData.updateOne({ $set: { banned: true } }); 100 | 101 | let refreshToken = global.refreshTokens.findIndex(i => i.accountId == userData.accountId); 102 | 103 | if (refreshToken != -1) 104 | global.refreshTokens.splice(refreshToken, 1); 105 | let accessToken = global.accessTokens.findIndex(i => i.accountId == userData.accountId); 106 | 107 | if (accessToken != -1) { 108 | global.accessTokens.splice(accessToken, 1); 109 | let xmppClient = global.Clients.find(client => client.accountId == userData.accountId); 110 | if (xmppClient) 111 | xmppClient.client.close(); 112 | } 113 | 114 | if (accessToken != -1 || refreshToken != -1) { 115 | await functions.UpdateTokens(); 116 | } 117 | 118 | log.debug(`user ${memberBan.user.username} (ID: ${memberBan.user.id}) was banned on the discord and also in the game (Cross Ban active).`); 119 | } 120 | }); 121 | 122 | client.on("guildBanRemove", async (ban) => { 123 | if (!config.bEnableCrossBans) 124 | return; 125 | 126 | if (ban.user.bot) 127 | return; 128 | 129 | const userData = await Users.findOne({ discordId: ban.user.id }); 130 | 131 | if (userData && userData.banned === true) { 132 | await userData.updateOne({ $set: { banned: false } }); 133 | 134 | log.debug(`User ${ban.user.username} (ID: ${ban.user.id}) is now unbanned.`); 135 | } 136 | }); 137 | 138 | //AntiCrash System 139 | client.on("error", (err) => { 140 | console.log("Discord API Error:", err); 141 | }); 142 | 143 | process.on("unhandledRejection", (reason, p) => { 144 | console.log("Unhandled promise rejection:", reason, p); 145 | }); 146 | 147 | process.on("uncaughtException", (err, origin) => { 148 | console.log("Uncaught Exception:", err, origin); 149 | }); 150 | 151 | process.on("uncaughtExceptionMonitor", (err, origin) => { 152 | console.log("Uncaught Exception Monitor:", err, origin); 153 | }); 154 | 155 | client.login(config.discord.bot_token); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2024-2025, Project Reload 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Website/Data/Images/reload.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/Website/Data/Images/reload.ico -------------------------------------------------------------------------------- /Website/Data/css/accountExists.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #121212; 3 | color: #ffffff; 4 | font-family: Arial, sans-serif; 5 | margin: 0; 6 | padding: 0; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | height: 100vh; 11 | transition: background-color 0.5s, color 0.5s; 12 | } 13 | 14 | .container, .register-container { 15 | text-align: center; 16 | } 17 | 18 | a { 19 | color: #1e90ff; 20 | text-decoration: none; 21 | transition: color 0.5s; 22 | } 23 | 24 | a:hover { 25 | text-decoration: underline; 26 | } 27 | 28 | .theme-toggle-button { 29 | position: fixed; 30 | bottom: 20px; 31 | right: 20px; 32 | width: 50px; 33 | height: 50px; 34 | background-color: #1e90ff; 35 | border: none; 36 | border-radius: 50%; 37 | cursor: pointer; 38 | transition: background-color 0.5s, transform 0.2s; 39 | } 40 | 41 | .theme-toggle-button.light-mode { 42 | background-color: #ff6347; 43 | } 44 | 45 | .theme-toggle-button:hover { 46 | transform: scale(1.1); 47 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 48 | } 49 | 50 | body.light-mode { 51 | background-color: #f0f0f0; 52 | color: #000000; 53 | } 54 | 55 | body.light-mode a { 56 | color: #007bff; 57 | } -------------------------------------------------------------------------------- /Website/Data/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | height: 100vh; 6 | margin: 0; 7 | padding: 0; 8 | font-family: Arial, sans-serif; 9 | transition: background-color 0.5s, color 0.5s; 10 | } 11 | 12 | body.dark-theme { 13 | background-color: #121212; 14 | color: #ffffff; 15 | } 16 | 17 | body.dark-theme .register-container { 18 | background-color: #1e1e1e; 19 | color: #ffffff; 20 | position: relative; 21 | border-radius: 20px; 22 | padding: 30px 40px; 23 | width: 320px; 24 | text-align: center; 25 | overflow: hidden; 26 | } 27 | 28 | body.light-theme { 29 | background-color: #f0f0f0; 30 | color: #000000; 31 | } 32 | 33 | body.light-theme .register-container { 34 | background-color: white; 35 | color: #000000; 36 | position: relative; 37 | border-radius: 20px; 38 | padding: 30px 40px; 39 | width: 320px; 40 | text-align: center; 41 | overflow: hidden; 42 | } 43 | 44 | .register-container { 45 | position: relative; 46 | padding: 30px 40px; 47 | border-radius: 20px; 48 | width: 320px; 49 | text-align: center; 50 | overflow: hidden; 51 | } 52 | 53 | .register-container .input-container { 54 | position: relative; 55 | width: 100%; 56 | margin: 12px 0; 57 | } 58 | 59 | .register-container input { 60 | width: 100%; 61 | padding: 12px; 62 | border-radius: 5px; 63 | border: 1px solid #ccc; 64 | outline: none; 65 | transition: border-color 0.3s, box-shadow 0.3s; 66 | box-sizing: border-box; 67 | } 68 | 69 | .register-container input:focus { 70 | border-color: #4CAF50; 71 | box-shadow: 0 0 8px rgba(76, 175, 80, 0.8); 72 | } 73 | 74 | .register-container input[type="submit"] { 75 | background-color: #4CAF50; 76 | color: white; 77 | border: none; 78 | cursor: pointer; 79 | font-weight: bold; 80 | text-transform: uppercase; 81 | letter-spacing: 1px; 82 | margin-top: 20px; 83 | transition: background-color 0.3s; 84 | width: 100%; 85 | } 86 | 87 | .register-container input[type="submit"]:hover { 88 | background-color: #45a049; 89 | } 90 | 91 | .toggle-password { 92 | position: absolute; 93 | right: 10px; 94 | top: 50%; 95 | transform: translateY(-50%); 96 | cursor: pointer; 97 | font-size: 18px; 98 | color: #999; 99 | } 100 | 101 | .toggle-password:hover { 102 | color: #4CAF50; 103 | } 104 | 105 | .theme-toggle { 106 | position: fixed; 107 | bottom: 20px; 108 | right: 20px; 109 | width: 50px; 110 | height: 50px; 111 | border: none; 112 | border-radius: 50%; 113 | cursor: pointer; 114 | transition: background-color 0.5s, border 0.5s; 115 | background-color: #ffffff; 116 | border: 2px solid #000000; 117 | } 118 | 119 | body.light-theme .theme-toggle { 120 | background-color: #000000; 121 | border: 2px solid #ffffff; 122 | } 123 | 124 | .signin-text { 125 | color: gray; 126 | font-size: 14px; 127 | margin-bottom: 20px; 128 | } 129 | 130 | .error-message { 131 | position: fixed; 132 | top: 20px; 133 | left: 50%; 134 | transform: translateX(-50%); 135 | z-index: 1000; 136 | display: flex; 137 | justify-content: space-between; 138 | align-items: center; 139 | background-color: #ff4c4c; 140 | color: white; 141 | padding: 15px 20px; 142 | border-radius: 5px; 143 | opacity: 0; 144 | transition: opacity 0.5s ease-in-out, transform 0.5s ease-in-out; 145 | } 146 | 147 | .error-message.visible { 148 | opacity: 1; 149 | transform: translateX(-50%) translateY(0); 150 | } 151 | 152 | .error-message.hidden { 153 | opacity: 0; 154 | transform: translateX(-50%) translateY(-20px); 155 | } 156 | 157 | .close-error { 158 | cursor: pointer; 159 | font-weight: bold; 160 | margin-left: 10px; 161 | } 162 | 163 | .close-error:hover { 164 | color: #ccc; 165 | } -------------------------------------------------------------------------------- /Website/Data/html/accountExists.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Account Exists 7 | 8 | 9 | 10 | 11 |
12 |

Account Exists

13 |

The Discord user already has an account.

14 |
15 | 16 | 17 | 18 | 21 | 22 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Website/Data/html/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Register 7 | 8 | 9 | 10 | 11 |
12 |

REGISTER

13 |

Sign In

14 | 15 | 19 | 20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 | 👁️ 30 |
31 | 32 |
33 |
34 | 35 | 36 | 37 | 40 | 41 | 123 | 124 | -------------------------------------------------------------------------------- /Website/Data/js/accountExists.js: -------------------------------------------------------------------------------- 1 | module.exports = (req, res) => { 2 | const username = req.query.username; 3 | res.send(` 4 | 5 | 6 |

Account Exists

7 |

The Discord user ${username} already has an account.

8 | Go to Home 9 | 10 | 11 | `); 12 | }; -------------------------------------------------------------------------------- /Website/Data/js/oauthCallback.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const log = require("../../../structs/log.js"); 3 | const User = require('../../../model/user.js'); 4 | 5 | module.exports = (DISCORD_API_URL, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI) => async (req, res) => { 6 | const code = req.query.code; 7 | 8 | if (!code) { 9 | return res.json({ 10 | "error": "Invalid oauth code provided." 11 | }) 12 | } 13 | 14 | try { 15 | const tokenResponse = await axios.post(`${DISCORD_API_URL}/oauth2/token`, new URLSearchParams({ 16 | client_id: CLIENT_ID, 17 | client_secret: CLIENT_SECRET, 18 | grant_type: 'authorization_code', 19 | code, 20 | redirect_uri: REDIRECT_URI, 21 | scope: 'identify' 22 | })); 23 | 24 | const accessToken = tokenResponse.data.access_token; 25 | 26 | const userResponse = await axios.get(`${DISCORD_API_URL}/users/@me`, { 27 | headers: { 28 | Authorization: `Bearer ${accessToken}` 29 | } 30 | }); 31 | 32 | const discordId = userResponse.data.id; 33 | const username = userResponse.data.username; 34 | 35 | const userExists = await User.findOne({ discordId: discordId }); 36 | 37 | if (userExists) { 38 | res.redirect(`/account-exists?discordId=${discordId}&username=${username}`); 39 | } else { 40 | res.redirect(`/register?discordId=${discordId}&username=${username}`); 41 | } 42 | } catch (err) { 43 | log.error('Error during Discord OAuth2:', err); 44 | return res.json({ 45 | "error": "Authentication failed" 46 | }) 47 | } 48 | }; -------------------------------------------------------------------------------- /Website/Data/js/registerUser.js: -------------------------------------------------------------------------------- 1 | const functions = require('../../../structs/functions'); 2 | const log = require("../../../structs/log.js"); 3 | 4 | module.exports = async (req, res) => { 5 | const { discordId, username, email, password } = req.body; 6 | 7 | try { 8 | const result = await functions.registerUser(discordId, username, email, password); 9 | 10 | if (result.status === 200) { 11 | res.json({ success: true, message: result.message }); 12 | } else { 13 | res.json({ success: false, message: result.message }); 14 | } 15 | } catch (error) { 16 | log.error('Error registering user:', error); 17 | res.json({ success: false, message: 'Failed to create account.' }); 18 | } 19 | }; -------------------------------------------------------------------------------- /Website/website.js: -------------------------------------------------------------------------------- 1 | module.exports = function(websiteApp) { 2 | const express = require("express"); 3 | const path = require("path"); 4 | const config = require("../Config/config.json"); 5 | 6 | const DISCORD_API_URL = 'https://discord.com/api'; 7 | const CLIENT_ID = config.Website.clientId; 8 | const CLIENT_SECRET = config.Website.clientSecret; 9 | const REDIRECT_URI = config.Website.redirectUri.replace("${websiteport}", config.Website.websiteport); 10 | 11 | websiteApp.use(express.json()); 12 | websiteApp.use(express.urlencoded({ extended: true })); 13 | 14 | websiteApp.use('/Images', express.static(path.join(__dirname, './Data/Images'))); 15 | websiteApp.use('/css', express.static(path.join(__dirname, './Data/css'))); 16 | websiteApp.use('/html', express.static(path.join(__dirname, './Data/html'))); 17 | 18 | websiteApp.get('/', (req, res) => { 19 | res.redirect('/login'); 20 | }); 21 | 22 | websiteApp.get('/login', (req, res) => { 23 | const authURL = `${DISCORD_API_URL}/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=identify`; 24 | 25 | res.redirect(authURL); 26 | }); 27 | 28 | const oauthCallback = require('./Data/js/oauthCallback')(DISCORD_API_URL, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI); 29 | websiteApp.get('/oauth2/callback', oauthCallback); 30 | 31 | websiteApp.post('/register-user', require('./Data/js/registerUser.js')); 32 | 33 | websiteApp.get('/register', (req, res) => { 34 | res.sendFile(path.join(__dirname, './Data/html/register.html')); 35 | }); 36 | 37 | websiteApp.get('/account-exists', (req, res) => { 38 | res.sendFile(path.join(__dirname, './Data/html/accountExists.html')); 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /install_packages.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | title Reload Backend Package Installer 3 | 4 | npm i 5 | npm install express 6 | npm install ws 7 | pause -------------------------------------------------------------------------------- /matchmaker/matchmaker.js: -------------------------------------------------------------------------------- 1 | const functions = require("../structs/functions.js"); 2 | 3 | module.exports = async (ws) => { 4 | // create hashes 5 | const ticketId = functions.MakeID().replace(/-/ig, ""); 6 | const matchId = functions.MakeID().replace(/-/ig, ""); 7 | const sessionId = functions.MakeID().replace(/-/ig, ""); 8 | 9 | Connecting(); 10 | await functions.sleep(800); 11 | Waiting(); 12 | await functions.sleep(1000); 13 | Queued(); 14 | await functions.sleep(4000); 15 | SessionAssignment(); 16 | await functions.sleep(2000); 17 | Join(); 18 | 19 | function Connecting() { 20 | ws.send(JSON.stringify({ 21 | "payload": { 22 | "state": "Connecting" 23 | }, 24 | "name": "StatusUpdate" 25 | })); 26 | } 27 | 28 | function Waiting() { 29 | ws.send(JSON.stringify({ 30 | "payload": { 31 | "totalPlayers": 1, 32 | "connectedPlayers": 1, 33 | "state": "Waiting" 34 | }, 35 | "name": "StatusUpdate" 36 | })); 37 | } 38 | 39 | function Queued() { 40 | ws.send(JSON.stringify({ 41 | "payload": { 42 | "ticketId": ticketId, 43 | "queuedPlayers": 0, 44 | "estimatedWaitSec": 0, 45 | "status": {}, 46 | "state": "Queued" 47 | }, 48 | "name": "StatusUpdate" 49 | })); 50 | } 51 | 52 | function SessionAssignment() { 53 | ws.send(JSON.stringify({ 54 | "payload": { 55 | "matchId": matchId, 56 | "state": "SessionAssignment" 57 | }, 58 | "name": "StatusUpdate" 59 | })); 60 | } 61 | 62 | function Join() { 63 | ws.send(JSON.stringify({ 64 | "payload": { 65 | "matchId": matchId, 66 | "sessionId": sessionId, 67 | "joinDelaySec": 1 68 | }, 69 | "name": "Play" 70 | })); 71 | } 72 | } -------------------------------------------------------------------------------- /model/friends.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const FriendsSchema = new mongoose.Schema( 4 | { 5 | created: { type: Date, required: true }, 6 | accountId: { type: String, required: true, unique: true }, 7 | list: { type: Object, default: { accepted: [], incoming: [], outgoing: [], blocked: [] } } 8 | }, 9 | { 10 | collection: "friends" 11 | } 12 | ) 13 | 14 | const model = mongoose.model('FriendsSchema', FriendsSchema); 15 | 16 | module.exports = model; -------------------------------------------------------------------------------- /model/mmcodes.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const MMCodesSchema = new mongoose.Schema({ 4 | created: { type: Date, required: true }, 5 | code: { type: String, required: true }, 6 | code_lower: { type: String, required: true }, 7 | ip: { type: String, required: true }, 8 | port: { type: Number, required: true }, 9 | }, { 10 | collection: "mmcodes" 11 | }); 12 | 13 | const model = mongoose.model('MMCodeSchema', MMCodesSchema); 14 | 15 | module.exports = model; -------------------------------------------------------------------------------- /model/profiles.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const ProfilesSchema = new mongoose.Schema( 4 | { 5 | created: { type: Date, required: true }, 6 | accountId: { type: String, required: true, unique: true }, 7 | profiles: { type: Object, required: true } 8 | }, 9 | { 10 | collection: "profiles" 11 | } 12 | ) 13 | 14 | const model = mongoose.model('ProfilesSchema', ProfilesSchema); 15 | 16 | module.exports = model; -------------------------------------------------------------------------------- /model/saccodes.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const SACCodesSchema = new mongoose.Schema( 4 | { 5 | created: { type: Date, required: true }, 6 | createdby: { type: String, required: true }, 7 | owneraccountId: { type: String, required: true }, 8 | code: { type: String, required: true }, 9 | code_lower: { type: String, required: true }, 10 | code_higher: { type: String, required: true }, 11 | }, 12 | { 13 | collection: "SACcodes" 14 | } 15 | ); 16 | 17 | const model = mongoose.model('SACCodeSchema', SACCodesSchema); 18 | 19 | module.exports = model; -------------------------------------------------------------------------------- /model/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const UserSchema = new mongoose.Schema( 4 | { 5 | created: { type: Date, required: true }, 6 | banned: { type: Boolean, default: false }, 7 | discordId: { type: String, default: null, unique: true, sparse: true }, 8 | accountId: { type: String, required: true, unique: true }, 9 | username: { type: String, required: true, unique: true }, 10 | username_lower: { type: String, required: true, unique: true }, 11 | email: { type: String, required: true, unique: true }, 12 | password: { type: String, required: true }, 13 | matchmakingId: { type: String, required: true, unique: true}, 14 | isServer: { type: Boolean, default: false}, 15 | currentSACCode: { type: String, default: null } 16 | }, 17 | { 18 | collection: "users" 19 | } 20 | ) 21 | 22 | const model = mongoose.model('UserSchema', UserSchema); 23 | 24 | module.exports = model; -------------------------------------------------------------------------------- /model/userstats.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const UserStatsSchema = new mongoose.Schema( 4 | { 5 | created: { type: Date, required: true }, 6 | accountId: { type: String, required: true, unique: true }, 7 | solo: { type: Object, required: true }, 8 | duo: { type: Object, required: true }, 9 | trio: { type: Object, required: true }, 10 | squad: { type: Object, required: true }, 11 | ltm: { type: Object, required: true }, 12 | }, 13 | { 14 | collection: "userstats" 15 | } 16 | ) 17 | 18 | const model = mongoose.model("UserStatsSchema", UserStatsSchema); 19 | 20 | module.exports = model; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reloadbackend", 3 | "version": "1.1.8", 4 | "description": "Created by Burlone, This is a modded backend, all main backend credits to lawin", 5 | "main": "index.js", 6 | "dependencies": { 7 | "axios": "^1.7.7", 8 | "bad-words": "^3.0.4", 9 | "bcrypt": "^5.1.0", 10 | "destr": "^2.0.3", 11 | "discord.js": "^13.7.0", 12 | "dotenv": "^16.0.3", 13 | "express": "^4.21.1", 14 | "express-rate-limit": "^6.7.0", 15 | "ioredis": "^5.4.1", 16 | "jsonwebtoken": "^8.5.1", 17 | "keyv": "^4.2.2", 18 | "mongoose": "^6.6.5", 19 | "path": "^0.12.7", 20 | "sjcl": "^1.0.8", 21 | "uuid": "^9.0.0", 22 | "ws": "^8.9.0", 23 | "xml-parser": "^1.2.1", 24 | "xmlbuilder": "^15.1.1" 25 | }, 26 | "scripts": { 27 | "test": "echo \"Error: no test specified\" && exit 1" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/Project-Reload/Reload-Backend.git" 32 | }, 33 | "keywords": [ 34 | "fortnite", 35 | "backend", 36 | "node", 37 | "server", 38 | "v2" 39 | ], 40 | "author": "burlone0", 41 | "license": "GPL-3.0", 42 | "bugs": { 43 | "url": "https://github.com/Project-Reload/Reload-Backend/issues" 44 | }, 45 | "homepage": "https://github.com/Project-Reload/Reload-Backend#readme" 46 | } -------------------------------------------------------------------------------- /responses/Athena/BattlePass/Season2.json: -------------------------------------------------------------------------------- 1 | { 2 | "battlePassOfferId": "C3BA14F04F4D56FC1D490F8011B56553", 3 | "tierOfferId": "F86AC2ED4B3EA4B2D65EF1B2629572A0", 4 | "paidRewards": [ 5 | { 6 | "Token:athenaseasonxpboost": 50, 7 | "Token:athenaseasonfriendxpboost": 10, 8 | "AthenaCharacter:cid_032_athena_commando_m_medieval": 1, 9 | "AthenaBackpack:bid_001_bluesquire": 1 10 | }, 11 | { 12 | "Token:athenaseasonxpboost": 10, 13 | "Token:athenanextseasontierboost": 5 14 | }, 15 | { 16 | "HomebaseBannerIcon:brseason02bush": 1 17 | }, 18 | { 19 | "Currency:mtxgiveaway": 100 20 | }, 21 | { 22 | "AthenaDance:emoji_clapping": 1 23 | }, 24 | { 25 | "AccountResource:athenaseasonalxp": 1000 26 | }, 27 | { 28 | "AthenaPickaxe:pickaxe_id_012_district": 1 29 | }, 30 | { 31 | "AccountResource:athenaseasonalxp": 1000 32 | }, 33 | { 34 | "Token:athenaseasonfriendxpboost": 5 35 | }, 36 | { 37 | "HomebaseBannerIcon:brseason02lionherald": 1 38 | }, 39 | { 40 | "Currency:mtxgiveaway": 100 41 | }, 42 | { 43 | "AthenaDance:emoji_rip": 1 44 | }, 45 | { 46 | "AccountResource:athenaseasonalxp": 1000 47 | }, 48 | { 49 | "AthenaGlider:glider_id_004_disco": 1 50 | }, 51 | { 52 | "HomebaseBannerIcon:brseason02catsoldier": 1 53 | }, 54 | { 55 | "Token:athenaseasonxpboost": 10 56 | }, 57 | { 58 | "HomebaseBannerIcon:brseason02dragon": 1 59 | }, 60 | { 61 | "Currency:mtxgiveaway": 100 62 | }, 63 | { 64 | "AthenaDance:emoji_rage": 1 65 | }, 66 | { 67 | "AccountResource:athenaseasonalxp": 1000 68 | }, 69 | { 70 | "AthenaCharacter:cid_033_athena_commando_f_medieval": 1, 71 | "AthenaBackpack:bid_002_royaleknight": 1 72 | }, 73 | { 74 | "AthenaDance:emoji_peacesign": 1 75 | }, 76 | { 77 | "Token:athenaseasonfriendxpboost": 5 78 | }, 79 | { 80 | "HomebaseBannerIcon:brseason02planet": 1 81 | }, 82 | { 83 | "Currency:mtxgiveaway": 100 84 | }, 85 | { 86 | "AthenaDance:emoji_disco": 1 87 | }, 88 | { 89 | "AccountResource:athenaseasonalxp": 1000 90 | }, 91 | { 92 | "AthenaDance:eid_worm": 1 93 | }, 94 | { 95 | "HomebaseBannerIcon:brseason02bowling": 1 96 | }, 97 | { 98 | "Token:athenaseasonxpboost": 10 99 | }, 100 | { 101 | "HomebaseBannerIcon:brseason02monstertruck": 1 102 | }, 103 | { 104 | "Currency:mtxgiveaway": 100 105 | }, 106 | { 107 | "AthenaDance:emoji_exclamation": 1 108 | }, 109 | { 110 | "AccountResource:athenaseasonalxp": 1000 111 | }, 112 | { 113 | "AthenaPickaxe:pickaxe_id_011_medieval": 1 114 | }, 115 | { 116 | "AthenaDance:emoji_armflex": 1 117 | }, 118 | { 119 | "Token:athenaseasonfriendxpboost": 5 120 | }, 121 | { 122 | "HomebaseBannerIcon:brseason02icecream": 1 123 | }, 124 | { 125 | "Currency:mtxgiveaway": 100 126 | }, 127 | { 128 | "AthenaDance:emoji_mvp": 1 129 | }, 130 | { 131 | "AccountResource:athenaseasonalxp": 1000 132 | }, 133 | { 134 | "AthenaGlider:glider_id_003_district": 1 135 | }, 136 | { 137 | "AccountResource:athenaseasonalxp": 1000 138 | }, 139 | { 140 | "Token:athenaseasonxpboost": 10 141 | }, 142 | { 143 | "HomebaseBannerIcon:brseason02log": 1 144 | }, 145 | { 146 | "Currency:mtxgiveaway": 100 147 | }, 148 | { 149 | "AthenaDance:emoji_baited": 1 150 | }, 151 | { 152 | "AccountResource:athenaseasonalxp": 1000 153 | }, 154 | { 155 | "AthenaDance:eid_floss": 1 156 | }, 157 | { 158 | "HomebaseBannerIcon:brseason02cake": 1 159 | }, 160 | { 161 | "Token:athenaseasonfriendxpboost": 5 162 | }, 163 | { 164 | "HomebaseBannerIcon:brseason02tank": 1 165 | }, 166 | { 167 | "Currency:mtxgiveaway": 100 168 | }, 169 | { 170 | "AthenaDance:emoji_salty": 1 171 | }, 172 | { 173 | "AccountResource:athenaseasonalxp": 1000 174 | }, 175 | { 176 | "AthenaCharacter:cid_039_athena_commando_f_disco": 1 177 | }, 178 | { 179 | "AthenaDance:emoji_stealthy": 1 180 | }, 181 | { 182 | "Token:athenaseasonxpboost": 10 183 | }, 184 | { 185 | "HomebaseBannerIcon:brseason02gasmask": 1 186 | }, 187 | { 188 | "Currency:mtxgiveaway": 100 189 | }, 190 | { 191 | "AthenaDance:emoji_potatoaim": 1 192 | }, 193 | { 194 | "AccountResource:athenaseasonalxp": 1000 195 | }, 196 | { 197 | "AthenaPickaxe:pickaxe_id_013_teslacoil": 1 198 | }, 199 | { 200 | "AccountResource:athenaseasonalxp": 1000 201 | }, 202 | { 203 | "Token:athenaseasonxpboost": 10 204 | }, 205 | { 206 | "HomebaseBannerIcon:brseason02vulture": 1 207 | }, 208 | { 209 | "Currency:mtxgiveaway": 100 210 | }, 211 | { 212 | "AthenaDance:emoji_onfire": 1 213 | }, 214 | { 215 | "AccountResource:athenaseasonalxp": 1000 216 | }, 217 | { 218 | "AthenaCharacter:cid_035_athena_commando_m_medieval": 1, 219 | "AthenaBackpack:bid_004_blackknight": 1 220 | } 221 | ], 222 | "freeRewards": [ 223 | {}, 224 | { 225 | "AthenaDance:emoji_lol": 1 226 | }, 227 | {}, 228 | {}, 229 | { 230 | "AthenaDance:eid_wave": 1 231 | }, 232 | {}, 233 | {}, 234 | { 235 | "HomebaseBannerIcon:brseason02salt": 1 236 | }, 237 | {}, 238 | {}, 239 | { 240 | "AthenaDance:emoji_hearthands": 1 241 | }, 242 | {}, 243 | {}, 244 | { 245 | "Currency:mtxgiveaway": 100 246 | }, 247 | {}, 248 | {}, 249 | { 250 | "AthenaDance:emoji_bullseye": 1 251 | }, 252 | {}, 253 | {}, 254 | { 255 | "AthenaDance:eid_ridethepony_athena": 1 256 | }, 257 | {}, 258 | {}, 259 | { 260 | "AccountResource:athenaseasonalxp": 1000 261 | }, 262 | {}, 263 | {}, 264 | { 265 | "HomebaseBannerIcon:brseason02crosshair": 1 266 | }, 267 | {}, 268 | {}, 269 | { 270 | "Currency:mtxgiveaway": 100 271 | }, 272 | {}, 273 | {}, 274 | { 275 | "HomebaseBannerIcon:brseason02shark": 1 276 | }, 277 | {}, 278 | {}, 279 | { 280 | "AthenaGlider:glider_id_002_medieval": 1 281 | }, 282 | {}, 283 | {}, 284 | {}, 285 | {}, 286 | {}, 287 | {}, 288 | {}, 289 | {}, 290 | {}, 291 | {}, 292 | {}, 293 | {}, 294 | {}, 295 | {}, 296 | {}, 297 | {}, 298 | {}, 299 | {}, 300 | {}, 301 | {}, 302 | {}, 303 | {}, 304 | {}, 305 | {}, 306 | {}, 307 | {}, 308 | {}, 309 | {}, 310 | {}, 311 | {}, 312 | {}, 313 | {}, 314 | {}, 315 | {}, 316 | {} 317 | ] 318 | } -------------------------------------------------------------------------------- /responses/Athena/BattlePass/Season20.json: -------------------------------------------------------------------------------- 1 | { 2 | "battleBundleOfferId": "C6A175814C40EE7AE38C3899B891B732", 3 | "battlePassOfferId": "12D84F2D4B6A9FCA152F71A317AAA83E", 4 | "tierOfferId": "ABA50BB54BEFFD925095869DD16DC561", 5 | "paidRewards": [ 6 | { 7 | "Emoji_S20_CyberArmor": 1, 8 | "Pickaxe_ID_768_CyberArmorFemale": 1, 9 | "MtxGiveaway": 100, 10 | "Glider_ID_356_CyberArmorFemale": 1, 11 | "Wrap_454_CyberArmor": 1, 12 | "LSID_412_CyberArmor": 1, 13 | "CID_A_369_Athena_Commando_F_CyberArmor": 1 14 | }, 15 | {}, 16 | { 17 | "Pickaxe_ID_773_OrderGuardMale": 1 18 | }, 19 | {}, 20 | { 21 | "CID_A_370_Athena_Commando_M_OrderGuard": 1 22 | }, 23 | {}, 24 | { 25 | "BRS20_OrderGuard": 1 26 | }, 27 | { 28 | "BID_970_OrderGuard": 1 29 | }, 30 | {}, 31 | { 32 | "Glider_ID_361_OrderGuardMale": 1 33 | }, 34 | { 35 | "SPID_365_CyberArmor": 1 36 | }, 37 | { 38 | "MtxGiveaway": 100 39 | }, 40 | {}, 41 | { 42 | "VTID_A_523_OrderGuard_StyleB": 1 43 | }, 44 | { 45 | "MtxGiveaway": 100 46 | }, 47 | { 48 | "LSID_418_S20_Drill": 1 49 | }, 50 | { 51 | "EID_CyberArmor": 1 52 | }, 53 | {}, 54 | { 55 | "MtxGiveaway": 100 56 | }, 57 | { 58 | "VTID_A_524_OrderGuard_ColorB": 1 59 | }, 60 | { 61 | "MtxGiveaway": 100 62 | }, 63 | { 64 | "MusicPack_129_OrderGuard": 1 65 | }, 66 | { 67 | "LSID_419_S20_AllOutWar": 1 68 | }, 69 | { 70 | "BID_974_Binary": 1 71 | }, 72 | {}, 73 | { 74 | "MtxGiveaway": 100 75 | }, 76 | { 77 | "Pickaxe_ID_767_CadetFemale": 1 78 | }, 79 | { 80 | "BRS20_Cadet": 1 81 | }, 82 | { 83 | "Emoji_S20_Cadet": 1 84 | }, 85 | {}, 86 | { 87 | "MtxGiveaway": 100 88 | }, 89 | { 90 | "CID_A_374_Athena_Commando_F_Binary": 1 91 | }, 92 | { 93 | "LSID_410_Cadet": 1 94 | }, 95 | { 96 | "Pickaxe_ID_766_BinaryFemale": 1 97 | }, 98 | { 99 | "MusicPack_127_Cadet": 1 100 | }, 101 | {}, 102 | { 103 | "MtxGiveaway": 100 104 | }, 105 | { 106 | "BID_971_Cadet": 1 107 | }, 108 | { 109 | "Emoji_S20_KnightCat": 1 110 | }, 111 | { 112 | "EID_KnightCat": 1 113 | }, 114 | {}, 115 | { 116 | "MtxGiveaway": 100 117 | }, 118 | { 119 | "CID_A_372_Athena_Commando_F_KnightCat": 1 120 | }, 121 | { 122 | "LSID_413_KnightCat": 1 123 | }, 124 | { 125 | "BID_972_KnightCat_Female": 1 126 | }, 127 | { 128 | "VTID_A_540_OriginPrisoner_ColorB": 1 129 | }, 130 | {}, 131 | { 132 | "MtxGiveaway": 100 133 | }, 134 | { 135 | "Pickaxe_ID_776_TheOriginMale": 1 136 | } 137 | ], 138 | "freeRewards": [ 139 | {}, 140 | { 141 | "SPID_370_MysticAmulet": 1 142 | }, 143 | {}, 144 | { 145 | "MtxGiveaway": 100 146 | }, 147 | {}, 148 | { 149 | "BID_969_CyberArmor": 1 150 | }, 151 | {}, 152 | {}, 153 | { 154 | "LSID_414_OrderGuard": 1 155 | }, 156 | {}, 157 | {}, 158 | {}, 159 | { 160 | "MtxGiveaway": 100 161 | }, 162 | {}, 163 | {}, 164 | {}, 165 | { 166 | "SPID_372_OrderGuard": 1 167 | }, 168 | {}, 169 | {}, 170 | {}, 171 | {}, 172 | { 173 | "MtxGiveaway": 100 174 | }, 175 | { 176 | "BRS20_CadetCat": 1 177 | }, 178 | { 179 | "EID_OrderGuard": 1 180 | }, 181 | {}, 182 | { 183 | "MtxGiveaway": 100 184 | }, 185 | { 186 | "Pickaxe_ID_766_BinaryFemale": 1 187 | }, 188 | { 189 | "BRS20_Binary": 1 190 | }, 191 | { 192 | "SPID_372_OrderGuard": 1 193 | }, 194 | { 195 | "Wrap_456_OriginPrison": 1 196 | }, 197 | {}, 198 | { 199 | "MtxGiveaway": 100 200 | }, 201 | { 202 | "SPID_371_MysticRunes": 1 203 | }, 204 | { 205 | "Emoji_S20_CubeKing": 1 206 | }, 207 | { 208 | "EID_KnightCat": 1 209 | }, 210 | {}, 211 | { 212 | "MtxGiveaway": 100 213 | }, 214 | { 215 | "CID_A_367_Athena_Commando_M_Mystic": 1 216 | }, 217 | { 218 | "LSID_416_Mystic": 1 219 | } 220 | ] 221 | } -------------------------------------------------------------------------------- /responses/CloudDir/Full.ini: -------------------------------------------------------------------------------- 1 | [LawinServer.manifest,FortniteCreative] 2 | DeltaDownloadSize="0" 3 | DownloadSize="0" 4 | [LawinServer.manifest,FortniteCampaign] 5 | DeltaDownloadSize="0" 6 | DownloadSize="0" 7 | [LawinServer.manifest,Lang.pl] 8 | DeltaDownloadSize="0" 9 | DownloadSize="0" 10 | [LawinServer.manifest,Lang.es-419Optional] 11 | DeltaDownloadSize="0" 12 | DownloadSize="0" 13 | [LawinServer.manifest,StartupOptional] 14 | DeltaDownloadSize="0" 15 | DownloadSize="0" 16 | [LawinServer.manifest,FortniteCampaignTutorial] 17 | DeltaDownloadSize="0" 18 | DownloadSize="0" 19 | [LawinServer.manifest,Lang.allOptional] 20 | DeltaDownloadSize="0" 21 | DownloadSize="0" 22 | [LawinServer.manifest,Lang.itOptional] 23 | DeltaDownloadSize="0" 24 | DownloadSize="0" 25 | [LawinServer.manifest,Lang.es-419] 26 | DeltaDownloadSize="0" 27 | DownloadSize="0" 28 | [LawinServer.manifest,KairosCapture] 29 | DeltaDownloadSize="0" 30 | DownloadSize="0" 31 | [LawinServer.manifest,FortniteBR] 32 | DeltaDownloadSize="0" 33 | DownloadSize="0" 34 | [LawinServer.manifest,FortniteCreativeOptional] 35 | DeltaDownloadSize="0" 36 | DownloadSize="0" 37 | [LawinServer.manifest,Lang.zh-CN] 38 | DeltaDownloadSize="0" 39 | DownloadSize="0" 40 | [LawinServer.manifest,Lang.ruOptional] 41 | DeltaDownloadSize="0" 42 | DownloadSize="0" 43 | [LawinServer.manifest,Lang.frOptional] 44 | DeltaDownloadSize="0" 45 | DownloadSize="0" 46 | [LawinServer.manifest,Lang.deOptional] 47 | DeltaDownloadSize="0" 48 | DownloadSize="0" 49 | [LawinServer.manifest,Lang.de] 50 | DeltaDownloadSize="0" 51 | DownloadSize="0" 52 | [LawinServer.manifest,FortniteBROptional] 53 | DeltaDownloadSize="0" 54 | DownloadSize="0" 55 | [LawinServer.manifest,FortniteCampaignOptional] 56 | DeltaDownloadSize="0" 57 | DownloadSize="0" 58 | [LawinServer.manifest,FortniteCampaignTutorialOptional] 59 | DeltaDownloadSize="0" 60 | DownloadSize="0" 61 | [LawinServer.manifest,Lang.ru] 62 | DeltaDownloadSize="0" 63 | DownloadSize="0" 64 | [LawinServer.manifest,Lang.plOptional] 65 | DeltaDownloadSize="0" 66 | DownloadSize="0" 67 | [LawinServer.manifest,Lang.fr] 68 | DeltaDownloadSize="0" 69 | DownloadSize="0" 70 | [LawinServer.manifest,Lang.es-ESOptional] 71 | DeltaDownloadSize="0" 72 | DownloadSize="0" 73 | [LawinServer.manifest,KairosCaptureOptional] 74 | DeltaDownloadSize="0" 75 | DownloadSize="0" 76 | [LawinServer.manifest,Lang.all] 77 | DeltaDownloadSize="0" 78 | DownloadSize="0" 79 | [LawinServer.manifest,Lang.zh-CNOptional] 80 | DeltaDownloadSize="0" 81 | DownloadSize="0" 82 | [LawinServer.manifest,Lang.it] 83 | DeltaDownloadSize="0" 84 | DownloadSize="0" 85 | [LawinServer.manifest,Lang.es-ES] 86 | DeltaDownloadSize="0" 87 | DownloadSize="0" 88 | [LawinServer.manifest,Startup] 89 | DeltaDownloadSize="0" 90 | DownloadSize="0" -------------------------------------------------------------------------------- /responses/CloudDir/LawinServer.chunk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/CloudDir/LawinServer.chunk -------------------------------------------------------------------------------- /responses/CloudDir/LawinServer.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/CloudDir/LawinServer.manifest -------------------------------------------------------------------------------- /responses/Discovery/discovery_api_assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "FortCreativeDiscoverySurface": { 3 | "meta": { 4 | "promotion": 1 5 | }, 6 | "assets": { 7 | "CreativeDiscoverySurface_Frontend": { 8 | "meta": { 9 | "revision": 1, 10 | "headRevision": 1, 11 | "revisedAt": "2022-04-11T16:34:03.517Z", 12 | "promotion": 1, 13 | "promotedAt": "2022-04-11T16:34:49.510Z" 14 | }, 15 | "assetData": { 16 | "AnalyticsId": "t412", 17 | "TestCohorts": [ 18 | { 19 | "AnalyticsId": "c522715413", 20 | "CohortSelector": "PlayerDeterministic", 21 | "PlatformBlacklist": [], 22 | "ContentPanels": [ 23 | { 24 | "NumPages": 1, 25 | "AnalyticsId": "p536", 26 | "PanelType": "AnalyticsList", 27 | "AnalyticsListName": "ByEpicWoven", 28 | "CuratedListOfLinkCodes": [], 29 | "ModelName": "", 30 | "PageSize": 7, 31 | "PlatformBlacklist": [], 32 | "PanelName": "ByEpicWoven", 33 | "MetricInterval": "", 34 | "SkippedEntriesCount": 0, 35 | "SkippedEntriesPercent": 0, 36 | "SplicedEntries": [], 37 | "PlatformWhitelist": [], 38 | "EntrySkippingMethod": "None", 39 | "PanelDisplayName": { 40 | "Category": "Game", 41 | "NativeCulture": "", 42 | "Namespace": "CreativeDiscoverySurface_Frontend", 43 | "LocalizedStrings": [ 44 | { 45 | "key": "ar", 46 | "value": "العب بأسلوبك" 47 | }, 48 | { 49 | "key": "de", 50 | "value": "Spiele auf deine Weise" 51 | }, 52 | { 53 | "key": "en", 54 | "value": "Play Your Way" 55 | }, 56 | { 57 | "key": "es", 58 | "value": "Juega como quieras" 59 | }, 60 | { 61 | "key": "fr", 62 | "value": "Jouez à votre façon" 63 | }, 64 | { 65 | "key": "it", 66 | "value": "Gioca a modo tuo" 67 | }, 68 | { 69 | "key": "ja", 70 | "value": "好きにプレイしよう" 71 | }, 72 | { 73 | "key": "ko", 74 | "value": "나만의 플레이" 75 | }, 76 | { 77 | "key": "pl", 78 | "value": "Graj po swojemu" 79 | }, 80 | { 81 | "key": "ru", 82 | "value": "Играйте как нравится" 83 | }, 84 | { 85 | "key": "tr", 86 | "value": "İstediğin Gibi Oyna" 87 | }, 88 | { 89 | "key": "pt-BR", 90 | "value": "Jogue do Seu Jeito" 91 | }, 92 | { 93 | "key": "es-419", 94 | "value": "Juega a tu manera" 95 | } 96 | ], 97 | "bIsMinimalPatch": false, 98 | "NativeString": "Play Your Way", 99 | "Key": "ByEpicWoven" 100 | }, 101 | "PlayHistoryType": "RecentlyPlayed", 102 | "bLowestToHighest": false, 103 | "PanelLinkCodeBlacklist": [], 104 | "PanelLinkCodeWhitelist": [], 105 | "FeatureTags": [], 106 | "MetricName": "" 107 | } 108 | ], 109 | "PlatformWhitelist": [], 110 | "SelectionChance": 0.1, 111 | "TestName": "Reload" 112 | } 113 | ], 114 | "GlobalLinkCodeBlacklist": [], 115 | "SurfaceName": "CreativeDiscoverySurface_Frontend", 116 | "TestName": "20.10_4/11/2022_hero_combat_popularConsole", 117 | "primaryAssetId": "FortCreativeDiscoverySurface:CreativeDiscoverySurface_Frontend", 118 | "GlobalLinkCodeWhitelist": [] 119 | } 120 | } 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/init_0.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/init_0.mp4 -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_1.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_1.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_10.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_10.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_11.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_11.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_12.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_12.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_13.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_13.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_14.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_14.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_15.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_15.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_16.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_16.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_2.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_2.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_3.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_3.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_4.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_4.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_5.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_5.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_6.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_6.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_7.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_7.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_8.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_8.m4s -------------------------------------------------------------------------------- /responses/audio/JamTracks/OGRemix/segment_0_9.m4s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/responses/audio/JamTracks/OGRemix/segment_0_9.m4s -------------------------------------------------------------------------------- /responses/motdTarget.json: -------------------------------------------------------------------------------- 1 | { 2 | "contentType": "collection", 3 | "contentId": "motd-default-collection", 4 | "tcId": "634e8e85-e2fc-4c68-bb10-93604cf6605f", 5 | "contentItems": [ 6 | { 7 | "contentType": "content-item", 8 | "contentId": "46874c56-0973-4cbe-ac98-b580c5b36df5", 9 | "tcId": "61fb3dd8-f23d-45cc-9058-058ab223ba5c", 10 | "contentFields": { 11 | "body": { 12 | "ar": "تم الإنشاء بواسطة بورلون، هذا هو خلفية معدلة، جميع حقوق الخلفية الأصلية تذهب إلى لاوين", 13 | "en": "Created by Burlone, This is a modded backend, all main backend credits to lawin", 14 | "de": "Erstellt von Burlone, dies ist ein modifiziertes Backend, alle Hauptcredits für das Backend gehen an Lawin", 15 | "es": "Creado por Burlone, este es un backend modificado, todos los créditos del backend principal van para Lawin", 16 | "es-419": "Creado por Burlone, este es un backend modificado, todos los créditos del backend principal van para Lawin", 17 | "fr": "Créé par Burlone, il s'agit d'un backend modifié, tous les crédits du backend principal vont à Lawin", 18 | "it": "Creato da Burlone, questo è un backend modificato, tutti i crediti del backend principale vanno a Lawin", 19 | "ja": "Burloneによって作成されました。これは改造されたバックエンドです。すべてのメインバックエンドのクレジットはLawinに帰属します", 20 | "ko": "Burlone이 만든 수정된 백엔드입니다. 모든 주요 백엔드 크레딧은 Lawin에게 있습니다", 21 | "pl": "Stworzone przez Burlone, to jest zmodyfikowany backend, wszystkie główne zasługi należą do Lawina", 22 | "pt-BR": "Criado por Burlone, este é um backend modificado, todos os créditos do backend principal vão para Lawin", 23 | "ru": "Создано Burlone, это модифицированный бэкенд, все основные заслуги за бэкенд принадлежат Lawin", 24 | "tr": "Burlone tarafından oluşturulmuştur, bu modifiye bir backend'dir, tüm ana backend kredileri Lawin'e aittir" 25 | }, 26 | "entryType": "Website", 27 | "image": [ 28 | { 29 | "width": 1920, 30 | "height": 1080, 31 | "url": "https://i.imgur.com/ImIwpRm.png" 32 | } 33 | ], 34 | "tabTitleOverride": "Reload Backend", 35 | "tileImage": [ 36 | { 37 | "width": 1024, 38 | "height": 512, 39 | "url": "https://i.imgur.com/KaJkHgq.png" 40 | } 41 | ], 42 | "title": { 43 | "ar": "مرحبًا بك في Reload!", 44 | "en": "Welcome to Reload!", 45 | "de": "Willkommen bei Reload!", 46 | "es": "¡Bienvenidos a Reload!", 47 | "es-419": "¡Bienvenidos a Reload!", 48 | "fr": "Bienvenue sur Reload !", 49 | "it": "Benvenuto in Reload!", 50 | "ja": "Reloadへようこそ!", 51 | "ko": "Reload에 오신 것을 환영합니다!", 52 | "pl": "Witaj w Reloadze!", 53 | "pt-BR": "Bem-vindo ao Reload!", 54 | "ru": "Добро пожаловать в Reload!", 55 | "tr": "Reload'a Hoş Geldiniz!" 56 | }, 57 | "videoAutoplay": false, 58 | "videoLoop": false, 59 | "videoMute": false, 60 | "videoStreamingEnabled": false, 61 | "websiteButtonText": "Reload Backend - Github", 62 | "websiteURL": "https://github.com/Project-Reload/Reload-Backend" 63 | }, 64 | "contentHash": "reloadbackendcontenthashtylawin", 65 | "contentSchemaName": "MotdWebsiteNewsWithVideo" 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /responses/winterfestRewards.json: -------------------------------------------------------------------------------- 1 | { 2 | "Season11": { 3 | "ERG.Node.A.1": ["AthenaCharacter:cid_645_athena_commando_f_wolly"], 4 | "ERG.Node.B.1": ["AthenaGlider:glider_id_188_galileorocket_g7oki"], 5 | "ERG.Node.C.1": ["AthenaBackpack:bid_430_galileospeedboat_9rxe3"], 6 | "ERG.Node.D.1": ["AthenaCharacter:cid_643_athena_commando_m_ornamentsoldier"], 7 | "ERG.Node.A.2": ["AthenaPickaxe:pickaxe_id_329_gingerbreadcookie1h"], 8 | "ERG.Node.A.3": ["AthenaPickaxe:pickaxe_id_332_mintminer"], 9 | "ERG.Node.A.4": ["AthenaDance:eid_snowglobe"], 10 | "ERG.Node.A.5": ["AthenaGlider:glider_id_191_pinetree"], 11 | "ERG.Node.A.6": ["AthenaItemWrap:wrap_188_wrappingpaper"], 12 | "ERG.Node.A.7": ["AthenaItemWrap:wrap_183_newyear2020"], 13 | "ERG.Node.A.8": ["AthenaSkyDiveContrail:trails_id_082_holidaygarland"], 14 | "ERG.Node.A.9": ["AthenaMusicPack:musicpack_040_xmaschiptunes"], 15 | "ERG.Node.A.10": ["AthenaLoadingScreen:lsid_208_smpattern"], 16 | "ERG.Node.A.11": ["AthenaLoadingScreen:lsid_209_akcrackshot"] 17 | }, 18 | "Season19": { 19 | "ERG.Node.A.1": ["Token:14daysoffortnite_small_giftbox"], 20 | "ERG.Node.B.1": ["AthenaDance:emoji_s19_animwinterfest2021"], 21 | "ERG.Node.C.1": ["AthenaGlider:glider_id_335_logarithm_40qgl"], 22 | "ERG.Node.D.1": ["AthenaCharacter:cid_a_323_athena_commando_m_bananawinter"], 23 | "ERG.Node.A.2": ["HomebaseBannerIcon:brs19_winterfest2021"], 24 | "ERG.Node.A.3": ["AthenaSkyDiveContrail:trails_id_137_turtleneckcrystal"], 25 | "ERG.Node.A.4": ["AthenaItemWrap:wrap_429_holidaysweater"], 26 | "ERG.Node.A.5": ["AthenaLoadingScreen:lsid_393_winterfest2021"], 27 | "ERG.Node.A.6": ["AthenaMusicPack:musicpack_117_winterfest2021"], 28 | "ERG.Node.A.7": ["AthenaDance:eid_epicyarn"], 29 | "ERG.Node.A.8": ["AthenaCharacter:cid_a_310_athena_commando_F_scholarfestive"], 30 | "ERG.Node.A.9": ["AthenaPickaxe:pickaxe_id_731_scholarfestivefemale1h"], 31 | "ERG.Node.A.10": ["AthenaItemWrap:wrap_430_winterlights"], 32 | "ERG.Node.A.11": ["AthenaDance:spid_346_winterfest_2021"], 33 | "ERG.Node.A.12": ["AthenaPickaxe:pickaxe_ID_732_shovelmale"] 34 | }, 35 | "Season23": { 36 | "ERG.Node.A.1": ["AthenaCharacter:character_sportsfashion_winter"], 37 | "ERG.Node.B.1": ["AthenaCharacter:character_cometdeer"], 38 | "ERG.Node.A.2": ["AthenaGlider:glider_default_jolly"], 39 | "ERG.Node.A.3": ["AthenaDance:eid_dashing"], 40 | "ERG.Node.A.4": ["AthenaDance:spray_guffholidaytree_winterfest2022", "AthenaDance:spray_winterreindeer_winterfest2022", "AthenaDance:spray_defacedsnowman_winterfest2022"], 41 | "ERG.Node.A.5": ["AthenaDance:emoji_s23_winterfest_2022"], 42 | "ERG.Node.A.6": ["AthenaMusicPack:musicpack_164_redpepper_winterfest"], 43 | "ERG.Node.A.7": ["AthenaItemWrap:wrap_winter_pal"], 44 | "ERG.Node.A.8": ["AthenaPickaxe:pickaxe_jollytroll"], 45 | "ERG.Node.A.9": ["AthenaGlider:glider_jollytroll"], 46 | "ERG.Node.A.10": ["AthenaBackpack:backpack_jollytroll"], 47 | "ERG.Node.A.11": ["AthenaMusicPack:musicpack_163_winterfest_2022", "AthenaMusicPack:musicpack_157_radish_nightnight"], 48 | "ERG.Node.A.12": ["AthenaItemWrap:wrap_cometwinter"], 49 | "ERG.Node.A.13": ["AthenaSkyDiveContrail:contrail_jollytroll"] 50 | }, 51 | "Season33": { 52 | "ERG.Node.A.1": ["AthenaCharacter:character_blowwire"], 53 | "ERG.Node.A.2": ["SparksBass:sparks_bass_almondsplash"], 54 | "ERG.Node.A.3": ["SparksGuitar:sparks_guitar_icewater"], 55 | "ERG.Node.A.4": ["AthenaDance:spray_s33winterfest", "AthenaDance:spray_s33winterfest2", "AthenaDance:spray_musicpasswinterfests32"], 56 | "ERG.Node.A.5": ["AthenaDance:emoji_s33_winterfest"], 57 | "ERG.Node.A.6": ["SparksSong:sid_placeholder_437"], 58 | "ERG.Node.A.7": ["AthenaItemWrap:wrap_blowwire"], 59 | "ERG.Node.A.8": ["AthenaPickaxe:pickaxe_blowwire"], 60 | "ERG.Node.A.9": ["AthenaGlider:glider_blowwire"], 61 | "ERG.Node.A.10": ["AthenaBackpack:backpack_blowwire"], 62 | "ERG.Node.A.11": ["AthenaBackpack:backpack_almondsplash"], 63 | "ERG.Node.A.12": ["AthenaSkyDiveContrail:contrail_frostedglass"], 64 | "ERG.Node.B.1": ["AthenaCharacter:character_almondsplash"], 65 | "ERG.Node.A.13": ["AthenaPickaxe:pickaxe_almondsplash"] 66 | } 67 | } -------------------------------------------------------------------------------- /routes/affiliate.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | 4 | const codes = require("./../model/saccodes.js"); 5 | const Profile = require("../model/profiles.js"); 6 | const User = require("./../model/user.js"); 7 | 8 | const { verifyToken, verifyClient } = require("../tokenManager/tokenVerify.js"); 9 | const log = require("../structs/log.js"); 10 | 11 | app.get("/affiliate/api/public/affiliates/slug/:slug", async (req, res) => { 12 | var slug = req.params.slug; 13 | var lccode = slug.toLowerCase(); 14 | 15 | log.debug(`GET /affiliate/api/public/affiliates/slug/${slug} called`); 16 | 17 | const code = await codes.findOne({ code_lower: lccode }); 18 | 19 | var ValidCode = null; 20 | 21 | if (code === null) { ValidCode = false } else { ValidCode = true } 22 | 23 | if (ValidCode === true) { 24 | log.debug(`Code found: ${code.code}`); 25 | return res.json({ 26 | "id": code.code, 27 | "slug": code.code, 28 | "displayName": code.code, 29 | "code_higher": code.code_higher, 30 | "status": "ACTIVE", 31 | "verified": false 32 | }); 33 | } 34 | 35 | if (ValidCode === false) { 36 | log.debug(`Code not found: ${slug}`); 37 | res.status(404); 38 | res.json({}); 39 | } 40 | }); 41 | 42 | app.post("/fortnite/api/game/v2/profile/*/client/SetAffiliateName", verifyToken, async (req, res) => { 43 | const profiles = await Profile.findOne({ accountId: req.params[0] }); 44 | let profile = profiles.profiles[req.query.profileId]; 45 | 46 | var ApplyProfileChanges = []; 47 | var BaseRevision = profile.rvn || 0; 48 | var QueryRevision = req.query.rvn || -1; 49 | var StatChanged = false; 50 | 51 | var slug = req.body.affiliateName; 52 | var lccode = slug.toLowerCase(); 53 | 54 | log.debug(`POST /fortnite/api/game/v2/profile/*/client/SetAffiliateName called with slug: ${slug}`); 55 | 56 | const code = await codes.findOne({ code_lower: lccode }); 57 | 58 | if (code == null) { 59 | log.debug(`Affiliate name not found: ${slug}`); 60 | res.status(404); 61 | res.json({}); 62 | } 63 | 64 | profile.stats.attributes.mtx_affiliate_set_time = new Date().toISOString(); 65 | profile.stats.attributes.mtx_affiliate = code.code; 66 | 67 | await User.updateOne({ accountId: req.user.accountId }, { $set: { currentSACCode: code.code } }); 68 | 69 | StatChanged = true; 70 | 71 | if (StatChanged === true) { 72 | profile.rvn += 1; 73 | profile.commandRevision += 1; 74 | 75 | ApplyProfileChanges.push({ 76 | "changeType": "statModified", 77 | "name": "mtx_affiliate_set_time", 78 | "value": profile.stats.attributes.mtx_affiliate_set_time 79 | }); 80 | 81 | ApplyProfileChanges.push({ 82 | "changeType": "statModified", 83 | "name": "mtx_affiliate", 84 | "value": profile.stats.attributes.mtx_affiliate 85 | }); 86 | } 87 | 88 | if (QueryRevision != BaseRevision) { 89 | ApplyProfileChanges = [{ 90 | "changeType": "fullProfileUpdate", 91 | "profile": profile 92 | }]; 93 | } 94 | 95 | await profiles.updateOne({ $set: { [`profiles.${req.query.profileId}`]: profile } }); 96 | 97 | res.json({ 98 | "profileRevision": profile.rvn || 0, 99 | "profileId": req.query.profileId || "common_core", 100 | "profileChangesBaseRevision": BaseRevision, 101 | "profileChanges": ApplyProfileChanges, 102 | "profileCommandRevision": profile.commandRevision || 0, 103 | "serverTime": new Date().toISOString(), 104 | "responseVersion": 1 105 | }); 106 | res.end(); 107 | }); 108 | 109 | module.exports = app; -------------------------------------------------------------------------------- /routes/cloudstorage.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | const fs = require("fs"); 4 | const crypto = require("crypto"); 5 | const path = require("path"); 6 | 7 | const { verifyToken, verifyClient } = require("../tokenManager/tokenVerify.js"); 8 | const functions = require("../structs/functions.js"); 9 | 10 | let seasons = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; 11 | 12 | app.get("/fortnite/api/cloudstorage/system", (req, res) => { 13 | const dir = path.join(__dirname, "..", "CloudStorage"); 14 | 15 | let CloudFiles = []; 16 | 17 | fs.readdirSync(dir).forEach(name => { 18 | if (name.toLowerCase().endsWith(".ini")) { 19 | const ParsedFile = fs.readFileSync(path.join(dir, name)).toString(); 20 | const ParsedStats = fs.statSync(path.join(dir, name)); 21 | 22 | CloudFiles.push({ 23 | "uniqueFilename": name, 24 | "filename": name, 25 | "hash": crypto.createHash('sha1').update(ParsedFile).digest('hex'), 26 | "hash256": crypto.createHash('sha256').update(ParsedFile).digest('hex'), 27 | "length": ParsedFile.length, 28 | "contentType": "application/octet-stream", 29 | "uploaded": ParsedStats.mtime, 30 | "storageType": "S3", 31 | "storageIds": {}, 32 | "doNotCache": true 33 | }); 34 | } 35 | }); 36 | 37 | res.json(CloudFiles); 38 | }); 39 | 40 | app.get("/fortnite/api/cloudstorage/system/:file", (req, res) => { 41 | if (req.params.file.includes("..")) return res.status(404).end(); 42 | if (req.params.file.includes("~")) return res.status(404).end(); 43 | 44 | const file = path.join(__dirname, "..", "CloudStorage", path.basename(req.params.file)); 45 | 46 | if (fs.existsSync(file)) return res.status(200).send(fs.readFileSync(file)); 47 | 48 | res.status(200).end(); 49 | }); 50 | 51 | app.get("/fortnite/api/cloudstorage/user/*/:file", verifyToken, (req, res) => { 52 | let clientSettingsPath = path.join(__dirname, "..", "ClientSettings", req.user.accountId); 53 | if (!fs.existsSync(clientSettingsPath)) fs.mkdirSync(clientSettingsPath); 54 | 55 | if (req.params.file.toLowerCase() != "clientsettings.sav") return res.status(200).end(); 56 | 57 | const memory = functions.GetVersionInfo(req); 58 | if (!seasons.includes(memory.season)) return res.status(200).end(); 59 | 60 | let file = path.join(clientSettingsPath, `ClientSettings-${memory.season}.Sav`); 61 | 62 | if (fs.existsSync(file)) return res.status(200).send(fs.readFileSync(file)); 63 | 64 | res.status(200).end(); 65 | }); 66 | 67 | app.get("/fortnite/api/cloudstorage/user/:accountId", verifyToken, (req, res) => { 68 | let clientSettingsPath = path.join(__dirname, "..", "ClientSettings", req.user.accountId); 69 | if (!fs.existsSync(clientSettingsPath)) fs.mkdirSync(clientSettingsPath); 70 | 71 | const memory = functions.GetVersionInfo(req); 72 | if (!seasons.includes(memory.season)) return res.json([]); 73 | 74 | let file = path.join(clientSettingsPath, `ClientSettings-${memory.season}.Sav`); 75 | 76 | if (fs.existsSync(file)) { 77 | const ParsedFile = fs.readFileSync(file, 'latin1'); 78 | const ParsedStats = fs.statSync(file); 79 | 80 | return res.json([{ 81 | "uniqueFilename": "ClientSettings.Sav", 82 | "filename": "ClientSettings.Sav", 83 | "hash": crypto.createHash('sha1').update(ParsedFile).digest('hex'), 84 | "hash256": crypto.createHash('sha256').update(ParsedFile).digest('hex'), 85 | "length": Buffer.byteLength(ParsedFile), 86 | "contentType": "application/octet-stream", 87 | "uploaded": ParsedStats.mtime, 88 | "storageType": "S3", 89 | "storageIds": {}, 90 | "accountId": req.user.accountId, 91 | "doNotCache": false 92 | }]); 93 | } 94 | 95 | res.json([]); 96 | }); 97 | 98 | app.put("/fortnite/api/cloudstorage/user/*/:file", verifyToken, getRawBody, (req, res) => { 99 | if (Buffer.byteLength(req.rawBody) >= 400000) return res.status(403).json({ "error": "File size must be less than 400kb." }); 100 | 101 | let clientSettingsPath = path.join(__dirname, "..", "ClientSettings", req.user.accountId); 102 | if (!fs.existsSync(clientSettingsPath)) fs.mkdirSync(clientSettingsPath); 103 | 104 | if (req.params.file.toLowerCase() != "clientsettings.sav") return res.status(204).end(); 105 | 106 | const memory = functions.GetVersionInfo(req); 107 | if (!seasons.includes(memory.season)) return res.status(204).end(); 108 | 109 | let file = path.join(clientSettingsPath, `ClientSettings-${memory.season}.Sav`); 110 | fs.writeFileSync(file, req.rawBody, 'latin1'); 111 | 112 | res.status(204).end(); 113 | }); 114 | 115 | function getRawBody(req, res, next) { 116 | if (req.headers["content-length"]) { 117 | if (Number(req.headers["content-length"]) >= 400000) return res.status(403).json({ "error": "File size must be less than 400kb." }); 118 | } 119 | 120 | // Get raw body in encoding latin1 for ClientSettings 121 | try { 122 | req.rawBody = ""; 123 | req.setEncoding("latin1"); 124 | 125 | req.on("data", (chunk) => req.rawBody += chunk); 126 | req.on("end", () => next()); 127 | } catch { 128 | res.status(400).json({ "error": "Something went wrong while trying to access the request body." }); 129 | } 130 | } 131 | 132 | module.exports = app; 133 | -------------------------------------------------------------------------------- /routes/contentpages.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | const functions = require("../structs/functions.js"); 4 | 5 | app.get("/content/api/pages/fortnite-game/spark-tracks", async (req, res) => { 6 | const sparkTracks = require("./../responses/sparkTracks.json"); 7 | 8 | res.json(sparkTracks) 9 | }) 10 | 11 | app.get("/content/api/pages/*", async (req, res) => { 12 | const contentpages = functions.getContentPages(req); 13 | 14 | res.json(contentpages); 15 | }); 16 | 17 | app.post("/api/v1/fortnite-br/surfaces/motd/target", async (req, res) => { 18 | const motdTarget = JSON.parse(JSON.stringify(require("./../responses/motdTarget.json"))); 19 | 20 | try { 21 | motdTarget.contentItems.forEach(item => { 22 | item.contentFields.title = item.contentFields.title[req.body.language]; 23 | item.contentFields.body = item.contentFields.body[req.body.language]; 24 | }) 25 | } catch (err) {} 26 | 27 | res.json(motdTarget) 28 | }) 29 | 30 | module.exports = app; -------------------------------------------------------------------------------- /routes/discovery.js: -------------------------------------------------------------------------------- 1 | const Express = require("express"); 2 | const app = Express.Router(); 3 | const discovery = require("./../responses/Discovery/discovery_frontend.json"); 4 | const functions = require("./../structs/functions.js"); 5 | 6 | app.post("*/api/v2/discovery/surface/*", async (req, res) => { 7 | res.json(discovery); 8 | }); 9 | 10 | app.post("*/discovery/surface/*", async (req, res) => { 11 | res.json(discovery); 12 | }) 13 | 14 | app.get("/fortnite/api/discovery/accessToken/:branch", async (req, res) => { 15 | res.json({ 16 | "branchName": req.params.branch, 17 | "appId": "Fortnite", 18 | "token": functions.MakeID() 19 | }); 20 | }); 21 | 22 | app.post("/links/api/fn/mnemonic", async (req, res) => { 23 | var MnemonicArray = []; 24 | 25 | for (var i in discovery.Panels[0].Pages[0].results) { 26 | MnemonicArray.push(discovery.Panels[0].Pages[0].results[i].linkData) 27 | } 28 | 29 | res.json(MnemonicArray); 30 | }) 31 | 32 | app.get("/links/api/fn/mnemonic/:playlist/related", async (req, res) => { 33 | var response = { 34 | "parentLinks": [], 35 | "links": {} 36 | }; 37 | 38 | if (req.params.playlist) { 39 | for (var i in discovery.Panels[0].Pages[0].results) { 40 | var linkData = discovery.Panels[0].Pages[0].results[i].linkData; 41 | if (linkData.mnemonic == req.params.playlist) { 42 | response.links[req.params.playlist] = linkData; 43 | } 44 | } 45 | } 46 | 47 | res.json(response); 48 | }) 49 | 50 | app.get("/links/api/fn/mnemonic/*", async (req, res) => { 51 | for (var i in discovery.Panels[0].Pages[0].results) { 52 | if (discovery.Panels[0].Pages[0].results[i].linkData.mnemonic == req.url.split("/").slice(-1)[0]) { 53 | res.json(discovery.Panels[0].Pages[0].results[i].linkData); 54 | } 55 | } 56 | }) 57 | 58 | module.exports = app; -------------------------------------------------------------------------------- /routes/legal.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const app = express.Router(); 3 | const fs = require("fs") 4 | const eulaJson = JSON.parse(fs.readFileSync('./responses/eula/SharedAgreements.json', 'utf8')); 5 | 6 | app.get("/eulatracking/api/shared/agreements/fn", async (req, res) => { 7 | res.json(eulaJson); 8 | }); 9 | 10 | app.get("/eulatracking/api/public/agreements/fn/account/:accountId", async (req, res) => { 11 | res.status(204).send(); 12 | }); 13 | 14 | module.exports = app; -------------------------------------------------------------------------------- /routes/lightswitch.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | 4 | const { verifyToken, verifyClient } = require("../tokenManager/tokenVerify.js"); 5 | 6 | app.get("/lightswitch/api/service/Fortnite/status", async (req, res) => { 7 | res.json({ 8 | "serviceInstanceId": "fortnite", 9 | "status": "UP", 10 | "message": "Fortnite is online", 11 | "maintenanceUri": null, 12 | "overrideCatalogIds": [ 13 | "a7f138b2e51945ffbfdacc1af0541053" 14 | ], 15 | "allowedActions": [], 16 | "banned": false, 17 | "launcherInfoDTO": { 18 | "appName": "Fortnite", 19 | "catalogItemId": "4fe75bbc5a674f4f9b356b5c90567da5", 20 | "namespace": "fn" 21 | } 22 | }); 23 | }); 24 | 25 | app.get("/lightswitch/api/service/bulk/status", async (req, res) => { 26 | res.json([{ 27 | "serviceInstanceId": "fortnite", 28 | "status": "UP", 29 | "message": "fortnite is up.", 30 | "maintenanceUri": null, 31 | "overrideCatalogIds": [ 32 | "a7f138b2e51945ffbfdacc1af0541053" 33 | ], 34 | "allowedActions": [ 35 | "PLAY", 36 | "DOWNLOAD" 37 | ], 38 | "banned": false, 39 | "launcherInfoDTO": { 40 | "appName": "Fortnite", 41 | "catalogItemId": "4fe75bbc5a674f4f9b356b5c90567da5", 42 | "namespace": "fn" 43 | } 44 | }]); 45 | }); 46 | 47 | module.exports = app; -------------------------------------------------------------------------------- /routes/matchmaking.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | const config = require("../Config/config.json"); 4 | const functions = require("../structs/functions.js"); 5 | const log = require("../structs/log.js"); 6 | const MMCode = require("../model/mmcodes.js"); 7 | const { verifyToken } = require("../tokenManager/tokenVerify.js"); 8 | const qs = require("qs"); 9 | const error = require("../structs/error.js"); 10 | 11 | let buildUniqueId = {}; 12 | 13 | app.get("/fortnite/api/matchmaking/session/findPlayer/*", (req, res) => { 14 | log.debug("GET /fortnite/api/matchmaking/session/findPlayer/* called"); 15 | res.status(200); 16 | res.end(); 17 | }); 18 | 19 | app.get("/fortnite/api/game/v2/matchmakingservice/ticket/player/*", verifyToken, async (req, res) => { 20 | log.debug("GET /fortnite/api/game/v2/matchmakingservice/ticket/player/* called"); 21 | if (req.user.isServer == true) return res.status(403).end(); 22 | if (req.user.matchmakingId == null) return res.status(400).end(); 23 | 24 | const playerCustomKey = qs.parse(req.url.split("?")[1], { ignoreQueryPrefix: true })['player.option.customKey']; 25 | const bucketId = qs.parse(req.url.split("?")[1], { ignoreQueryPrefix: true })['bucketId']; 26 | if (typeof bucketId !== "string" || bucketId.split(":").length !== 4) { 27 | return res.status(400).end(); 28 | } 29 | const rawPlaylist = bucketId.split(":")[3]; 30 | let playlist = functions.PlaylistNames(rawPlaylist).toLowerCase(); 31 | 32 | const gameServers = config.gameServerIP; 33 | let selectedServer = gameServers.find(server => server.split(":")[2].toLowerCase() === playlist); 34 | if (!selectedServer) { 35 | log.debug("No server found for playlist", playlist); 36 | return error.createError("errors.com.epicgames.common.matchmaking.playlist.not_found", `No server found for playlist ${playlist}`, [], 1013, "invalid_playlist", 404, res); 37 | } 38 | await global.kv.set(`playerPlaylist:${req.user.accountId}`, playlist); 39 | if (typeof playerCustomKey == "string") { 40 | let codeDocument = await MMCode.findOne({ code_lower: playerCustomKey?.toLowerCase() }); 41 | if (!codeDocument) { 42 | return error.createError("errors.com.epicgames.common.matchmaking.code.not_found", `The matchmaking code "${playerCustomKey}" was not found`, [], 1013, "invalid_code", 404, res); 43 | } 44 | const kvDocument = JSON.stringify({ 45 | ip: codeDocument.ip, 46 | port: codeDocument.port, 47 | playlist: playlist, 48 | }); 49 | await global.kv.set(`playerCustomKey:${req.user.accountId}`, kvDocument); 50 | } 51 | if (typeof req.query.bucketId !== "string" || req.query.bucketId.split(":").length !== 4) { 52 | return res.status(400).end(); 53 | } 54 | 55 | buildUniqueId[req.user.accountId] = req.query.bucketId.split(":")[0]; 56 | 57 | const matchmakerIP = config.matchmakerIP; 58 | return res.json({ 59 | "serviceUrl": matchmakerIP.includes("ws") || matchmakerIP.includes("wss") ? matchmakerIP : `ws://${matchmakerIP}`, 60 | "ticketType": "mms-player", 61 | "payload": `${req.user.matchmakingId}`, 62 | "signature": "account" 63 | }); 64 | }); 65 | 66 | app.get("/fortnite/api/game/v2/matchmaking/account/:accountId/session/:sessionId", (req, res) => { 67 | log.debug(`GET /fortnite/api/game/v2/matchmaking/account/${req.params.accountId}/session/${req.params.sessionId} called`); 68 | res.json({ 69 | "accountId": req.params.accountId, 70 | "sessionId": req.params.sessionId, 71 | "key": "none" 72 | }); 73 | }); 74 | 75 | app.get("/fortnite/api/matchmaking/session/:sessionId", verifyToken, async (req, res) => { 76 | log.debug(`GET /fortnite/api/matchmaking/session/${req.params.sessionId} called`); 77 | const playlist = await global.kv.get(`playerPlaylist:${req.user.accountId}`); 78 | let kvDocument = await global.kv.get(`playerCustomKey:${req.user.accountId}`); 79 | if (!kvDocument) { 80 | const gameServers = config.gameServerIP; 81 | let selectedServer = gameServers.find(server => server.split(":")[2] === playlist); 82 | if (!selectedServer) { 83 | log.debug("No server found for playlist", playlist); 84 | return error.createError("errors.com.epicgames.common.matchmaking.playlist.not_found", `No server found for playlist ${playlist}`, [], 1013, "invalid_playlist", 404, res); 85 | } 86 | kvDocument = JSON.stringify({ 87 | ip: selectedServer.split(":")[0], 88 | port: selectedServer.split(":")[1], 89 | playlist: selectedServer.split(":")[2] 90 | }); 91 | } 92 | let codeKV = JSON.parse(kvDocument); 93 | 94 | res.json({ 95 | "id": req.params.sessionId, 96 | "ownerId": functions.MakeID().replace(/-/ig, "").toUpperCase(), 97 | "ownerName": "[DS]fortnite-liveeugcec1c2e30ubrcore0a-z8hj-1968", 98 | "serverName": "[DS]fortnite-liveeugcec1c2e30ubrcore0a-z8hj-1968", 99 | "serverAddress": codeKV.ip, 100 | "serverPort": codeKV.port, 101 | "maxPublicPlayers": 220, 102 | "openPublicPlayers": 175, 103 | "maxPrivatePlayers": 0, 104 | "openPrivatePlayers": 0, 105 | "attributes": { 106 | "REGION_s": "EU", 107 | "GAMEMODE_s": "FORTATHENA", 108 | "ALLOWBROADCASTING_b": true, 109 | "SUBREGION_s": "GB", 110 | "DCID_s": "FORTNITE-LIVEEUGCEC1C2E30UBRCORE0A-14840880", 111 | "tenant_s": "Fortnite", 112 | "MATCHMAKINGPOOL_s": "Any", 113 | "STORMSHIELDDEFENSETYPE_i": 0, 114 | "HOTFIXVERSION_i": 0, 115 | "PLAYLISTNAME_s": codeKV.playlist, 116 | "SESSIONKEY_s": functions.MakeID().replace(/-/ig, "").toUpperCase(), 117 | "TENANT_s": "Fortnite", 118 | "BEACONPORT_i": 15009 119 | }, 120 | "publicPlayers": [], 121 | "privatePlayers": [], 122 | "totalPlayers": 45, 123 | "allowJoinInProgress": false, 124 | "shouldAdvertise": false, 125 | "isDedicated": false, 126 | "usesStats": false, 127 | "allowInvites": false, 128 | "usesPresence": false, 129 | "allowJoinViaPresence": true, 130 | "allowJoinViaPresenceFriendsOnly": false, 131 | "buildUniqueId": buildUniqueId[req.user.accountId] || "0", 132 | "lastUpdated": new Date().toISOString(), 133 | "started": false 134 | }); 135 | }); 136 | 137 | app.post("/fortnite/api/matchmaking/session/*/join", (req, res) => { 138 | log.debug("POST /fortnite/api/matchmaking/session/*/join called"); 139 | res.status(204); 140 | res.end(); 141 | }); 142 | 143 | app.post("/fortnite/api/matchmaking/session/matchMakingRequest", (req, res) => { 144 | log.debug("POST /fortnite/api/matchmaking/session/matchMakingRequest called"); 145 | res.json([]); 146 | }); 147 | 148 | module.exports = app; -------------------------------------------------------------------------------- /routes/privacy.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const Profile = require("./../model/profiles.js") 4 | 5 | const { verifyToken } = require("../tokenManager/tokenVerify.js"); 6 | 7 | 8 | app.get("/fortnite/api/game/v2/privacy/account/:accountId", verifyToken , async (req, res) => { 9 | const profiles = await Profile.findOne({ accountId: req.user.accountId }); 10 | 11 | if(!profiles) return res.status(400).end(); 12 | 13 | res.json({ 14 | accountId: profiles.accountId, 15 | optOutOfPublicLeaderboards: profiles.profiles.athena.stats.attributes.optOutOfPublicLeaderboards 16 | }).end(); 17 | }) 18 | 19 | app.post("/fortnite/api/game/v2/privacy/account/:accountId", verifyToken , async (req, res) => { 20 | const profiles = await Profile.findOne({ accountId: req.user.accountId }); 21 | 22 | if(!profiles) return res.status(400).end(); 23 | 24 | let profile = profiles.profiles.athena; 25 | 26 | profile.stats.attributes.optOutOfPublicLeaderboards = req.body.optOutOfPublicLeaderboards 27 | 28 | await profiles.updateOne({ $set: { [`profiles.athena`]: profile} }); 29 | 30 | res.json({ 31 | accountId: profiles.accountId, 32 | optOutOfPublicLeaderboards: profile.stats.attributes.optOutOfPublicLeaderboards 33 | }).end(); 34 | }) 35 | 36 | module.exports = app; -------------------------------------------------------------------------------- /routes/reports.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | const { verifyToken } = require("../tokenManager/tokenVerify.js"); 4 | const User = require("../model/user.js"); 5 | const Profiles = require("../model/profiles.js"); 6 | const { Client, Intents, TextChannel } = require('discord.js'); 7 | const config = require('../Config/config.json'); 8 | const log = require("../structs/log.js"); 9 | 10 | app.post("/fortnite/api/game/v2/toxicity/account/:unsafeReporter/report/:reportedPlayer", verifyToken, async (req, res) => { 11 | if (config.bEnableReports === true) { 12 | try { 13 | log.debug(`POST /fortnite/api/game/v2/toxicity/account/${req.params.unsafeReporter}/report/${req.params.reportedPlayer} called`); 14 | 15 | const reporter = req.user.accountId; 16 | const reportedPlayer = req.params.reportedPlayer; 17 | 18 | log.debug(`Searching for reporter with accountId: ${reporter}`); 19 | let reporterData = await User.findOne({ accountId: reporter }).lean(); 20 | 21 | log.debug(`Searching for reported player with accountId: ${reportedPlayer}`); 22 | let reportedPlayerData = await User.findOne({ accountId: reportedPlayer }).lean(); 23 | let reportedPlayerDataProfile = await Profiles.findOne({ accountId: reportedPlayer }).lean(); 24 | 25 | if (!reportedPlayerData) { 26 | log.error(`Reported player with accountId: ${reportedPlayer} not found in the database`); 27 | return res.status(404).send({ "error": "Player not found" }); 28 | } 29 | 30 | const reason = req.body.reason || 'No reason provided'; 31 | const details = req.body.details || 'No details provided'; 32 | const playerAlreadyReported = reportedPlayerDataProfile.profiles?.totalReports ? 'Yes' : 'No'; 33 | 34 | log.debug(`Player already reported: ${playerAlreadyReported}`); 35 | 36 | const client = new Client({ 37 | intents: [ 38 | Intents.FLAGS.GUILDS, 39 | Intents.FLAGS.GUILD_MESSAGES, 40 | Intents.FLAGS.GUILD_MEMBERS, 41 | Intents.FLAGS.DIRECT_MESSAGES, 42 | Intents.FLAGS.DIRECT_MESSAGE_TYPING, 43 | Intents.FLAGS.DIRECT_MESSAGE_REACTIONS 44 | ] 45 | }); 46 | 47 | await Profiles.findOneAndUpdate( 48 | { accountId: reportedPlayer }, 49 | { $inc: { 'profiles.totalReports': 1 } }, 50 | { new: true, upsert: true } 51 | ).then((updatedProfile) => { 52 | log.debug(`Successfully updated totalReports to ${updatedProfile.profiles.totalReports} for accountId: ${reportedPlayer}`); 53 | }).catch((err) => { 54 | log.error(`Error updating totalReports for accountId: ${reportedPlayer}`, err); 55 | return res.status(500).send({ "error": "Database update error" }); 56 | }); 57 | 58 | await new Promise((resolve, reject) => { 59 | client.once('ready', async () => { 60 | 61 | try { 62 | const payload = { 63 | embeds: [{ 64 | title: 'New User Report', 65 | description: 'A new report has arrived!', 66 | color: 0xFFA500, 67 | fields: [ 68 | { 69 | name: "Reporting Player", 70 | value: reporterData.username, 71 | inline: true 72 | }, 73 | { 74 | name: "Reported Player", 75 | value: reportedPlayerData.username, 76 | inline: true 77 | }, 78 | { 79 | name: "Player already reported", 80 | value: playerAlreadyReported, 81 | inline: false 82 | }, 83 | { 84 | name: "Reason", 85 | value: reason, 86 | inline: true 87 | }, 88 | { 89 | name: "Additional Details", 90 | value: details, 91 | inline: true 92 | } 93 | ] 94 | }] 95 | }; 96 | 97 | const channel = await client.channels.fetch(config.bReportChannelId); 98 | 99 | if (channel instanceof TextChannel) { 100 | log.debug(`Sending embed to channel with ID: ${channel.id}`); 101 | const message = await channel.send({ 102 | embeds: [payload.embeds[0]] 103 | }); 104 | log.debug(`Message sent with ID: ${message.id}`); 105 | } else { 106 | log.error("The channel is not a valid text channel or couldn't be found."); 107 | } 108 | 109 | resolve(); 110 | } catch (error) { 111 | log.error('Error sending message:', error); 112 | reject(error); 113 | } 114 | }); 115 | 116 | client.login(config.discord.bot_token).catch((err) => { 117 | log.error("Error logging in Discord bot:", err); 118 | reject(err); 119 | }); 120 | }); 121 | 122 | return res.status(200).send({ "success": true }); 123 | } catch (error) { 124 | log.error(error); 125 | return res.status(500).send({ "error": "Internal server error" }); 126 | } 127 | } 128 | }); 129 | 130 | module.exports = app; -------------------------------------------------------------------------------- /routes/storefront.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | const Profile = require("../model/profiles.js"); 4 | const Friends = require("../model/friends.js"); 5 | const functions = require("../structs/functions.js"); 6 | const log = require("../structs/log.js"); 7 | const error = require("../structs/error.js"); 8 | 9 | const { verifyToken, verifyClient } = require("../tokenManager/tokenVerify.js"); 10 | const keychain = require("../responses/keychain.json"); 11 | 12 | app.get("/fortnite/api/storefront/v2/catalog", (req, res) => { 13 | log.debug("Request to /fortnite/api/storefront/v2/catalog"); 14 | if (req.headers["user-agent"] == undefined) return; 15 | if (req.headers["user-agent"].includes("2870186")) { 16 | return res.status(404).end(); 17 | } 18 | 19 | res.json(functions.getItemShop()); 20 | }); 21 | 22 | app.get("/fortnite/api/storefront/v2/gift/check_eligibility/recipient/:recipientId/offer/:offerId", verifyToken, async (req, res) => { 23 | log.debug(`Request to /fortnite/api/storefront/v2/gift/check_eligibility/recipient/${req.params.recipientId}/offer/${req.params.offerId}`); 24 | const findOfferId = functions.getOfferID(req.params.offerId); 25 | if (!findOfferId) return error.createError( 26 | "errors.com.epicgames.fortnite.id_invalid", 27 | `Offer ID (id: "${req.params.offerId}") not found`, 28 | [req.params.offerId], 16027, undefined, 400, res 29 | ); 30 | 31 | let sender = await Friends.findOne({ accountId: req.user.accountId }).lean(); 32 | 33 | if (!sender.list.accepted.find(i => i.accountId == req.params.recipientId) && req.params.recipientId != req.user.accountId) return error.createError( 34 | "errors.com.epicgames.friends.no_relationship", 35 | `User ${req.user.accountId} is not friends with ${req.params.recipientId}`, 36 | [req.user.accountId, req.params.recipientId], 28004, undefined, 403, res 37 | ); 38 | 39 | const profiles = await Profile.findOne({ accountId: req.params.recipientId }); 40 | 41 | let athena = profiles.profiles["athena"]; 42 | 43 | for (let itemGrant of findOfferId.offerId.itemGrants) { 44 | for (let itemId in athena.items) { 45 | if (itemGrant.templateId.toLowerCase() == athena.items[itemId].templateId.toLowerCase()) return error.createError( 46 | "errors.com.epicgames.modules.gamesubcatalog.purchase_not_allowed", 47 | `Could not purchase catalog offer ${findOfferId.offerId.devName}, item ${itemGrant.templateId}`, 48 | [findOfferId.offerId.devName, itemGrant.templateId], 28004, undefined, 403, res 49 | ); 50 | } 51 | } 52 | 53 | res.json({ 54 | price: findOfferId.offerId.prices[0], 55 | items: findOfferId.offerId.itemGrants 56 | }); 57 | }); 58 | 59 | app.get("/fortnite/api/storefront/v2/keychain", (req, res) => { 60 | log.debug("Request to /fortnite/api/storefront/v2/keychain"); 61 | res.json(keychain); 62 | }); 63 | 64 | app.get("/catalog/api/shared/bulk/offers", (req, res) => { 65 | log.debug("Request to /catalog/api/shared/bulk/offers"); 66 | res.json({}); 67 | }); 68 | 69 | module.exports = app; -------------------------------------------------------------------------------- /routes/version.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express.Router(); 3 | 4 | const { verifyToken, verifyClient } = require("../tokenManager/tokenVerify.js"); 5 | 6 | app.get("/fortnite/api/version", (req, res) => { 7 | res.json({ 8 | "app": "fortnite", 9 | "serverDate": new Date().toISOString(), 10 | "overridePropertiesVersion": "unknown", 11 | "cln": "17951730", 12 | "build": "444", 13 | "moduleName": "Fortnite-Core", 14 | "buildDate": "2021-10-27T21:00:51.697Z", 15 | "version": "18.30", 16 | "branch": "Release-18.30", 17 | "modules": { 18 | "Epic-LightSwitch-AccessControlCore": { 19 | "cln": "17237679", 20 | "build": "b2130", 21 | "buildDate": "2021-08-19T18:56:08.144Z", 22 | "version": "1.0.0", 23 | "branch": "trunk" 24 | }, 25 | "epic-xmpp-api-v1-base": { 26 | "cln": "5131a23c1470acbd9c94fae695ef7d899c1a41d6", 27 | "build": "b3595", 28 | "buildDate": "2019-07-30T09:11:06.587Z", 29 | "version": "0.0.1", 30 | "branch": "master" 31 | }, 32 | "epic-common-core": { 33 | "cln": "17909521", 34 | "build": "3217", 35 | "buildDate": "2021-10-25T18:41:12.486Z", 36 | "version": "3.0", 37 | "branch": "TRUNK" 38 | } 39 | } 40 | }); 41 | }); 42 | 43 | app.get("/fortnite/api*/versioncheck*", (req, res) => { 44 | res.json({ 45 | "type": "NO_UPDATE" 46 | }); 47 | }); 48 | 49 | app.get("/fortnite/api/v2/versioncheck/*", async (req, res) => { 50 | res.json({ 51 | "type": "NO_UPDATE" 52 | }) 53 | }) 54 | 55 | app.get("/fortnite/api/v2/versioncheck*", async (req, res) => { 56 | res.json({ 57 | "type": "NO_UPDATE" 58 | }) 59 | }) 60 | 61 | app.get("/fortnite/api/versioncheck*", async (req, res) => { 62 | res.json({ 63 | "type": "NO_UPDATE" 64 | }) 65 | }) 66 | 67 | module.exports = app; -------------------------------------------------------------------------------- /ssl/example_ca_bundle.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/ssl/example_ca_bundle.crt -------------------------------------------------------------------------------- /ssl/example_certificate.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/ssl/example_certificate.crt -------------------------------------------------------------------------------- /ssl/example_private.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-Reload/Reload-Backend/883fc794a70c62f8ba82da67e3fe71421a727453/ssl/example_private.key -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | title Reload Backend 3 | 4 | :start 5 | node index.js 6 | goto start 7 | -------------------------------------------------------------------------------- /structs/autobackendrestart.js: -------------------------------------------------------------------------------- 1 | const { exec } = require("child_process"); 2 | const log = require("../structs/log.js"); 3 | 4 | function parseRestartTime(restartTime) { 5 | const match = restartTime.match(/^(\d+)([ywdhms])$/); 6 | if (!match) return null; 7 | 8 | const value = parseInt(match[1], 10); 9 | const unit = match[2]; 10 | 11 | let milliseconds; 12 | switch (unit) { 13 | case 'y': milliseconds = value * 365 * 24 * 60 * 60 * 1000; break; 14 | case 'M': milliseconds = value * 30.44 * 24 * 60 * 60 * 1000; break; 15 | case 'w': milliseconds = value * 7 * 24 * 60 * 60 * 1000; break; 16 | case 'd': milliseconds = value * 24 * 60 * 60 * 1000; break; 17 | case 'h': milliseconds = value * 60 * 60 * 1000; break; 18 | case 'm': milliseconds = value * 60 * 1000; break; 19 | case 's': milliseconds = value * 1000; break; 20 | default: return null; 21 | } 22 | 23 | return milliseconds; 24 | } 25 | 26 | function formatTime(milliseconds) { 27 | const seconds = Math.floor((milliseconds / 1000) % 60); 28 | const minutes = Math.floor((milliseconds / (1000 * 60)) % 60); 29 | const hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); 30 | const days = Math.floor((milliseconds / (1000 * 60 * 60 * 24)) % 30); 31 | const months = Math.floor((milliseconds / (1000 * 60 * 60 * 24 * 30.44)) % 12); 32 | const years = Math.floor(milliseconds / (1000 * 60 * 60 * 24 * 365)); 33 | 34 | let timeString = ''; 35 | if (years > 0) timeString += `${years} year${years > 1 ? 's' : ''}, `; 36 | if (months > 0) timeString += `${months} month${months > 1 ? 's' : ''}, `; 37 | if (days > 0) timeString += `${days} day${days > 1 ? 's' : ''}, `; 38 | if (hours > 0) timeString += `${hours} hour${hours > 1 ? 's' : ''}, `; 39 | if (minutes > 0) timeString += `${minutes} minute${minutes > 1 ? 's' : ''}, `; 40 | if (seconds > 0) timeString += `${seconds} second${seconds > 1 ? 's' : ''}`; 41 | 42 | return timeString.trim().replace(/,([^,]*)$/, ' and$1'); 43 | } 44 | 45 | function scheduleRestart(restartTime) { 46 | const restartDelay = parseRestartTime(restartTime); 47 | if (!restartDelay) { 48 | log.error("Invalid 'bRestartTime' format"); 49 | return; 50 | } 51 | 52 | const formattedTime = formatTime(restartDelay); 53 | log.autobackendrestart(`The backend will restart in ${formattedTime}`); 54 | 55 | if (restartDelay > 10000) { 56 | setTimeout(() => { 57 | log.autobackendrestart("The backend will restart shortly..."); 58 | }, restartDelay - 10000); 59 | } else { 60 | setTimeout(() => { 61 | log.autobackendrestart("The backend will restart shortly..."); 62 | }, restartDelay); 63 | } 64 | 65 | setTimeout(() => { 66 | console.clear(); 67 | 68 | exec("node index.js", (error, stdout, stderr) => { 69 | if (error) { 70 | log.error(`Error restarting backend: ${error.message}`); 71 | return; 72 | } 73 | log.autobackendrestart("Backend restarted successfully"); 74 | }); 75 | 76 | process.exit(0); 77 | }, restartDelay); 78 | } 79 | 80 | module.exports = { scheduleRestart }; -------------------------------------------------------------------------------- /structs/checkforupdate.js: -------------------------------------------------------------------------------- 1 | const log = require("../structs/log.js"); 2 | const fetch = require("node-fetch"); 3 | 4 | class CheckForUpdate { 5 | static async checkForUpdate(currentVersion) { 6 | try { 7 | const response = await fetch('https://raw.githubusercontent.com/Project-Reload/Reload-Backend/refs/heads/main/package.json'); 8 | if (!response.ok) { 9 | log.error(`Failed to fetch package.json. Status: ${response.status}`); 10 | return false; 11 | } 12 | 13 | const packageJson = await response.json(); 14 | const latestVersion = packageJson.version; 15 | 16 | if (isNewerVersion(latestVersion, currentVersion)) { 17 | log.checkforupdate(`A new version of the Backend has been released! ${currentVersion} -> ${latestVersion}, Download it from the GitHub repo.`); 18 | return true; 19 | } else { 20 | 21 | } 22 | 23 | return false; 24 | } catch (error) { 25 | log.error(`Error while checking for updates: ${error.message}`); 26 | return false; 27 | } 28 | } 29 | } 30 | 31 | function isNewerVersion(latest, current) { 32 | const latestParts = latest.split('.').map(Number); 33 | const currentParts = current.split('.').map(Number); 34 | 35 | for (let i = 0; i < latestParts.length; i++) { 36 | if (latestParts[i] > (currentParts[i] || 0)) { 37 | return true; 38 | } else if (latestParts[i] < (currentParts[i] || 0)) { 39 | return false; 40 | } 41 | } 42 | 43 | return false; 44 | } 45 | 46 | module.exports = CheckForUpdate; -------------------------------------------------------------------------------- /structs/error.js: -------------------------------------------------------------------------------- 1 | function createError(errorCode, errorMessage, messageVars, numericErrorCode, error, statusCode, res) { 2 | res.set({ 3 | 'X-Epic-Error-Name': errorCode, 4 | 'X-Epic-Error-Code': numericErrorCode 5 | }); 6 | 7 | res.status(statusCode).json({ 8 | errorCode: errorCode, 9 | errorMessage: errorMessage, 10 | messageVars: messageVars, 11 | numericErrorCode: numericErrorCode, 12 | originatingService: "any", 13 | intent: "prod", 14 | error_description: errorMessage, 15 | error: error 16 | }); 17 | } 18 | 19 | module.exports = { 20 | createError 21 | } -------------------------------------------------------------------------------- /structs/kv.js: -------------------------------------------------------------------------------- 1 | const Redis = require('ioredis'); 2 | const Keyv = require('keyv'); 3 | 4 | //Don't touch "Use_Redis" 5 | const Use_Redis = false; 6 | const memkv = new Keyv(); 7 | const redis = Use_Redis ? new Redis() : null; 8 | 9 | class KV { 10 | async get(key) { 11 | if (Use_Redis && redis) { 12 | return await redis.get(key); 13 | } else { 14 | return await memkv.get(key); 15 | } 16 | } 17 | 18 | async set(key, value) { 19 | if (Use_Redis && redis) { 20 | const set = await redis.set(key, value); 21 | return set === 'OK'; 22 | } else { 23 | const set = await memkv.set(key, value); 24 | return set; 25 | } 26 | } 27 | 28 | async setTTL(key, value, ttl) { 29 | if (Use_Redis && redis) { 30 | const set = await redis.set(key, value, 'EX', ttl); 31 | return set === 'OK'; 32 | } else { 33 | const set = await memkv.set(key, value, ttl); 34 | return set; 35 | } 36 | } 37 | } 38 | 39 | module.exports = new KV(); -------------------------------------------------------------------------------- /structs/log.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const config = JSON.parse(fs.readFileSync("./Config/config.json").toString()); 3 | 4 | function getTimestamp() { 5 | const now = new Date(); 6 | const date = now.toLocaleDateString('en-US'); 7 | const time = now.toLocaleTimeString(); 8 | 9 | return `${date} ${time}`; 10 | } 11 | 12 | function formatLog(prefixColor, prefix, ...args) { 13 | let msg = args.join(" "); 14 | let formattedMessage = `${prefixColor}[${getTimestamp()}] ${prefix}\x1b[0m: ${msg}`; 15 | console.log(formattedMessage); 16 | } 17 | 18 | function backend(...args) { 19 | let msg = args.join(" "); 20 | if (config.bEnableFormattedLogs) { 21 | formatLog("\x1b[32m", "Reload Backend Log", ...args); 22 | } else { 23 | console.log(`\x1b[32mReload Backend Log\x1b[0m: ${msg}`); 24 | } 25 | } 26 | 27 | function bot(...args) { 28 | let msg = args.join(" "); 29 | if (config.bEnableFormattedLogs) { 30 | formatLog("\x1b[33m", "Reload Bot Log", ...args); 31 | } else { 32 | console.log(`\x1b[33mReload Bot Log\x1b[0m: ${msg}`); 33 | } 34 | } 35 | 36 | function xmpp(...args) { 37 | let msg = args.join(" "); 38 | if (config.bEnableFormattedLogs) { 39 | formatLog("\x1b[34m", "Reload Xmpp Log", ...args); 40 | } else { 41 | console.log(`\x1b[34mReload Xmpp Log\x1b[0m: ${msg}`); 42 | } 43 | } 44 | 45 | function error(...args) { 46 | let msg = args.join(" "); 47 | if (config.bEnableFormattedLogs) { 48 | formatLog("\x1b[31m", "Reload Error Log", ...args); 49 | } else { 50 | console.log(`\x1b[31mReload Error Log\x1b[0m: ${msg}`); 51 | } 52 | } 53 | 54 | function debug(...args) { 55 | if (config.bEnableDebugLogs) { 56 | let msg = args.join(" "); 57 | if (config.bEnableFormattedLogs) { 58 | formatLog("\x1b[35m", "Reload Debug Log", ...args); 59 | } else { 60 | console.log(`\x1b[35mReload Debug Log\x1b[0m: ${msg}`); 61 | } 62 | } 63 | } 64 | 65 | function website(...args) { 66 | let msg = args.join(" "); 67 | if (config.bEnableFormattedLogs) { 68 | formatLog("\x1b[36m", "Reload Website Log", ...args); 69 | } else { 70 | console.log(`\x1b[36mReload Website Log\x1b[0m: ${msg}`); 71 | } 72 | } 73 | 74 | function AutoRotation(...args) { 75 | if (config.bEnableAutoRotateDebugLogs) { 76 | let msg = args.join(" "); 77 | if (config.bEnableFormattedLogs) { 78 | formatLog("\x1b[36m", "Reload AutoRotation Debug Log", ...args); 79 | } else { 80 | console.log(`\x1b[36mReload AutoRotation Debug Log\x1b[0m: ${msg}`); 81 | } 82 | } 83 | } 84 | 85 | function checkforupdate(...args) { 86 | let msg = args.join(" "); 87 | if (config.bEnableFormattedLogs) { 88 | formatLog("\x1b[33m", "Reload Update Log", ...args); 89 | } else { 90 | console.log(`\x1b[33mReload Update Log\x1b[0m: ${msg}`); 91 | } 92 | } 93 | 94 | function autobackendrestart(...args) { 95 | let msg = args.join(" "); 96 | if (config.bEnableFormattedLogs) { 97 | formatLog("\x1b[92m", "Reload Auto Backend Restart Log", ...args); 98 | } else { 99 | console.log(`\x1b[92mReload Auto Backend Restart\x1b[0m: ${msg}`); 100 | } 101 | } 102 | 103 | function calderaservice(...args) { 104 | let msg = args.join(" "); 105 | if (config.bEnableFormattedLogs) { 106 | formatLog("\x1b[91m", "Caldera Service Log", ...args); 107 | } else { 108 | console.log(`\x1b[91mCaldera Service\x1b[0m: ${msg}`); 109 | } 110 | } 111 | 112 | module.exports = { 113 | backend, 114 | bot, 115 | xmpp, 116 | error, 117 | debug, 118 | website, 119 | AutoRotation, 120 | checkforupdate, 121 | autobackendrestart, 122 | calderaservice 123 | }; -------------------------------------------------------------------------------- /structs/profile.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const UserStats = require("../model/userstats.js"); 3 | 4 | function createProfiles(accountId) { 5 | let profiles = {}; 6 | 7 | fs.readdirSync("./Config/DefaultProfiles").forEach(fileName => { 8 | const profile = require(`../Config/DefaultProfiles/${fileName}`); 9 | 10 | profile.accountId = accountId; 11 | profile.created = new Date().toISOString(); 12 | profile.updated = new Date().toISOString(); 13 | 14 | profiles[profile.profileId] = profile; 15 | }); 16 | 17 | return profiles; 18 | } 19 | 20 | async function createUserStatsProfiles(accountId) { 21 | await UserStats.create({ 22 | created: new Date().toISOString(), 23 | accountId: accountId, 24 | solo: { 25 | placetop1: 0, 26 | placetop10: 0, 27 | placetop25: 0, 28 | score: 0, 29 | kills: 0, 30 | deaths: 0, 31 | kd: 0, 32 | matchesplayed: 0, 33 | minutesplayed: 0, 34 | playersoutlived: 0 35 | }, 36 | duo: { 37 | placetop1: 0, 38 | placetop5: 0, 39 | placetop12: 0, 40 | score: 0, 41 | kills: 0, 42 | deaths: 0, 43 | kd: 0, 44 | matchesplayed: 0, 45 | minutesplayed: 0, 46 | playersoutlived: 0 47 | }, 48 | trio: { 49 | placetop1: 0, 50 | placetop3: 0, 51 | placetop6: 0, 52 | score: 0, 53 | kills: 0, 54 | deaths: 0, 55 | kd: 0, 56 | matchesplayed: 0, 57 | minutesplayed: 0, 58 | playersoutlived: 0 59 | }, 60 | squad: { 61 | placetop1: 0, 62 | placetop3: 0, 63 | placetop6: 0, 64 | score: 0, 65 | kills: 0, 66 | deaths: 0, 67 | kd: 0, 68 | matchesplayed: 0, 69 | minutesplayed: 0, 70 | playersoutlived: 0 71 | }, 72 | ltm: { 73 | wins: 0, 74 | score: 0, 75 | kills: 0, 76 | deaths: 0, 77 | kd: 0, 78 | matchesplayed: 0, 79 | minutesplayed: 0, 80 | playersoutlived: 0 81 | }, 82 | }); 83 | } 84 | 85 | async function validateProfile(profileId, profiles) { 86 | try { 87 | let profile = profiles.profiles[profileId]; 88 | 89 | if (!profile || !profileId) throw new Error("Invalid profile/profileId"); 90 | } catch { 91 | return false; 92 | } 93 | 94 | return true; 95 | } 96 | 97 | module.exports = { 98 | createProfiles, 99 | createUserStatsProfiles, 100 | validateProfile 101 | } -------------------------------------------------------------------------------- /tokenManager/tokenCreation.js: -------------------------------------------------------------------------------- 1 | const functions = require("../structs/functions.js"); 2 | const jwt = require("jsonwebtoken"); 3 | 4 | function createClient(clientId, grant_type, ip, expiresIn) { 5 | let clientToken = jwt.sign({ 6 | "p": Buffer.from(functions.MakeID()).toString("base64"), 7 | "clsvc": "fortnite", 8 | "t": "s", 9 | "mver": false, 10 | "clid": clientId, 11 | "ic": true, 12 | "am": grant_type, 13 | "jti": functions.MakeID().replace(/-/ig, ""), 14 | "creation_date": new Date(), 15 | "hours_expire": expiresIn 16 | }, global.JWT_SECRET, { expiresIn: `${expiresIn}h` }); 17 | 18 | global.clientTokens.push({ ip: ip, token: `eg1~${clientToken}` }); 19 | 20 | return clientToken; 21 | } 22 | 23 | function createAccess(user, clientId, grant_type, deviceId, expiresIn) { 24 | let accessToken = jwt.sign({ 25 | "app": "fortnite", 26 | "sub": user.accountId, 27 | "dvid": deviceId, 28 | "mver": false, 29 | "clid": clientId, 30 | "dn": user.username, 31 | "am": grant_type, 32 | "p": Buffer.from(functions.MakeID()).toString("base64"), 33 | "iai": user.accountId, 34 | "sec": 1, 35 | "clsvc": "fortnite", 36 | "t": "s", 37 | "ic": true, 38 | "jti": functions.MakeID().replace(/-/ig, ""), 39 | "creation_date": new Date(), 40 | "hours_expire": expiresIn 41 | }, global.JWT_SECRET, { expiresIn: `${expiresIn}h` }); 42 | 43 | global.accessTokens.push({ accountId: user.accountId, token: `eg1~${accessToken}` }); 44 | 45 | return accessToken; 46 | } 47 | 48 | function createRefresh(user, clientId, grant_type, deviceId, expiresIn) { 49 | let refreshToken = jwt.sign({ 50 | "sub": user.accountId, 51 | "dvid": deviceId, 52 | "t": "r", 53 | "clid": clientId, 54 | "am": grant_type, 55 | "jti": functions.MakeID().replace(/-/ig, ""), 56 | "creation_date": new Date(), 57 | "hours_expire": expiresIn 58 | }, global.JWT_SECRET, { expiresIn: `${expiresIn}h` }); 59 | 60 | global.refreshTokens.push({ accountId: user.accountId, token: `eg1~${refreshToken}` }); 61 | 62 | return refreshToken; 63 | } 64 | 65 | module.exports = { 66 | createClient, 67 | createAccess, 68 | createRefresh 69 | } -------------------------------------------------------------------------------- /tokenManager/tokenVerify.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | 3 | const User = require("../model/user.js"); 4 | const functions = require("../structs/functions.js"); 5 | const error = require("../structs/error.js"); 6 | 7 | async function verifyToken(req, res, next) { 8 | let authErr = () => error.createError( 9 | "errors.com.epicgames.common.authorization.authorization_failed", 10 | `Authorization failed for ${req.originalUrl}`, 11 | [req.originalUrl], 1032, undefined, 401, res 12 | ); 13 | 14 | if (!req.headers["authorization"] || !req.headers["authorization"].startsWith("bearer eg1~")) return authErr(); 15 | 16 | const token = req.headers["authorization"].replace("bearer eg1~", ""); 17 | 18 | try { 19 | const decodedToken = jwt.decode(token); 20 | 21 | if (!global.accessTokens.find(i => i.token == `eg1~${token}`)) throw new Error("Invalid token."); 22 | 23 | if (DateAddHours(new Date(decodedToken.creation_date), decodedToken.hours_expire).getTime() <= new Date().getTime()) { 24 | throw new Error("Expired access token."); 25 | } 26 | 27 | req.user = await User.findOne({ accountId: decodedToken.sub }).lean(); 28 | 29 | if (req.user.banned) return error.createError( 30 | "errors.com.epicgames.account.account_not_active", 31 | "You have been permanently banned from Fortnite.", 32 | [], -1, undefined, 400, res 33 | ); 34 | 35 | next(); 36 | } catch { 37 | let accessIndex = global.accessTokens.findIndex(i => i.token == `eg1~${token}`); 38 | if (accessIndex != -1) { 39 | global.accessTokens.splice(accessIndex, 1); 40 | 41 | functions.UpdateTokens(); 42 | } 43 | 44 | return authErr(); 45 | } 46 | } 47 | 48 | async function verifyClient(req, res, next) { 49 | let authErr = () => error.createError( 50 | "errors.com.epicgames.common.authorization.authorization_failed", 51 | `Authorization failed for ${req.originalUrl}`, 52 | [req.originalUrl], 1032, undefined, 401, res 53 | ); 54 | 55 | if (!req.headers["authorization"] || !req.headers["authorization"].startsWith("bearer eg1~")) return authErr(); 56 | 57 | const token = req.headers["authorization"].replace("bearer eg1~", ""); 58 | 59 | try { 60 | const decodedToken = jwt.decode(token); 61 | 62 | let findAccess = global.accessTokens.find(i => i.token == `eg1~${token}`); 63 | 64 | if (!findAccess && !global.clientTokens.find(i => i.token == `eg1~${token}`)) throw new Error("Invalid token."); 65 | 66 | if (DateAddHours(new Date(decodedToken.creation_date), decodedToken.hours_expire).getTime() <= new Date().getTime()) { 67 | throw new Error("Expired access/client token."); 68 | } 69 | 70 | if (findAccess) { 71 | req.user = await User.findOne({ accountId: decodedToken.sub }).lean(); 72 | 73 | if (req.user.banned) return error.createError( 74 | "errors.com.epicgames.account.account_not_active", 75 | "You have been permanently banned from Fortnite.", 76 | [], -1, undefined, 400, res 77 | ); 78 | } 79 | 80 | next(); 81 | } catch (err) { 82 | let accessIndex = global.accessTokens.findIndex(i => i.token == `eg1~${token}`); 83 | if (accessIndex != -1) global.accessTokens.splice(accessIndex, 1); 84 | 85 | let clientIndex = global.clientTokens.findIndex(i => i.token == `eg1~${token}`); 86 | if (clientIndex != -1) global.clientTokens.splice(clientIndex, 1); 87 | 88 | if (accessIndex != -1 || clientIndex != -1) functions.UpdateTokens(); 89 | 90 | return authErr(); 91 | } 92 | } 93 | 94 | function DateAddHours(pdate, number) { 95 | let date = pdate; 96 | date.setHours(date.getHours() + number); 97 | 98 | return date; 99 | } 100 | 101 | module.exports = { 102 | verifyToken, 103 | verifyClient 104 | } -------------------------------------------------------------------------------- /tokenManager/tokens.json: -------------------------------------------------------------------------------- 1 | { 2 | "accessTokens": [], 3 | "refreshTokens": [], 4 | "clientTokens": [] 5 | } --------------------------------------------------------------------------------