fnlb 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -161
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,162 +1,162 @@
|
|
|
1
|
-
# 🚀 FNLB – Self-Host Your Own Fortnite Bot
|
|
2
|
-
|
|
3
|
-
Easily run your own bot using **[FNLB](https://fnlb.net)**, a powerful and scalable system for managing Fortnite bots.
|
|
4
|
-
Whether you're running one bot or hundreds, FNLB gives you full control.
|
|
5
|
-
|
|
6
|
-
## 📦 Installation
|
|
7
|
-
|
|
8
|
-
Install the latest version via **npm install**:
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npm install fnlb@latest
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
Using **[bun](https://bun.sh)**? Install the latest version via **bun install**:
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
bun install fnlb@latest
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## 🔗 Useful Links
|
|
21
|
-
|
|
22
|
-
- 🌐 **[FNLB Website](https://fnlb.net)**
|
|
23
|
-
- 📖 **[FNLB Documentation](https://docs.fnlb.net/introduction)**
|
|
24
|
-
- 📄 **[FNLB Changelog](https://docs.fnlb.net/bots/changelog)**
|
|
25
|
-
- 🗨️ **[FNLB Discord Server](https://fnlb.net/discord)**
|
|
26
|
-
|
|
27
|
-
## 🤖 Starting a Single Bot
|
|
28
|
-
|
|
29
|
-
Get started with a single bot using your [FNLB API token](https://app.fnlb.net/account):
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
import FNLB from 'fnlb';
|
|
33
|
-
|
|
34
|
-
const fnlb = new FNLB();
|
|
35
|
-
|
|
36
|
-
await fnlb.start({
|
|
37
|
-
apiToken: 'your-api-token', // 🔑 Replace with your actual token from https://app.fnlb.net/account
|
|
38
|
-
categories: ['your-category-id'] // Get the category of your bot on https://app.fnlb.net/bots -> Click on your bot -> About this bot -> Category ID
|
|
39
|
-
});
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## 🔁 Running Multiple Bots (Same Shard)
|
|
43
|
-
|
|
44
|
-
Run multiple bots in a **single subprocess** using the `botsPerShard` option:
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
import FNLB from 'fnlb';
|
|
48
|
-
|
|
49
|
-
const fnlb = new FNLB();
|
|
50
|
-
|
|
51
|
-
await fnlb.start({
|
|
52
|
-
apiToken: 'your-api-token',
|
|
53
|
-
categories: ['your-category-id'],
|
|
54
|
-
botsPerShard: 10 // 🧱 Spawns max 10 bots
|
|
55
|
-
});
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## 🧩 Using Multiple Shards (Subprocesses)
|
|
59
|
-
|
|
60
|
-
Scale even more by spawning multiple **shards** (subprocesses) with multiple bots each:
|
|
61
|
-
|
|
62
|
-
```ts
|
|
63
|
-
import FNLB from 'fnlb';
|
|
64
|
-
|
|
65
|
-
const fnlb = new FNLB();
|
|
66
|
-
|
|
67
|
-
await fnlb.start({
|
|
68
|
-
apiToken: 'your-api-token',
|
|
69
|
-
categories: ['your-category-id'],
|
|
70
|
-
numberOfShards: 2, // 2 shards 🧩
|
|
71
|
-
botsPerShard: 10 // max 10 bots per shard 🤖
|
|
72
|
-
});
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
> 💡 Total bots: `numberOfShards × botsPerShard`
|
|
76
|
-
> In this example: 2 × 10 = **20 bots max**
|
|
77
|
-
|
|
78
|
-
## 🗂️ Launching Bots Across Multiple Categories
|
|
79
|
-
|
|
80
|
-
Want to run bots from different FNLB categories? Just add them to the `categories` array:
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
import FNLB from 'fnlb';
|
|
84
|
-
|
|
85
|
-
const fnlb = new FNLB();
|
|
86
|
-
|
|
87
|
-
await fnlb.start({
|
|
88
|
-
apiToken: 'your-api-token',
|
|
89
|
-
categories: ['category-id-1', 'category-id-2'], // 🔄 Multi-category support
|
|
90
|
-
numberOfShards: 2,
|
|
91
|
-
botsPerShard: 10
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## ⛔ Stopping All Bots
|
|
96
|
-
|
|
97
|
-
Shut everything down cleanly using the `stop()` method:
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
import FNLB from 'fnlb';
|
|
101
|
-
|
|
102
|
-
const fnlb = new FNLB();
|
|
103
|
-
|
|
104
|
-
await fnlb.start({
|
|
105
|
-
apiToken: 'your-api-token',
|
|
106
|
-
numberOfShards: 2,
|
|
107
|
-
botsPerShard: 10
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
await fnlb.stop(); // 🛑 Stops all shards and bots
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## 🏷️ Naming Your Cluster
|
|
114
|
-
|
|
115
|
-
Customize your cluster with a unique name using the `clusterName` option:
|
|
116
|
-
|
|
117
|
-
```ts
|
|
118
|
-
import FNLB from 'fnlb';
|
|
119
|
-
|
|
120
|
-
const fnlb = new FNLB({ clusterName: 'MyAwesomeCluster' });
|
|
121
|
-
|
|
122
|
-
await fnlb.start({
|
|
123
|
-
apiToken: 'your-api-token'
|
|
124
|
-
});
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## 🔄 Auto-Restart Every Hour (Optional)
|
|
128
|
-
|
|
129
|
-
Want to keep things fresh? Here's how to restart your bots automatically every hour:
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
import FNLB from 'fnlb';
|
|
133
|
-
|
|
134
|
-
const fnlb = new FNLB();
|
|
135
|
-
|
|
136
|
-
async function startFNLB() {
|
|
137
|
-
await fnlb.start({
|
|
138
|
-
apiToken: 'your-api-token',
|
|
139
|
-
numberOfShards: 1,
|
|
140
|
-
botsPerShard: 5,
|
|
141
|
-
categories: ['your-category-id']
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
async function restartFNLB() {
|
|
146
|
-
console.log('🔁 Restarting FNLB...');
|
|
147
|
-
await fnlb.stop();
|
|
148
|
-
await startFNLB();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
await startFNLB();
|
|
152
|
-
|
|
153
|
-
// ⏱️ Restart every hour (3600000 ms)
|
|
154
|
-
setInterval(restartFNLB, 3_600_000);
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## 🗨️ Join the Community
|
|
158
|
-
|
|
159
|
-
Need help, support, or just want to chat with other developers?
|
|
160
|
-
Come hang out with us on Discord! 👇
|
|
161
|
-
|
|
1
|
+
# 🚀 FNLB – Self-Host Your Own Fortnite Bot
|
|
2
|
+
|
|
3
|
+
Easily run your own bot using **[FNLB](https://fnlb.net)**, a powerful and scalable system for managing Fortnite bots.
|
|
4
|
+
Whether you're running one bot or hundreds, FNLB gives you full control.
|
|
5
|
+
|
|
6
|
+
## 📦 Installation
|
|
7
|
+
|
|
8
|
+
Install the latest version via **npm install**:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install fnlb@latest
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Using **[bun](https://bun.sh)**? Install the latest version via **bun install**:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun install fnlb@latest
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 🔗 Useful Links
|
|
21
|
+
|
|
22
|
+
- 🌐 **[FNLB Website](https://fnlb.net)**
|
|
23
|
+
- 📖 **[FNLB Documentation](https://docs.fnlb.net/introduction)**
|
|
24
|
+
- 📄 **[FNLB Changelog](https://docs.fnlb.net/bots/changelog)**
|
|
25
|
+
- 🗨️ **[FNLB Discord Server](https://fnlb.net/discord)**
|
|
26
|
+
|
|
27
|
+
## 🤖 Starting a Single Bot
|
|
28
|
+
|
|
29
|
+
Get started with a single bot using your [FNLB API token](https://app.fnlb.net/account):
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import FNLB from 'fnlb';
|
|
33
|
+
|
|
34
|
+
const fnlb = new FNLB();
|
|
35
|
+
|
|
36
|
+
await fnlb.start({
|
|
37
|
+
apiToken: 'your-api-token', // 🔑 Replace with your actual token from https://app.fnlb.net/account
|
|
38
|
+
categories: ['your-category-id'] // Get the category of your bot on https://app.fnlb.net/bots -> Click on your bot -> About this bot -> Category ID
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 🔁 Running Multiple Bots (Same Shard)
|
|
43
|
+
|
|
44
|
+
Run multiple bots in a **single subprocess** using the `botsPerShard` option:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import FNLB from 'fnlb';
|
|
48
|
+
|
|
49
|
+
const fnlb = new FNLB();
|
|
50
|
+
|
|
51
|
+
await fnlb.start({
|
|
52
|
+
apiToken: 'your-api-token',
|
|
53
|
+
categories: ['your-category-id'],
|
|
54
|
+
botsPerShard: 10 // 🧱 Spawns max 10 bots
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 🧩 Using Multiple Shards (Subprocesses)
|
|
59
|
+
|
|
60
|
+
Scale even more by spawning multiple **shards** (subprocesses) with multiple bots each:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import FNLB from 'fnlb';
|
|
64
|
+
|
|
65
|
+
const fnlb = new FNLB();
|
|
66
|
+
|
|
67
|
+
await fnlb.start({
|
|
68
|
+
apiToken: 'your-api-token',
|
|
69
|
+
categories: ['your-category-id'],
|
|
70
|
+
numberOfShards: 2, // 2 shards 🧩
|
|
71
|
+
botsPerShard: 10 // max 10 bots per shard 🤖
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> 💡 Total bots: `numberOfShards × botsPerShard`
|
|
76
|
+
> In this example: 2 × 10 = **20 bots max**
|
|
77
|
+
|
|
78
|
+
## 🗂️ Launching Bots Across Multiple Categories
|
|
79
|
+
|
|
80
|
+
Want to run bots from different FNLB categories? Just add them to the `categories` array:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import FNLB from 'fnlb';
|
|
84
|
+
|
|
85
|
+
const fnlb = new FNLB();
|
|
86
|
+
|
|
87
|
+
await fnlb.start({
|
|
88
|
+
apiToken: 'your-api-token',
|
|
89
|
+
categories: ['category-id-1', 'category-id-2'], // 🔄 Multi-category support
|
|
90
|
+
numberOfShards: 2,
|
|
91
|
+
botsPerShard: 10
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## ⛔ Stopping All Bots
|
|
96
|
+
|
|
97
|
+
Shut everything down cleanly using the `stop()` method:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import FNLB from 'fnlb';
|
|
101
|
+
|
|
102
|
+
const fnlb = new FNLB();
|
|
103
|
+
|
|
104
|
+
await fnlb.start({
|
|
105
|
+
apiToken: 'your-api-token',
|
|
106
|
+
numberOfShards: 2,
|
|
107
|
+
botsPerShard: 10
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await fnlb.stop(); // 🛑 Stops all shards and bots
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 🏷️ Naming Your Cluster
|
|
114
|
+
|
|
115
|
+
Customize your cluster with a unique name using the `clusterName` option:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import FNLB from 'fnlb';
|
|
119
|
+
|
|
120
|
+
const fnlb = new FNLB({ clusterName: 'MyAwesomeCluster' });
|
|
121
|
+
|
|
122
|
+
await fnlb.start({
|
|
123
|
+
apiToken: 'your-api-token'
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## 🔄 Auto-Restart Every Hour (Optional)
|
|
128
|
+
|
|
129
|
+
Want to keep things fresh? Here's how to restart your bots automatically every hour:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import FNLB from 'fnlb';
|
|
133
|
+
|
|
134
|
+
const fnlb = new FNLB();
|
|
135
|
+
|
|
136
|
+
async function startFNLB() {
|
|
137
|
+
await fnlb.start({
|
|
138
|
+
apiToken: 'your-api-token',
|
|
139
|
+
numberOfShards: 1,
|
|
140
|
+
botsPerShard: 5,
|
|
141
|
+
categories: ['your-category-id']
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function restartFNLB() {
|
|
146
|
+
console.log('🔁 Restarting FNLB...');
|
|
147
|
+
await fnlb.stop();
|
|
148
|
+
await startFNLB();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
await startFNLB();
|
|
152
|
+
|
|
153
|
+
// ⏱️ Restart every hour (3600000 ms)
|
|
154
|
+
setInterval(restartFNLB, 3_600_000);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 🗨️ Join the Community
|
|
158
|
+
|
|
159
|
+
Need help, support, or just want to chat with other developers?
|
|
160
|
+
Come hang out with us on Discord! 👇
|
|
161
|
+
|
|
162
162
|
[](https://fnlb.net/discord)
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fork as u}from"node:child_process";import{createHash as f}from"node:crypto";import{mkdir as g,readFile as m,writeFile as w}from"node:fs/promises";import{resolve as S}from"node:path";class
|
|
1
|
+
import{fork as u}from"node:child_process";import{createHash as f}from"node:crypto";import{mkdir as g,readFile as m,writeFile as w}from"node:fs/promises";import{resolve as S}from"node:path";class d{static wait(t){return new Promise((r)=>setTimeout(r,t))}}class l{config;activeProcesses=new Map;packageName=`${process.versions.bun?"zenith-bun":"zenith"}`;isLoaded=!1;shouldRestart=!0;runId=0;constructor(t){this.config=t}async start(t){await this.stop(),this.shouldRestart=!0,this.runId++;let r=this.runId;if(!t?.apiToken)throw new Error("[FNLB ShardingManager] Please provide a FNLB API token.");await this.update();let o=t.numberOfShards??1,h=(~~(Math.random()*1e4)).toString(36)+"fnlb"+(~~(Date.now()/1000)).toString(36);for(let i=0;i<o;i++){let e=`${h}-${i.toString().padStart(2,"0")}`,n=await this.startShard(t,e,r);this.activeProcesses.set(e,n)}}async stop(){if(this.shouldRestart=!1,this.runId++,this.activeProcesses.size===0)return;this.log("Stopping all active shards...");for(let[t,r]of this.activeProcesses)this.log(`Stopping shard with ID: ${t}`),r.kill();this.activeProcesses.clear(),this.log("All shards stopped.")}async startShard(t,r,o){if(await this.update(),!t?.apiToken||t.apiToken.length<10)throw new Error("[FNLB ShardingManager] Please provide a valid FNLB API token.");this.log("Starting shard with ID:",r);let h=u(`./.fnlb/${this.packageName}.mjs`,[],{env:{...process.env,FORCE_COLOR:"1",SHARD_ID:r,API_TOKEN:t.apiToken,CATEGORIES:t.categories?.join(","),BOTS_PER_SHARD:(t.botsPerShard??1).toString(),HIDE_USERNAMES:t.hideUsernames?"true":"false",HIDE_EMAILS:t.hideEmails?"true":"false",LOG_LEVEL:t.logLevel,CLUSTER_ID:this.config?.clusterName?.trim().replace(/ +(?= )/g,"").toLowerCase().replaceAll(" ","-")??"unknown",CLUSTER_NAME:this.config?.clusterName?.trim()},stdio:["inherit","pipe","pipe","ipc"]});if(!this.config?.disableSubProcessLogs)h.stdout?.on("data",(i)=>{let e=i.toString("utf8");process.stdout.write(e),this.config?.onSubProcessLogMessage?.({timestamp:Date.now(),content:e,format:0})});if(!this.config?.disableSubProcessErrorLogs)h.stderr?.on("data",(i)=>{let e=i.toString("utf8");process.stderr.write(e),this.config?.onSubProcessLogMessage?.({timestamp:Date.now(),content:e,format:4})});return h.on("close",async(i)=>{if(this.activeProcesses.delete(r),this.shouldRestart&&o===this.runId){if(i===0)this.warn("Shard exited with code:",i);else this.error("Shard exited with code:",i?.toString()??"none");this.log("Trying to restart shard..."),await d.wait(1e4);let e=await this.startShard(t,r,o);this.activeProcesses.set(r,e)}else this.log(`Shard ${r} stopped.`)}),h}async update(){if(this.isLoaded)return;let t=S(`./.fnlb/${this.packageName}.mjs`),r=await m(t,"utf-8").catch(()=>null),o=this.config?.maxDownloadRetries||1/0,h=this.config?.maxBackoffMs||60000;this.log(r?"Checking for updates...":"Downloading FNLB...");let i=`https://dist.fnlb.net/packages/${this.packageName}/release`,e,n=0,c=1000;while(n<o)try{let s=await fetch(i);if(!s.ok)throw new Error(`Status code: ${s.status}`);e=await s.json();break}catch(s){let a=Math.min(c*2,h);if(n++,this.error("Update error:",s),this.warn(`Check for updates attempt ${n} failed: ${s.message}. Retrying in ${a>=60000?`${~~(a/60000)}m`:`${~~(a/1000)}s`}...`),n>=o)break;await new Promise((p)=>setTimeout(p,c)),c=a}if(!e){if(r){this.warn("Failed to check for updates. Using existing local version."),this.isLoaded=!0,this.success("Loaded existing FNLB version");return}throw new Error("[FNLB ShardingManager] Failed to check for updates and no local file found.")}if(r){if(f("sha256").update(r).digest("hex")===e.hash){this.success(`FNLB v${e.version} is up to date`),this.isLoaded=!0,this.success(`Finished loading FNLB v${e.version}`);return}this.log(`Downloading update for FNLB v${e.version}`)}n=0,c=1000;while(n<o)try{let s=await fetch(e.url);if(!s.ok)throw new Error(`Download failed with status ${s.status}`);let a=await s.text();if(f("sha256").update(a).digest("hex")!==e.hash)throw new Error("Downloaded file hash mismatch...");await g(".fnlb",{recursive:!0}),await w(t,a),this.isLoaded=!0,this.success(`Finished loading FNLB v${e.version}`);return}catch(s){let a=Math.min(c*2,h);n++,this.error("Download error:",s),this.warn(`Download attempt ${n} failed: ${s.message}. Retrying in ${a>=60000?`${~~(a/60000)}m`:`${~~(a/1000)}s`}...`),await d.wait(c),c=a}if(r){this.warn("Max retries reached. Using existing local version."),this.isLoaded=!0,this.success("Loaded existing FNLB version");return}throw new Error(`[FNLB ShardingManager] Failed to download and verify update after ${n} attempts`)}log(...t){if(!this.config?.disableLogs)console.log("[FNLB ShardingManager]",...t),this.config?.onLogMessage?.({timestamp:Date.now(),content:t.join(" "),format:0})}success(...t){if(!this.config?.disableLogs)console.log("[FNLB ShardingManager] [OK]",...t),this.config?.onLogMessage?.({timestamp:Date.now(),content:t.join(" "),format:1})}warn(...t){if(!this.config?.disableErrorLogs)console.warn("[FNLB ShardingManager] [WRN]",...t),this.config?.onLogMessage?.({timestamp:Date.now(),content:t.join(" "),format:3})}error(...t){if(!this.config?.disableErrorLogs)console.error("[FNLB ShardingManager] [ERR]",...t),this.config?.onLogMessage?.({timestamp:Date.now(),content:t.join(" "),format:4})}}var L;((o)=>{o.Info="INFO";o.Debug="DEBUG"})(L||={});var M=l;export{M as default,L as LogLevel};
|