├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── ISSUE_TEMPLATE
│ ├── bug-report-or-feature-request.md
│ └── custom.md
├── dependabot.yml
└── media
│ ├── bg.mp4
│ ├── bg_1.gif
│ ├── bg_3.gif
│ ├── custom_node.png
│ ├── custom_node2.png
│ ├── logo.png
│ ├── logo2.png
│ ├── logo3.png
│ └── node_example.png
├── .gitignore
├── LICENSE
├── README.md
├── backend
├── config
│ └── ssl.js
├── flow_functions
│ ├── attack_entity.js
│ ├── chat.js
│ ├── collect_items.js
│ ├── craft_item.js
│ ├── disconnect_on_player_proximity.js
│ ├── drop_item.js
│ ├── eat_food.js
│ ├── elytra_fly_to_location.js
│ ├── equip_item.js
│ ├── find_item_in_chests.js
│ ├── functions.json
│ ├── get_item_from_chest.js
│ ├── get_item_from_nearby_chest.js
│ ├── javascript_code_executor.js
│ ├── look_at.js
│ ├── mine.js
│ ├── place_block.js
│ ├── place_block_nearby.js
│ ├── say_in_chat.js
│ ├── sleep.js
│ ├── smelt_item.js
│ ├── test.js
│ ├── toggle_options.js
│ ├── use_block.js
│ ├── wait_for_chat_message.js
│ └── walk_to.js
├── flows
│ └── crafting_table.json
├── main.js
├── package.json
├── scripts
│ └── setup-https.js
└── utils.js
├── frontend
├── .env.development
├── .eslintrc.json
├── .gitignore
├── components.json
├── next.config.mjs
├── package.json
├── postcss.config.mjs
├── public
│ ├── fonts
│ │ ├── MinecraftBold.otf
│ │ ├── MinecraftBoldItalic.otf
│ │ ├── MinecraftItalic.otf
│ │ └── MinecraftRegular.otf
│ ├── next.svg
│ ├── pixel_images
│ │ ├── book.crop.png
│ │ └── book.png
│ └── vercel.svg
├── src
│ ├── app
│ │ ├── deliver
│ │ │ └── page.tsx
│ │ ├── favicon.ico
│ │ ├── flow
│ │ │ └── page.tsx
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── map
│ │ │ ├── App.css
│ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── test
│ │ │ └── page.tsx
│ ├── components
│ │ ├── login.tsx
│ │ ├── modeswitch.tsx
│ │ ├── theme-provider.tsx
│ │ └── ui
│ │ │ ├── alert-dialog.tsx
│ │ │ ├── badge.tsx
│ │ │ ├── button.tsx
│ │ │ ├── card.tsx
│ │ │ ├── chart.tsx
│ │ │ ├── checkbox.tsx
│ │ │ ├── collapsible.tsx
│ │ │ ├── command.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── drawer.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── form.tsx
│ │ │ ├── input-otp.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── popover.tsx
│ │ │ ├── resizable.tsx
│ │ │ ├── scroll-area.tsx
│ │ │ ├── select.tsx
│ │ │ ├── separator.tsx
│ │ │ ├── sheet.tsx
│ │ │ ├── slider.tsx
│ │ │ ├── switch.tsx
│ │ │ ├── table.tsx
│ │ │ ├── textarea.tsx
│ │ │ ├── toast.tsx
│ │ │ ├── toaster.tsx
│ │ │ ├── tooltip.tsx
│ │ │ └── use-toast.ts
│ └── lib
│ │ ├── utils.ts
│ │ └── utils
│ │ └── useApiIp.ts
├── tailwind.config.ts
└── tsconfig.json
├── package-lock.json
└── package.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [silkepilon]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: silkepilon
14 | custom: [https://www.paypal.com/donate/?hosted_button_id=24FFQ9FFJCC76]
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Thank you for your interest in the KnowledgeBook project! As the owner and sole developer, I truly appreciate your enthusiasm and support.
2 |
3 | Before submitting an issue, please consider the following:
4 |
5 | **For support questions:**
6 | Please use Stack Overflow for general support inquiries. The issues section of this repository is reserved for bug reports and feature requests.
7 |
8 | **I'm submitting a:**
9 |
10 | - [ ] Bug report
11 | - [ ] Feature request
12 |
13 | **Please describe the current behavior:**
14 |
15 | **If reporting a bug, please provide steps to reproduce and, if possible, a minimal example demonstrating the issue:**
16 |
17 | **What is the expected behavior?**
18 |
19 | **What is the motivation or use case for changing the behavior?**
20 |
21 | **Please provide information about your environment:**
22 |
23 | - KnowledgeBook Version:
24 | - Node.js Version:
25 | - Operating System:
26 | - Browser (if applicable):
27 |
28 | **Additional context:**
29 | Please provide any other relevant information, such as related issues, suggestions for fixes, or links to helpful resources.
30 |
31 | **Supporting the Project:**
32 | If you find KnowledgeBook useful and would like to support its development, consider sponsoring or donating to the project. You can find donation buttons and links in the main README file of the repository. Your support helps maintain and improve the project!
33 |
34 | I appreciate your patience as I review and respond to submissions. As a solo developer, there may be some delay in addressing issues and requests, but I am committed to improving KnowledgeBook and value your input.
35 |
36 | Thank you for contributing to and supporting the project!
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report-or-feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report or Feature request
3 | about: Create a report to help me improve or fix things
4 | title: ''
5 | labels: bug, enhancement
6 | assignees: SilkePilon
7 |
8 | ---
9 |
10 | Thank you for your interest in the KnowledgeBook project! As the owner and sole developer, I truly appreciate your enthusiasm and support.
11 |
12 | Before submitting an issue, please consider the following:
13 |
14 | **For support questions:**
15 | Please use Stack Overflow for general support inquiries. The issues section of this repository is reserved for bug reports and feature requests.
16 |
17 | **I'm submitting a:**
18 |
19 | - [ ] Bug report
20 | - [ ] Feature request
21 |
22 | **Please describe the current behavior:**
23 |
24 | **If reporting a bug, please provide steps to reproduce and, if possible, a minimal example demonstrating the issue:**
25 |
26 | **What is the expected behavior?**
27 |
28 | **What is the motivation or use case for changing the behavior?**
29 |
30 | **Please provide information about your environment:**
31 |
32 | - KnowledgeBook Version:
33 | - Node.js Version:
34 | - Operating System:
35 | - Browser (if applicable):
36 |
37 | **Additional context:**
38 | Please provide any other relevant information, such as related issues, suggestions for fixes, or links to helpful resources.
39 |
40 | **Supporting the Project:**
41 | If you find KnowledgeBook useful and would like to support its development, consider sponsoring or donating to the project. You can find donation buttons and links in the main README file of the repository. Your support helps maintain and improve the project!
42 |
43 | I appreciate your patience as I review and respond to submissions. As a solo developer, there may be some delay in addressing issues and requests, but I am committed to improving KnowledgeBook and value your input.
44 |
45 | Thank you for contributing to and supporting the project!
46 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: SilkePilon
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 | - package-ecosystem: "npm"
13 | # Files stored in `app` directory
14 | directory: "/frontend"
15 | schedule:
16 | interval: "weekly"
17 |
--------------------------------------------------------------------------------
/.github/media/bg.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/bg.mp4
--------------------------------------------------------------------------------
/.github/media/bg_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/bg_1.gif
--------------------------------------------------------------------------------
/.github/media/bg_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/bg_3.gif
--------------------------------------------------------------------------------
/.github/media/custom_node.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/custom_node.png
--------------------------------------------------------------------------------
/.github/media/custom_node2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/custom_node2.png
--------------------------------------------------------------------------------
/.github/media/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/logo.png
--------------------------------------------------------------------------------
/.github/media/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/logo2.png
--------------------------------------------------------------------------------
/.github/media/logo3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/logo3.png
--------------------------------------------------------------------------------
/.github/media/node_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/.github/media/node_example.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | profiles/
3 | block/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Silke pilon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 🤖 Use open and free AI models to generate reusable nodes for Minecraft bots.
10 |
11 |
12 |
13 |
14 |
15 | or donate on PayPal
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | About •
26 | Features •
27 | Install
28 |
29 |
30 | ## About 📬
31 |
32 | Project KnowledgeBook is an open-source Minecraft bot management platform that provides players with a user-friendly web interface to create and control helpful bots. Our goal is to enhance the Minecraft multiplayer experience with powerful automation tools, all at no cost to the player.
33 |
34 | **AI Stuff**
35 | More info and docs about the new AI features will be added soon!
36 |
37 |
38 |
39 |
40 |
41 | ## YouTube Demo
42 |
43 | [](https://www.youtube.com/watch?v=tEXtJFuGw8Y)
44 |
45 | ## Getting Started
46 |
47 | Check our (soon) [Wiki](link-to-wiki) for detailed guides on:
48 |
49 | - Setting up Project Skyview
50 | - Creating and managing bots
51 | - Using the item delivery system
52 | - Accessing the 2D map view
53 | - Building an custom bot using nodes
54 | - And more!
55 |
56 | ## Roadmap
57 |
58 | - Add checkboxes to nodes (booleans)
59 | - Add an way in export and import flows.
60 | - Auto save flows to browser.
61 |
62 | ## Ready to download flows🕹
63 |
64 | | Flow name | Description | Download |
65 | | --------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------- |
66 | | Obtain crafting table | Collects wood to craft a crafting table and drops it | [Download](https://github.com/SilkePilon/KnowledgeBook/blob/main/flows/crafting_table.json) |
67 | | | | |
68 |
69 | ## How To Install 📥
70 |
71 | ### Prerequisites
72 |
73 | Make sure you have the following installed:
74 |
75 | - [Node.js](https://nodejs.org/) 22.0.0 or above
76 | - [npm](https://www.npmjs.com/)
77 |
78 | ### Installation Steps
79 |
80 | 1. Clone the repository:
81 | ```bash
82 | git clone https://github.com/SilkePilon/KnowledgeBook.git
83 | cd KnowledgeBook
84 | ```
85 |
86 | 2. Install dependencies for all workspaces:
87 | ```bash
88 | npm run install:all
89 | ```
90 |
91 | 3. Set up HTTPS for development:
92 | ```bash
93 | cd backend
94 | npm run setup-https
95 | ```
96 |
97 | This will:
98 | - Install dependencies for both frontend and backend
99 | - Generate trusted SSL certificates for local development
100 | - Configure HTTPS for secure communication
101 |
102 | > **Note:** When first accessing the backend API in your browser, you may need to accept the self-signed certificate. This is normal for local development and doesn't affect security.
103 |
104 | ### Running the Application
105 |
106 | You can run the frontend and backend separately or together:
107 |
108 | #### Run both frontend and backend:
109 | ```bash
110 | npm run dev
111 | ```
112 |
113 | #### Run only the frontend:
114 | ```bash
115 | npm run frontend
116 | ```
117 |
118 | #### Run only the backend:
119 | ```bash
120 | npm run backend
121 | ```
122 |
123 | The frontend will be available at `http://localhost:3000` and the backend at `http://localhost:8080` by default.
124 |
125 | ### Development
126 |
127 | - Frontend code is located in the `frontend/` directory
128 | - Backend code is located in the `backend/` directory
129 | - Each directory has its own `package.json` with specific dependencies and scripts
130 |
131 | ```bash
132 | curl -sL -o main.zip https://github.com/SilkePilon/KnowledgeBook/archive/refs/heads/main.zip && unzip main.zip && cd KnowledgeBook-main && npm install && npm rebuild && cd .. && rm main.zip && cd KnowledgeBook-main && node main.js
133 | ```
134 |
135 | ## Manual Install
136 |
137 | #### 1. Clone the Repository
138 |
139 | First, make a local copy of the repository:
140 |
141 | ```bash
142 | git clone https://github.com/SilkePilon/KnowledgeBook.git
143 | ```
144 |
145 | Open the cloned repository in your preferred terminal app.
146 |
147 | #### 1. Install packages
148 |
149 | Assuming you have [Node](https://nodejs.org/en/download/package-manager/current) and [NPM](https://www.npmjs.com/) installed you can run the following commands:
150 |
151 | ```bash
152 | npm install
153 | npm rebuild
154 | node main.js
155 | ```
156 |
157 | That's it! You can now open up https://knowledgebook.vercel.app/ and start creating!
158 |
159 | ## Adding Custom Nodes to the Project
160 |
161 |
162 |
163 |
164 |
165 | Welcome to the project! This guide will walk you through the steps to add custom nodes. Follow these instructions to contribute your custom functionality.
166 |
167 | ### 1. Clone the Repository
168 |
169 | First, make a local copy of the repository:
170 |
171 | ```bash
172 | git clone https://github.com/SilkePilon/KnowledgeBook.git
173 | ```
174 |
175 | Open the cloned repository in your preferred IDE.
176 |
177 | ### 2. Create a New Node File
178 |
179 | Navigate to the `flow_functions` folder in the project directory. Create a new file for your node with the following naming conventions:
180 |
181 | - **Name Format:** `your_node_name.js`
182 | - **Rules:**
183 | - Use lowercase letters
184 | - Use underscores (`_`) to separate words
185 | - Do not include numbers in the file name
186 |
187 | For example, if you want to create a node for crafting planks, you might name the file `craft_planks.js`.
188 |
189 | ### 3. Update `functions.json`
190 |
191 | In the `flow_functions` directory, open the `functions.json` file and add an entry for your new node:
192 |
193 | ```json
194 | {
195 | "YOUR_NODE_NAME": {
196 | "name": "YOUR_NODE_NAME",
197 | "file": "YOUR_NODE_NAME.js",
198 | "id": "YOUR_NODE_NAME",
199 | "label": "DISPLAY NAME",
200 | "hasInput": true,
201 | "description": "YOUR NODE DESCRIPTION",
202 | // example of input
203 | "input": { "amount": "number", "message": "text", "sneak": "switch" },
204 | "author": "YOUR NAME"
205 | }
206 | }
207 | ```
208 |
209 | **Replace the placeholders:**
210 |
211 | - `YOUR_NODE_NAME` - The name of your node (in lowercase with underscores)
212 | - `DISPLAY NAME` - The name displayed in the UI
213 | - `YOUR NODE DESCRIPTION` - A description of what your node does
214 | - `YOUR NAME` - Your GitHub username
215 | - `{ "NAME": "number", "NAME": "text" }` - Your input fields.
216 |
217 | **Available input options:**
218 |
219 | - `text` - An general text input box
220 | - `number` - An input box limited to numbers only
221 | - `switch` - An switch that can be set to true or false
222 |
223 | ### 4. Implement the Node
224 |
225 | Open your newly created file and implement your node using the following structure:
226 |
227 | ```javascript
228 | const { getBot } = require("../main.js");
229 |
230 | function main(data) {
231 | // Get the bot object
232 | const bot = getBot();
233 | // Your function logic here
234 | console.log("Executing test_node with data:", data);
235 | }
236 |
237 | module.exports = { main };
238 | ```
239 |
240 | **Key Points:**
241 |
242 | - Require `bot` from `../main.js`.
243 | - The `main` function should be defined and exported. This function is executed when the node runs.
244 | - Use `try` and `catch` statements for error handling. If an error occurs, log it and rethrow it to ensure it can be caught elsewhere.
245 |
246 | **Accessing input fields**
247 | In order to the get values from the input field of a node you can use the `data` argument in the `main` function
248 | an example:
249 | in `functions.json` I've added an function with the following input:
250 | `{ "Amount": "number", "Message": "text" }`
251 | now i can access them in the `main` function by doing: `data.amount` and `data.message`
252 | the parameter name is based on the key provided in the input.
253 |
254 | **Examples**
255 |
256 | You can also checkout some of the already made nodes:
257 |
258 | - [Chat](https://github.com/SilkePilon/KnowledgeBook/blob/main/flow_functions/chat.js)
259 | - [Wait for chat message](https://github.com/SilkePilon/KnowledgeBook/blob/main/flow_functions/wait_for_chat_message.js)
260 | - [Walk to](https://github.com/SilkePilon/KnowledgeBook/blob/main/flow_functions/walk_to.js)
261 |
262 | **Limitations**
263 |
264 | - no way of outputting custom data from a node.
265 |
266 | ### 5. Submit a Pull Request
267 |
268 | Once you’ve added your node and updated the `functions.json` file, push your changes to a new branch and open a pull request on GitHub.
269 |
270 | ```bash
271 | git checkout -b your-feature-branch
272 | git add .
273 | git commit -m "Add custom node YOUR_NODE_NAME"
274 | git push origin your-feature-branch
275 | ```
276 |
277 | Go to the GitHub repository and create a pull request. Your changes will be reviewed, and if everything looks good, they will be merged!
278 |
279 | ## Thank You!
280 |
281 | Thank you for contributing to the project! If you have any questions or need further assistance, feel free to reach out.
282 |
283 | Happy coding! 🚀
284 |
285 | ## Star History
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
297 |
298 | ## Sponsor me
299 |
300 | ` `` `
301 |
302 |
303 |
--------------------------------------------------------------------------------
/backend/config/ssl.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const pem = require('pem');
4 | const { promisify } = require('util');
5 |
6 | const createCertificateAsync = promisify(pem.createCertificate);
7 |
8 | const SSL_DIR = path.join(__dirname, '..', 'ssl');
9 | const KEY_PATH = path.join(SSL_DIR, 'key.pem');
10 | const CERT_PATH = path.join(SSL_DIR, 'cert.pem');
11 |
12 | async function ensureSSLDirectory() {
13 | if (!fs.existsSync(SSL_DIR)) {
14 | fs.mkdirSync(SSL_DIR, { recursive: true });
15 | }
16 | }
17 |
18 | async function generateCertificate() {
19 | try {
20 | await ensureSSLDirectory();
21 |
22 | // Check if certificates already exist
23 | if (fs.existsSync(KEY_PATH) && fs.existsSync(CERT_PATH)) {
24 | return {
25 | key: fs.readFileSync(KEY_PATH),
26 | cert: fs.readFileSync(CERT_PATH)
27 | };
28 | }
29 |
30 | // Generate new certificates
31 | const keys = await createCertificateAsync({
32 | days: 365,
33 | selfSigned: true,
34 | commonName: 'localhost',
35 | altNames: ['localhost', '127.0.0.1'],
36 | });
37 |
38 | // Save the certificates
39 | fs.writeFileSync(KEY_PATH, keys.clientKey);
40 | fs.writeFileSync(CERT_PATH, keys.certificate);
41 |
42 | return {
43 | key: keys.clientKey,
44 | cert: keys.certificate
45 | };
46 | } catch (error) {
47 | console.error('Error generating SSL certificates:', error);
48 | throw error;
49 | }
50 | }
51 |
52 | module.exports = {
53 | generateCertificate
54 | };
55 |
--------------------------------------------------------------------------------
/backend/flow_functions/attack_entity.js:
--------------------------------------------------------------------------------
1 | // attack_entity.js
2 | const e = require("express");
3 | const { getBot } = require("../main.js");
4 |
5 | async function main(data) {
6 | const bot = getBot();
7 | const { entityName, range } = data;
8 | let entity;
9 | try {
10 | if (bot.players[entityName]) {
11 | entity = bot.players[entityName].entity;
12 | } else {
13 | entity = bot.nearestEntity(
14 | (entity) =>
15 | entity.name === entityName &&
16 | bot.entity.position.distanceTo(entity.position) <= range
17 | );
18 | }
19 | if (!entity) {
20 | throw new Error(`No ${entityName} found within ${range} blocks`);
21 | }
22 |
23 | await bot.lookAt(entity.position.offset(0, entity.height, 0));
24 | await bot.attack(entity);
25 | console.log(`Attacked ${entityName}`);
26 | } catch (error) {
27 | console.error(error);
28 | throw new Error("Error attacking entity");
29 | }
30 | }
31 |
32 | module.exports = { main };
33 |
--------------------------------------------------------------------------------
/backend/flow_functions/chat.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 |
3 | async function main(data) {
4 | // Your function logic here
5 | const bot = getBot();
6 | console.log("Executing test_node with data:", data);
7 | // Use bot as needed
8 | try {
9 | await bot.chat(data.message);
10 | console.log("Chat message sent");
11 | } catch (error) {
12 | console.error(error);
13 | throw new Error("Error sending chat message");
14 | }
15 | }
16 |
17 | module.exports = { main };
18 |
--------------------------------------------------------------------------------
/backend/flow_functions/collect_items.js:
--------------------------------------------------------------------------------
1 | // collect_items.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { range } = data;
7 |
8 | try {
9 | const items = bot.nearestEntity(
10 | (entity) =>
11 | entity.type === "object" &&
12 | bot.entity.position.distanceTo(entity.position) <= range
13 | );
14 | if (!items) {
15 | console.log("No dropped items found within range");
16 | return;
17 | }
18 |
19 | await bot.pathfinder.goto(items.position);
20 | console.log("Collected dropped items");
21 | } catch (error) {
22 | console.error(error);
23 | throw new Error("Error collecting items");
24 | }
25 | }
26 |
27 | module.exports = { main };
28 |
--------------------------------------------------------------------------------
/backend/flow_functions/craft_item.js:
--------------------------------------------------------------------------------
1 | // craft_item.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { itemName, amount } = data;
7 |
8 | try {
9 | const item = bot.registry.itemsByName[itemName];
10 | if (!item) {
11 | throw new Error(`Unknown item: ${itemName}`);
12 | }
13 |
14 | const recipe = bot.recipesFor(item.id, null, 1, null)[0];
15 | if (!recipe) {
16 | throw new Error(`No recipe found for ${itemName}`);
17 | }
18 |
19 | await bot.craft(recipe, amount, null);
20 | console.log(`Crafted ${amount} ${itemName}`);
21 | } catch (error) {
22 | console.error(error);
23 | throw new Error("Error crafting item");
24 | }
25 | }
26 |
27 | module.exports = { main };
28 |
--------------------------------------------------------------------------------
/backend/flow_functions/disconnect_on_player_proximity.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 |
3 | function main(data) {
4 | const bot = getBot();
5 | const radius = data.radius;
6 |
7 | if (radius <= 0) {
8 | throw new Error("Invalid radius. Please provide a positive number.");
9 | }
10 |
11 | console.log(
12 | `Starting background player proximity check with radius: ${radius} blocks`
13 | );
14 |
15 | function checkPlayerProximity() {
16 | const nearbyPlayers = Object.values(bot.players).filter((player) => {
17 | if (!player.entity) return false;
18 | const distance = bot.entity.position.distanceTo(player.entity.position);
19 | return distance <= radius && player.username !== bot.username;
20 | });
21 |
22 | if (nearbyPlayers.length > 0) {
23 | const nearestPlayer = nearbyPlayers[0];
24 | const distance = bot.entity.position.distanceTo(
25 | nearestPlayer.entity.position
26 | );
27 | console.log(
28 | `Player ${nearestPlayer.username} detected within ${distance.toFixed(
29 | 2
30 | )} blocks. Disconnecting...`
31 | );
32 | bot.quit("Player detected nearby");
33 | return true; // Player detected, bot disconnected
34 | }
35 |
36 | return false; // No players detected
37 | }
38 |
39 | function startBackgroundCheck() {
40 | const intervalId = setInterval(() => {
41 | try {
42 | if (checkPlayerProximity()) {
43 | clearInterval(intervalId);
44 | }
45 | } catch (error) {
46 | console.error("Error in player proximity check:", error);
47 | clearInterval(intervalId);
48 | }
49 | }, 1000); // Check every second
50 |
51 | // Attach the intervalId to the bot instance for potential cleanup
52 | bot.proximityCheckIntervalId = intervalId;
53 |
54 | console.log("Background player proximity check started");
55 | }
56 |
57 | // Start the background check
58 | startBackgroundCheck();
59 |
60 | // Continue execution immediately
61 | console.log("Node execution completed, background check is running");
62 | }
63 |
64 | module.exports = { main };
65 |
--------------------------------------------------------------------------------
/backend/flow_functions/drop_item.js:
--------------------------------------------------------------------------------
1 | // craft_item.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { itemName, amount } = data;
7 |
8 | try {
9 | const item = await bot.inventory.slots.find(
10 | (item) =>
11 | item && item.name.toLocaleLowerCase() === itemName.toLocaleLowerCase()
12 | );
13 | if (!item) {
14 | throw new Error(`Unknown item: ${itemName}`);
15 | }
16 | await bot.toss(item.type, null, amount);
17 | console.log(`Tossed ${amount} ${itemName}`);
18 | } catch (error) {
19 | console.error(error);
20 | throw new Error("Error tossing item");
21 | }
22 | }
23 |
24 | module.exports = { main };
25 |
--------------------------------------------------------------------------------
/backend/flow_functions/eat_food.js:
--------------------------------------------------------------------------------
1 | // eat_food.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { foodName } = data;
7 |
8 | try {
9 | const food = bot.inventory.items().find((item) => item.name === foodName);
10 | if (!food) {
11 | throw new Error(`Food not found in inventory: ${foodName}`);
12 | }
13 |
14 | await bot.equip(food, "hand");
15 | await bot.consume();
16 | console.log(`Ate ${foodName}`);
17 | } catch (error) {
18 | console.error(error);
19 | throw new Error("Error eating food");
20 | }
21 | }
22 |
23 | module.exports = { main };
24 |
--------------------------------------------------------------------------------
/backend/flow_functions/elytra_fly_to_location.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 | const { Vec3 } = require("vec3");
3 | const { GoalNear } = require("mineflayer-pathfinder").goals;
4 |
5 | async function sleep(ms) {
6 | return new Promise((resolve) => setTimeout(resolve, ms));
7 | }
8 |
9 | async function main(data) {
10 | const bot = getBot();
11 | const location = new Vec3(data.x, data.y, data.z);
12 |
13 | if (!bot.supportFeature("hasElytraFlying")) {
14 | console.log("Elytra flying is not supported in this version of Minecraft");
15 | return;
16 | }
17 |
18 | const elytraItem = bot.inventory.slots.find(
19 | (item) => item && item.name === "elytra"
20 | );
21 | if (!elytraItem) {
22 | console.log("No elytra available for long-distance travel");
23 | return;
24 | }
25 |
26 | await bot.equip(elytraItem, "torso");
27 | const fireworkItem = bot.inventory.slots.find(
28 | (item) => item && item.name === "firework_rocket"
29 | );
30 | if (!fireworkItem) {
31 | console.log("No fireworks available");
32 | return;
33 | }
34 | await bot.equip(fireworkItem, "hand");
35 |
36 | await sleep(1000);
37 | bot.elytrafly.start();
38 |
39 | console.log("Ascending to Y=150");
40 | while (bot.entity.position.y < 150) {
41 | bot.look(0, Math.PI / 2, true); // Look straight up
42 | bot.elytrafly.setControlState("up", true);
43 | bot.activateItem();
44 | await sleep(500);
45 | }
46 | bot.elytrafly.setControlState("up", false);
47 | console.log("Reached Y=150, starting horizontal flight");
48 |
49 | const maxFlightTime = 300000; // 5 minutes max flight time
50 | const flightStartTime = Date.now();
51 |
52 | while (true) {
53 | const currentPos = bot.entity.position.clone();
54 | const distanceToTarget = currentPos.xzDistanceTo(location);
55 | console.log(
56 | `Current distance to target: ${distanceToTarget.toFixed(2)} blocks`
57 | );
58 |
59 | if (distanceToTarget <= 40) {
60 | console.log("Within 40 blocks of target, starting descent");
61 | break;
62 | }
63 |
64 | if (Date.now() - flightStartTime > maxFlightTime) {
65 | console.log("Max flight time reached. Forcing landing.");
66 | break;
67 | }
68 |
69 | await bot.lookAt(new Vec3(location.x, currentPos.y, location.z));
70 | bot.elytrafly.setControlState("forward", true);
71 |
72 | if (bot.entity.velocity.xzDistanceTo(new Vec3(0, 0, 0)) < 0.8) {
73 | bot.activateItem();
74 | await sleep(1000);
75 | }
76 |
77 | await sleep(100);
78 | }
79 |
80 | console.log("Starting landing procedure");
81 | bot.elytrafly.setControlState("forward", false);
82 | let descending = true;
83 |
84 | while (!bot.entity.onGround) {
85 | await bot.lookAt(new Vec3(location.x, bot.entity.position.y, location.z));
86 |
87 | if (descending) {
88 | bot.elytrafly.setControlState("down", true);
89 | } else {
90 | bot.elytrafly.setControlState("down", false);
91 | }
92 |
93 | bot.elytrafly.setControlState("forward", true);
94 | await sleep(500);
95 | bot.elytrafly.setControlState("forward", false);
96 | bot.elytrafly.setControlState("back", true);
97 | await sleep(500);
98 | bot.elytrafly.setControlState("back", false);
99 |
100 | if (bot.entity.position.y > location.y + 10) {
101 | descending = true;
102 | } else {
103 | descending = false;
104 | if (bot.entity.velocity.y < -0.5) {
105 | bot.elytrafly.setControlState("up", true);
106 | await sleep(100);
107 | bot.elytrafly.setControlState("up", false);
108 | }
109 | }
110 |
111 | await sleep(100);
112 | }
113 |
114 | bot.elytrafly.stop();
115 | bot.elytrafly.forceStop();
116 | console.log("Bot has landed");
117 |
118 | await sleep(1000);
119 | await bot.pathfinder.cancel();
120 | bot.clearControlStates();
121 | bot.pathfinder.goto(new GoalNear(location.x, location.y, location.z, 2));
122 | }
123 |
124 | module.exports = { main };
125 |
--------------------------------------------------------------------------------
/backend/flow_functions/equip_item.js:
--------------------------------------------------------------------------------
1 | // equip_item.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { itemName, destination } = data;
7 |
8 | try {
9 | const item = bot.inventory.items().find((item) => item.name === itemName);
10 | if (!item) {
11 | throw new Error(`Item not found in inventory: ${itemName}`);
12 | }
13 |
14 | await bot.equip(item, destination);
15 | console.log(`Equipped ${itemName} to ${destination}`);
16 | } catch (error) {
17 | console.error(error);
18 | throw new Error("Error equipping item");
19 | }
20 | }
21 |
22 | module.exports = { main };
23 |
--------------------------------------------------------------------------------
/backend/flow_functions/find_item_in_chests.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 | const { Vec3 } = require("vec3");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { item_name, range } = data;
7 |
8 | try {
9 | const chests = bot.findBlocks({
10 | matching: (block) => block.name === "chest",
11 | maxDistance: range,
12 | });
13 |
14 | if (chests.length === 0) {
15 | throw new Error(`No chests found within ${range} blocks`);
16 | }
17 |
18 | for (const chest of chests) {
19 | const chestInventory = await bot.openChest(chest);
20 | const item = chestInventory.items().find((item) => item.name === item_name);
21 | if (item) {
22 | await bot.collect(item);
23 | console.log(`Found and collected ${item_name} from chest at ${chest.position}`);
24 | return;
25 | }
26 | }
27 |
28 | throw new Error(`Item ${item_name} not found in any chest within ${range} blocks`);
29 | } catch (error) {
30 | console.error(error);
31 | throw new Error("Error finding item in chests");
32 | }
33 | }
34 |
35 | module.exports = { main };
--------------------------------------------------------------------------------
/backend/flow_functions/functions.json:
--------------------------------------------------------------------------------
1 | {
2 | "test": {
3 | "name": "test",
4 | "file": "test.js",
5 | "id": "test",
6 | "label": "Test Node",
7 | "hasInput": true,
8 | "description": "Test node to check if everything is working correctly.",
9 | "input": {
10 | "number": "number",
11 | "text": "text",
12 | "Test Switch 1": "switch",
13 | "Test Switch 2": "switch"
14 | },
15 | "author": "SilkePilon",
16 | "badges": ["test"]
17 | },
18 | "chat": {
19 | "name": "chat",
20 | "file": "chat.js",
21 | "id": "chat",
22 | "label": "Send Chat Message",
23 | "hasInput": true,
24 | "description": "Send an chat message to public chat.",
25 | "input": {
26 | "message": "text"
27 | },
28 | "author": "SilkePilon"
29 | },
30 | "walk_to": {
31 | "name": "walk_to",
32 | "file": "walk_to.js",
33 | "id": "walk_to",
34 | "label": "Walk to location",
35 | "hasInput": true,
36 | "description": "Use pathfinding to walk to a location.",
37 | "input": {
38 | "x y z": "text"
39 | },
40 | "author": "SilkePilon",
41 | "badges": ["Not Working"]
42 | },
43 | "wait_for_chat_message": {
44 | "name": "wait_for_chat_message",
45 | "file": "wait_for_chat_message.js",
46 | "id": "wait_for_chat_message",
47 | "label": "Wait for chat message",
48 | "hasInput": true,
49 | "input": {
50 | "keyword": "text",
51 | "timeout (seconds)": "number"
52 | },
53 | "description": "Waits for a chat message containing a specific keyword.",
54 | "author": "SilkePilon"
55 | },
56 | "sleep": {
57 | "name": "sleep",
58 | "file": "sleep.js",
59 | "id": "sleep",
60 | "label": "Sleep",
61 | "hasInput": true,
62 | "description": "Sleeps for a certain amount of seconds.",
63 | "input": {
64 | "seconds": "number"
65 | },
66 | "author": "SilkePilon"
67 | },
68 | "mine": {
69 | "name": "mine",
70 | "file": "mine.js",
71 | "id": "mine",
72 | "label": "Mine",
73 | "hasInput": true,
74 | "description": "Mine a specific amount of a specified block",
75 | "input": {
76 | "block name": "text",
77 | "amount": "number"
78 | },
79 | "author": "Ash",
80 | "badges": ["beta", "user made"]
81 | },
82 | "place_block": {
83 | "name": "place_block",
84 | "file": "place_block.js",
85 | "id": "place_block",
86 | "label": "Place Block",
87 | "hasInput": true,
88 | "description": "Place a specified block at a relative position",
89 | "input": {
90 | "blockName": "text",
91 | "x": "text",
92 | "y": "text",
93 | "z": "text"
94 | },
95 | "author": "AI model"
96 | },
97 | "craft_item": {
98 | "name": "craft_item",
99 | "file": "craft_item.js",
100 | "id": "craft_item",
101 | "label": "Craft Item",
102 | "hasInput": true,
103 | "description": "Craft a specified amount of an item",
104 | "input": {
105 | "itemName": "text",
106 | "amount": "number"
107 | },
108 | "author": "AI model"
109 | },
110 | "equip_item": {
111 | "name": "equip_item",
112 | "file": "equip_item.js",
113 | "id": "equip_item",
114 | "label": "Equip Item",
115 | "hasInput": true,
116 | "description": "Equip a specified item to a destination (e.g., 'hand')",
117 | "input": {
118 | "itemName": "text",
119 | "destination": "text"
120 | },
121 | "author": "AI model"
122 | },
123 | "look_at": {
124 | "name": "look_at",
125 | "file": "look_at.js",
126 | "id": "look_at",
127 | "label": "Look At",
128 | "hasInput": true,
129 | "description": "Make the bot look at a specific coordinate",
130 | "input": {
131 | "x": "number",
132 | "y": "number",
133 | "z": "number"
134 | },
135 | "author": "AI model"
136 | },
137 | "eat_food": {
138 | "name": "eat_food",
139 | "file": "eat_food.js",
140 | "id": "eat_food",
141 | "label": "Eat Food",
142 | "hasInput": true,
143 | "description": "Make the bot eat a specified food item",
144 | "input": {
145 | "foodName": "text"
146 | },
147 | "author": "AI model"
148 | },
149 | "attack_entity": {
150 | "name": "attack_entity",
151 | "file": "attack_entity.js",
152 | "id": "attack_entity",
153 | "label": "Attack Entity",
154 | "hasInput": true,
155 | "description": "Attack a specified entity or player within a given range",
156 | "input": {
157 | "entityName": "text",
158 | "range": "number"
159 | },
160 | "author": "AI model"
161 | },
162 | "collect_items": {
163 | "name": "collect_items",
164 | "file": "collect_items.js",
165 | "id": "collect_items",
166 | "label": "Collect Dropped Items",
167 | "hasInput": true,
168 | "description": "Collect dropped items within a specified range",
169 | "input": {
170 | "range": "number"
171 | },
172 | "author": "AI model",
173 | "badges": ["Not Working"]
174 | },
175 | "toggle_options": {
176 | "name": "toggle_options",
177 | "file": "toggle_options.js",
178 | "id": "toggle_options",
179 | "label": "Toggle Player Options",
180 | "hasInput": true,
181 | "description": "Toggle player options on or off",
182 | "input": {
183 | "sneak": "switch",
184 | "sprint": "switch",
185 | "jump": "switch",
186 | "forward": "switch",
187 | "back": "switch",
188 | "left": "switch",
189 | "right": "switch"
190 | },
191 | "author": "AI model"
192 | },
193 | "use_block": {
194 | "name": "use_block",
195 | "file": "use_block.js",
196 | "id": "use_block",
197 | "label": "Use Block",
198 | "hasInput": true,
199 | "description": "Use (right-click) a block at a relative position",
200 | "input": {
201 | "x": "number",
202 | "y": "number",
203 | "z": "number"
204 | },
205 | "author": "AI model"
206 | },
207 | "place_block_nearby": {
208 | "name": "place_block_nearby",
209 | "file": "place_block_nearby.js",
210 | "id": "place_block_nearby",
211 | "label": "Place Block Nearby",
212 | "hasInput": true,
213 | "description": "Place a specified block in an open spot within a given range from the player",
214 | "input": {
215 | "blockName": "text",
216 | "range": "number"
217 | },
218 | "author": "AI model",
219 | "badges": ["Not Working"]
220 | },
221 | "smelt_item": {
222 | "name": "smelt_item",
223 | "file": "smelt_item.js",
224 | "id": "smelt_item",
225 | "label": "Smelt Item",
226 | "hasInput": true,
227 | "description": "Smelt an item in a nearby furnace",
228 | "input": {
229 | "itemName": "text",
230 | "fuelName": "text",
231 | "skip completion check": "switch"
232 | },
233 | "author": "AI model"
234 | },
235 | "drop_item": {
236 | "name": "drop_item",
237 | "file": "drop_item.js",
238 | "id": "drop_item",
239 | "label": "Drop Item",
240 | "hasInput": true,
241 | "description": "Drop a specified amount of an item",
242 | "input": {
243 | "itemName": "text",
244 | "amount": "number"
245 | },
246 | "author": "AI model"
247 | },
248 | "say_in_chat": {
249 | "name": "say_in_chat",
250 | "file": "say_in_chat.js",
251 | "id": "say_in_chat",
252 | "label": "Say in Chat",
253 | "hasInput": true,
254 | "description": "Makes the bot say a message in the chat",
255 | "input": {
256 | "message": "text"
257 | },
258 | "author": "meta/llama-3.1-405b-instruct",
259 | "badges": ["Made With AI"]
260 | },
261 | "get_item_from_chest": {
262 | "name": "get_item_from_chest",
263 | "file": "get_item_from_chest.js",
264 | "id": "get_item_from_chest",
265 | "label": "Get Item from Chest",
266 | "hasInput": true,
267 | "description": "Gets an item from a chest at a specific location",
268 | "input": {
269 | "chestLocation": "text",
270 | "itemName": "text"
271 | },
272 | "author": "meta/llama-3.1-405b-instruct",
273 | "badges": ["Made With AI @ 29/7/2024"]
274 | },
275 | "get_item_from_nearby_chest": {
276 | "name": "get_item_from_nearby_chest",
277 | "file": "get_item_from_nearby_chest.js",
278 | "id": "get_item_from_nearby_chest",
279 | "label": "Get Item from Nearby Chest",
280 | "hasInput": true,
281 | "description": "Searches for a chest within a given radius and collects a specified item from it",
282 | "input": {
283 | "itemName": "text",
284 | "radius": "number"
285 | },
286 | "author": "meta/llama-3.1-405b-instruct",
287 | "badges": ["Made With AI @ 29/7/2024"]
288 | },
289 | "javascript_code_executor": {
290 | "name": "javascript_code_executor",
291 | "file": "javascript_code_executor.js",
292 | "id": "javascript_code_executor",
293 | "label": "Execute JavaScript Code",
294 | "hasInput": true,
295 | "description": "Execute custom JavaScript code with access to the bot",
296 | "input": {
297 | "code": "text"
298 | },
299 | "author": "meta/llama-3.1-405b-instruct",
300 | "badges": ["Made With AI @ 31/7/2024"]
301 | },
302 | "find_item_in_chests": {
303 | "name": "find_item_in_chests",
304 | "file": "find_item_in_chests.js",
305 | "id": "find_item_in_chests",
306 | "label": "Find Item in Chests",
307 | "hasInput": true,
308 | "description": "Finds an item in chests within a given range and collects it",
309 | "input": {
310 | "item_name": "text",
311 | "range": "number"
312 | },
313 | "author": "meta/llama-3.1-405b-instruct",
314 | "badges": ["Made With AI @ 31/7/2024"]
315 | },
316 | "ELYTRA_FLY_TO_LOCATION": {
317 | "name": "ELYTRA_FLY_TO_LOCATION",
318 | "file": "elytra_fly_to_location.js",
319 | "id": "ELYTRA_FLY_TO_LOCATION",
320 | "label": "Elytra Fly to Location",
321 | "hasInput": true,
322 | "description": "Uses elytra to fly to a specified location",
323 | "input": {
324 | "x": "number",
325 | "y": "number",
326 | "z": "number"
327 | },
328 | "author": "YourGitHubUsername"
329 | },
330 | "DISCONNECT_ON_PLAYER_PROXIMITY": {
331 | "name": "DISCONNECT_ON_PLAYER_PROXIMITY",
332 | "file": "disconnect_on_player_proximity.js",
333 | "id": "DISCONNECT_ON_PLAYER_PROXIMITY",
334 | "label": "Disconnect on Player Proximity",
335 | "hasInput": true,
336 | "description": "Disconnects the bot when a player comes within a specified radius",
337 | "input": {
338 | "radius": "number"
339 | },
340 | "author": "Assistant"
341 | }
342 | }
343 |
--------------------------------------------------------------------------------
/backend/flow_functions/get_item_from_chest.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 | const { Vec3 } = require("vec3");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { chestLocation, itemName } = data;
7 |
8 | let position;
9 | if (chestLocation.includes(",")) {
10 | position = chestLocation.split(",");
11 | } else if (chestLocation.includes(" ")) {
12 | position = chestLocation.split(" ");
13 | }
14 | const chestPosition = new Vec3(position[0], position[1], position[2]);
15 |
16 | try {
17 | const chest = bot.blockAt(chestPosition);
18 | if (!chest || chest.name !== "chest") {
19 | throw new Error(`No chest found at location ${chestLocation}`);
20 | }
21 |
22 | const chestInventory = await bot.openChest(chest);
23 | const item = chestInventory.items().find((item) => item.name === itemName);
24 | if (!item) {
25 | throw new Error(`Item ${itemName} not found in chest`);
26 | }
27 |
28 | await bot.collect(item);
29 | console.log(`Collected ${itemName} from chest at ${chestLocation}`);
30 | } catch (error) {
31 | console.error(error);
32 | throw new Error(`Error getting item from chest: ${error.message}`);
33 | }
34 | }
35 |
36 | module.exports = { main };
--------------------------------------------------------------------------------
/backend/flow_functions/get_item_from_nearby_chest.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 | const { Vec3 } = require("vec3");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { itemName, radius } = data;
7 |
8 | try {
9 | const chest = bot.findBlock({
10 | matching: (block) => block.name === "chest",
11 | maxDistance: radius,
12 | });
13 |
14 | if (!chest) {
15 | throw new Error(`No chest found within ${radius} blocks`);
16 | }
17 |
18 | const chestInventory = await bot.openChest(chest);
19 | const item = chestInventory.items().find((item) => item.name === itemName);
20 | if (!item) {
21 | throw new Error(`Item ${itemName} not found in chest`);
22 | }
23 |
24 | await bot.collect(item);
25 | console.log(`Collected ${itemName} from chest at ${chest.position}`);
26 | } catch (error) {
27 | console.error(error);
28 | throw new Error("Error getting item from nearby chest");
29 | }
30 | }
31 |
32 | module.exports = { main };
--------------------------------------------------------------------------------
/backend/flow_functions/javascript_code_executor.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 |
3 | async function main(data) {
4 | const bot = getBot();
5 | const { code } = data;
6 |
7 | try {
8 | // Execute the JavaScript code with access to the bot
9 | const result = eval(code);
10 | console.log("Code executed successfully:", result);
11 | } catch (error) {
12 | console.error("Error executing code:", error);
13 | throw new Error("Error executing JavaScript code");
14 | }
15 | }
16 |
17 | module.exports = { main };
--------------------------------------------------------------------------------
/backend/flow_functions/look_at.js:
--------------------------------------------------------------------------------
1 | // look_at.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { x, y, z } = data;
7 |
8 | try {
9 | if (x == "~") {
10 | x = bot.entity.position.x;
11 | }
12 | if (y == "~") {
13 | y = bot.entity.position.y;
14 | }
15 | if (z == "~") {
16 | z = bot.entity.position.z;
17 | }
18 | await bot.lookAt(new Vec3(x, y, z));
19 | console.log(`Looking at x: ${x}, y: ${y}, z: ${z}`);
20 | } catch (error) {
21 | console.error(error);
22 | throw new Error("Error looking at position");
23 | }
24 | }
25 |
26 | module.exports = { main };
27 |
--------------------------------------------------------------------------------
/backend/flow_functions/mine.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 | const { autoTool } = require("../utils.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | let { amount, "block name": blockName } = data;
7 |
8 | try {
9 | while (amount > 0) {
10 | const blockInRegistry = bot.registry.blocksByName[blockName];
11 |
12 | if (!blockInRegistry) {
13 | return;
14 | }
15 |
16 | const block = getBlock(bot, blockName);
17 |
18 | if (block) {
19 | const distance = bot.entity.position.distanceTo(block.position);
20 |
21 | if (distance > 3) {
22 | const { x, y, z } = block.position;
23 | await bot.goToLocation({ x, y, z }, false);
24 | }
25 |
26 | try {
27 | const distance = bot.entity.position.distanceTo(block.position);
28 |
29 | if (distance > 4) {
30 | continue;
31 | }
32 |
33 | await autoTool(bot, block);
34 | await bot.collectBlock.collect(block);
35 | } catch (err) {
36 | console.error(err);
37 | throw new Error(`Failed to mine ${block.name}`);
38 | }
39 | } else {
40 | throw new Error(`Could not find block: ${block}`);
41 | }
42 |
43 | amount--;
44 | await sleep(100);
45 | }
46 | } catch (err) {
47 | console.error(`Error encountered: ${err.message}`);
48 | await sleep(1000); // Wait for 1 second before retrying
49 | main(data); // Retry the main function with the same data
50 | }
51 | }
52 |
53 | /**
54 | * Find a block near the bot
55 | * @param {import("mineflayer").Bot} bot
56 | * @param {string} targetBlock - The name of the target block.
57 | * @returns {import("prismarine-block").Block | null} - The closest block matching the target name or null if none found.
58 | */
59 | function getBlock(bot, targetBlock) {
60 | const possibleBlocks = {};
61 |
62 | const blockPositions = bot.findBlocks({
63 | matching: (block) => {
64 | if (block.name.includes(targetBlock)) {
65 | return true;
66 | }
67 | },
68 | maxDistance: 64,
69 | count: 128,
70 | point: bot.entity.position,
71 | });
72 |
73 | if (blockPositions.length === 0) {
74 | return null;
75 | }
76 |
77 | let closestBlockPosition = null;
78 | let minDistance = Infinity;
79 |
80 | for (const position of blockPositions) {
81 | const block = bot.blockAt(position);
82 |
83 | if (!block) continue;
84 |
85 | possibleBlocks[position] = block;
86 |
87 | const distance = getDistance(position, bot.entity.position);
88 | if (distance < minDistance) {
89 | minDistance = distance;
90 | closestBlockPosition = position;
91 | }
92 | }
93 |
94 | if (!closestBlockPosition) {
95 | return null;
96 | }
97 |
98 | return possibleBlocks[closestBlockPosition];
99 | }
100 |
101 | /**
102 | * Calculate the distance between two vectors.
103 | * @param {Vec3} vec1
104 | * @param {Vec3} vec2
105 | * @returns {number} - The distance between the two vectors.
106 | */
107 | function getDistance(vec1, vec2) {
108 | return vec1.distanceTo(vec2);
109 | }
110 |
111 | function sleep(ms) {
112 | return new Promise((resolve) => setTimeout(resolve, ms));
113 | }
114 |
115 | module.exports = { main };
116 |
--------------------------------------------------------------------------------
/backend/flow_functions/place_block.js:
--------------------------------------------------------------------------------
1 | // place_block.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { blockName, x, y, z } = data;
7 | if (x == "~") {
8 | x = bot.entity.position.x;
9 | }
10 | if (y == "~") {
11 | y = bot.entity.position.y;
12 | }
13 | if (z == "~") {
14 | z = bot.entity.position.z;
15 | }
16 |
17 | try {
18 | const position = bot.entity.position.offset(x, y, z);
19 | const block = bot.blockAt(position);
20 |
21 | if (block.name === "air") {
22 | const itemByName = bot.registry.itemsByName[blockName];
23 | if (!itemByName) {
24 | throw new Error(`Unknown block: ${blockName}`);
25 | }
26 |
27 | await bot.equip(itemByName, "hand");
28 | await bot.placeBlock(block, new Vec3(0, 1, 0));
29 | console.log(`Placed ${blockName} at ${position}`);
30 | } else {
31 | throw new Error(`Cannot place block at ${position}, space is not empty`);
32 | }
33 | } catch (error) {
34 | console.error(error);
35 | throw new Error("Error placing block");
36 | }
37 | }
38 |
39 | module.exports = { main };
40 |
--------------------------------------------------------------------------------
/backend/flow_functions/place_block_nearby.js:
--------------------------------------------------------------------------------
1 | // place_block_nearby.js
2 | const { getBot } = require("../main.js");
3 | const Vec3 = require("vec3");
4 |
5 | async function main(data) {
6 | const bot = getBot();
7 | const { blockName, range } = data;
8 |
9 | try {
10 | const item = bot.inventory.findInventoryItem(blockName);
11 | if (!item) {
12 | throw new Error(`${blockName} not found in inventory`);
13 | }
14 |
15 | const botPosition = bot.entity.position.floored();
16 | const placementPositions = bot.findBlocks({
17 | matching: (block) => !block.name === "air" && !isLiquid(block),
18 | maxDistance: range,
19 | count: 100, // Increase this if needed
20 | });
21 |
22 | if (placementPositions.length === 0) {
23 | throw new Error(
24 | `No suitable location found within ${range} blocks to place ${blockName}`
25 | );
26 | }
27 |
28 | // Sort positions by distance to the bot
29 | placementPositions.sort(
30 | (a, b) =>
31 | bot.entity.position.distanceTo(a) - bot.entity.position.distanceTo(b)
32 | );
33 |
34 | const placementPos = placementPositions[0].offset(0, 1, 0);
35 | const referenceBlock = bot.blockAt(placementPositions[0]);
36 |
37 | if (!referenceBlock) {
38 | throw new Error("Failed to get reference block for placement");
39 | }
40 |
41 | await bot.equip(item, "hand");
42 |
43 | // Move closer to the placement position if needed
44 | if (bot.entity.position.distanceTo(placementPos) > 4) {
45 | await bot.pathfinder.goto(placementPos);
46 | }
47 |
48 | // Look at the placement position
49 | await bot.lookAt(placementPos);
50 |
51 | // Place the block
52 | await bot.placeBlock(referenceBlock, new Vec3(0, 1, 0));
53 |
54 | console.log(`Placed ${blockName} at ${placementPos}`);
55 | } catch (error) {
56 | console.error(error);
57 | throw new Error(`Error placing ${blockName} nearby: ${error.message}`);
58 | }
59 | }
60 |
61 | function isLiquid(block) {
62 | return block && (block.name === "water" || block.name === "lava");
63 | }
64 |
65 | module.exports = { main };
66 |
--------------------------------------------------------------------------------
/backend/flow_functions/say_in_chat.js:
--------------------------------------------------------------------------------
1 | // say_in_chat.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { message } = data;
7 |
8 | try {
9 | await bot.chat(message);
10 | console.log(`Said "${message}" in chat`);
11 | } catch (error) {
12 | console.error(error);
13 | throw new Error("Error saying message in chat");
14 | }
15 | }
16 |
17 | module.exports = { main };
--------------------------------------------------------------------------------
/backend/flow_functions/sleep.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 |
3 | // Helper function to sleep for a given number of milliseconds
4 | function sleep(ms) {
5 | return new Promise((resolve) => setTimeout(resolve, ms));
6 | }
7 | // main function to execute the flow node
8 | async function main(data) {
9 | await sleep(data.seconds * 1000);
10 | }
11 |
12 | module.exports = { main };
13 |
--------------------------------------------------------------------------------
/backend/flow_functions/smelt_item.js:
--------------------------------------------------------------------------------
1 | // smelt_item.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { itemName, fuelName } = data;
7 |
8 | try {
9 | const furnace = bot.findBlock({
10 | matching: (block) => block.name === "furnace",
11 | maxDistance: 32,
12 | });
13 |
14 | if (!furnace) {
15 | throw new Error("No furnace found nearby");
16 | }
17 |
18 | const furnaceBlock = await bot.openFurnace(furnace);
19 | await bot.waitForTicks(8);
20 | const item = await furnaceBlock.slots.find(
21 | (item) => item && item.name === itemName
22 | );
23 | const fuel = await furnaceBlock.slots.find(
24 | (item) => item && item.name === fuelName
25 | );
26 | console.log(item);
27 | console.log(fuel);
28 | if (!item) {
29 | throw new Error(`${itemName} not found in inventory`);
30 | }
31 | if (!fuel) {
32 | throw new Error(`${fuelName} not found in inventory`);
33 | }
34 | await furnaceBlock.putFuel(fuel.type, null, fuel.count);
35 | await furnaceBlock.putInput(item.type, null, item.count);
36 | console.log(`Started smelting ${itemName}`);
37 | if (data["wait for completion"]) {
38 | await new Promise((resolve) => {
39 | furnaceBlock.on("update", () => {
40 | if (furnaceBlock.slots[2]?.count === item.count) {
41 | console.log("Smelting complete");
42 | resolve();
43 | }
44 | if (!furnaceBlock.slots[1]?.count) {
45 | if (!furnaceBlock.slots[0]?.count) {
46 | console.log("Fuel depleted");
47 | resolve();
48 | }
49 | }
50 | });
51 | });
52 | const output = await furnaceBlock.takeOutput();
53 | }
54 |
55 | await furnaceBlock.close();
56 | console.log(`Finished smelting ${output.name}`);
57 | } catch (error) {
58 | console.error(error);
59 | throw new Error("Error smelting item");
60 | }
61 | }
62 |
63 | module.exports = { main };
64 |
--------------------------------------------------------------------------------
/backend/flow_functions/test.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 |
3 | function main(data) {
4 | // Get the bot object
5 | const bot = getBot();
6 | // Your function logic here
7 | console.log("Executing test_node with data:", data);
8 | }
9 |
10 | module.exports = { main };
11 |
--------------------------------------------------------------------------------
/backend/flow_functions/toggle_options.js:
--------------------------------------------------------------------------------
1 | // toggle_sneak.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | for (const key in data) {
7 | try {
8 | if (data.hasOwnProperty(key)) {
9 | const value = data[key];
10 | await bot.setControlState(key, value);
11 | console.log(`Toggled ${key} to ${value}`);
12 | }
13 | } catch (error) {
14 | console.error(`Error toggling ${key}`);
15 | }
16 | }
17 | }
18 |
19 | module.exports = { main };
20 |
--------------------------------------------------------------------------------
/backend/flow_functions/use_block.js:
--------------------------------------------------------------------------------
1 | // use_block.js
2 | const { getBot } = require("../main.js");
3 |
4 | async function main(data) {
5 | const bot = getBot();
6 | const { x, y, z } = data;
7 |
8 | try {
9 | const targetPosition = bot.entity.position.offset(x, y, z);
10 | const targetBlock = bot.blockAt(targetPosition);
11 |
12 | if (!targetBlock) {
13 | throw new Error(`No block found at relative position ${x}, ${y}, ${z}`);
14 | }
15 |
16 | await bot.lookAt(targetPosition);
17 | await bot.activateBlock(targetBlock);
18 | console.log(`Used block at ${targetPosition}`);
19 | } catch (error) {
20 | console.error(error);
21 | throw new Error("Error using block");
22 | }
23 | }
24 |
25 | module.exports = { main };
26 |
--------------------------------------------------------------------------------
/backend/flow_functions/wait_for_chat_message.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 |
3 | async function main(data) {
4 | const bot = getBot();
5 | console.log("Executing test_node with data:", data);
6 |
7 | try {
8 | await new Promise((resolve, reject) => {
9 | const messageListener = (message, messagePosition, jsonMsg, sender) => {
10 | console.log("Message received:", message);
11 | if (
12 | toString(message)
13 | .toLocaleLowerCase()
14 | .includes(toString(data.keyword).toLocaleLowerCase())
15 | ) {
16 | console.log("Message matched:", message);
17 | bot.off("messagestr", messageListener); // Remove this specific listener
18 | resolve();
19 | }
20 | };
21 |
22 | // Add the listener
23 | bot.on("messagestr", messageListener);
24 |
25 | // Add a timeout to reject the promise if the message is not received within a certain time
26 | const timeoutId = setTimeout(() => {
27 | bot.off("messagestr", messageListener); // Remove the listener in case of timeout
28 | reject(new Error("Timeout waiting for the message"));
29 | }, data["timeout (seconds)"] * 1000);
30 | });
31 | console.log("Message processing complete");
32 | } catch (error) {
33 | console.error(error);
34 | throw new Error("Error waiting for the message");
35 | }
36 | }
37 |
38 | module.exports = { main };
39 |
--------------------------------------------------------------------------------
/backend/flow_functions/walk_to.js:
--------------------------------------------------------------------------------
1 | const { getBot } = require("../main.js");
2 |
3 | async function main(data) {
4 | // Your function logic here
5 | const bot = getBot();
6 | console.log("Executing test_node with data:", data);
7 | // Use bot as needed
8 | locationList = toString(data["x y z"]).split(" ");
9 | location = { x: locationList[0], y: locationList[1], z: locationList[2] };
10 | try {
11 | await bot.usePathfinding(location);
12 | console.log("Reached destination");
13 | } catch (error) {
14 | console.error(error);
15 | throw new Error("Error walking to location");
16 | }
17 | }
18 |
19 | module.exports = { main };
20 |
--------------------------------------------------------------------------------
/backend/flows/crafting_table.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "node-1",
5 | "type": "custom",
6 | "position": {
7 | "x": -142.02048190478115,
8 | "y": -209.63587501870896
9 | },
10 | "data": {
11 | "label": "Wait for chat message",
12 | "description": "Waits for a chat message containing a specific keyword.",
13 | "author": "SilkePilon",
14 | "position": {
15 | "x": 0,
16 | "y": 0
17 | },
18 | "reactFlowInstance": {
19 | "viewportInitialized": true
20 | },
21 | "input": {
22 | "keyword": "text",
23 | "timeout (seconds)": "number"
24 | },
25 | "badges": [],
26 | "theme": "dark",
27 | "inputValues": {
28 | "keyword": "start",
29 | "timeout (seconds)": "120"
30 | },
31 | "runningNodeId": null
32 | },
33 | "measured": {
34 | "width": 300,
35 | "height": 283
36 | },
37 | "selected": false,
38 | "dragging": false
39 | },
40 | {
41 | "id": "node-2",
42 | "type": "custom",
43 | "position": {
44 | "x": 209.47833467692044,
45 | "y": -122.70495757297569
46 | },
47 | "data": {
48 | "label": "Send Chat Message",
49 | "description": "Send an chat message to public chat.",
50 | "author": "SilkePilon",
51 | "position": {
52 | "x": 400,
53 | "y": 0
54 | },
55 | "reactFlowInstance": {
56 | "viewportInitialized": true
57 | },
58 | "input": {
59 | "message": "text"
60 | },
61 | "badges": [],
62 | "theme": "dark",
63 | "inputValues": {
64 | "message": "starting!"
65 | },
66 | "runningNodeId": null
67 | },
68 | "measured": {
69 | "width": 258,
70 | "height": 195
71 | },
72 | "selected": false,
73 | "dragging": false
74 | },
75 | {
76 | "id": "node-3",
77 | "type": "custom",
78 | "position": {
79 | "x": 542.9826788198479,
80 | "y": -105.07890479188538
81 | },
82 | "data": {
83 | "label": "Mine",
84 | "description": "Mine a specific amount of a specified block",
85 | "author": "Ash",
86 | "position": {
87 | "x": 800,
88 | "y": 0
89 | },
90 | "reactFlowInstance": {
91 | "viewportInitialized": true
92 | },
93 | "input": {
94 | "block name": "text",
95 | "amount": "number"
96 | },
97 | "badges": [
98 | "beta",
99 | "user made"
100 | ],
101 | "theme": "dark",
102 | "inputValues": {
103 | "block name": "oak_log",
104 | "amount": "2"
105 | },
106 | "runningNodeId": null
107 | },
108 | "measured": {
109 | "width": 291,
110 | "height": 267
111 | },
112 | "selected": false,
113 | "dragging": false
114 | },
115 | {
116 | "id": "node-4",
117 | "type": "custom",
118 | "position": {
119 | "x": 907.3897000712877,
120 | "y": 20.116958120098957
121 | },
122 | "data": {
123 | "label": "Craft Item",
124 | "description": "Craft a specified amount of an item",
125 | "author": "AI model",
126 | "position": {
127 | "x": 1200,
128 | "y": 0
129 | },
130 | "reactFlowInstance": {
131 | "viewportInitialized": true
132 | },
133 | "input": {
134 | "itemName": "text",
135 | "amount": "number"
136 | },
137 | "badges": [],
138 | "theme": "dark",
139 | "inputValues": {
140 | "itemName": "oak_planks",
141 | "amount": "4"
142 | },
143 | "runningNodeId": null
144 | },
145 | "measured": {
146 | "width": 246,
147 | "height": 267
148 | },
149 | "selected": false,
150 | "dragging": false
151 | },
152 | {
153 | "id": "node-5",
154 | "type": "custom",
155 | "position": {
156 | "x": 1238,
157 | "y": 110
158 | },
159 | "data": {
160 | "label": "Craft Item",
161 | "description": "Craft a specified amount of an item",
162 | "author": "AI model",
163 | "position": {
164 | "x": 1600,
165 | "y": 0
166 | },
167 | "reactFlowInstance": {
168 | "viewportInitialized": true
169 | },
170 | "input": {
171 | "itemName": "text",
172 | "amount": "number"
173 | },
174 | "badges": [],
175 | "theme": "dark",
176 | "inputValues": {
177 | "itemName": "crafting_table",
178 | "amount": "1"
179 | },
180 | "runningNodeId": null
181 | },
182 | "measured": {
183 | "width": 246,
184 | "height": 267
185 | },
186 | "selected": false,
187 | "dragging": false
188 | },
189 | {
190 | "id": "node-6",
191 | "type": "custom",
192 | "position": {
193 | "x": 1544.7495422363281,
194 | "y": 190
195 | },
196 | "data": {
197 | "label": "Drop Item",
198 | "description": "Drop a specified amount of an item",
199 | "author": "AI model",
200 | "position": {
201 | "x": 2000,
202 | "y": 0
203 | },
204 | "reactFlowInstance": {
205 | "viewportInitialized": true
206 | },
207 | "input": {
208 | "itemName": "text",
209 | "amount": "number"
210 | },
211 | "badges": [],
212 | "theme": "dark",
213 | "inputValues": {
214 | "itemName": "crafting_table",
215 | "amount": "1"
216 | },
217 | "runningNodeId": null
218 | },
219 | "measured": {
220 | "width": 242,
221 | "height": 267
222 | },
223 | "selected": false,
224 | "dragging": false
225 | }
226 | ],
227 | "edges": [
228 | {
229 | "id": "edge-1",
230 | "source": "node-1",
231 | "target": "node-2",
232 | "type": "custom-edge",
233 | "animated": true
234 | },
235 | {
236 | "id": "edge-2",
237 | "source": "node-2",
238 | "target": "node-3",
239 | "type": "custom-edge",
240 | "animated": true
241 | },
242 | {
243 | "id": "edge-3",
244 | "source": "node-3",
245 | "target": "node-4",
246 | "type": "custom-edge",
247 | "animated": true
248 | },
249 | {
250 | "id": "edge-4",
251 | "source": "node-4",
252 | "target": "node-5",
253 | "type": "custom-edge",
254 | "animated": true
255 | },
256 | {
257 | "id": "edge-5",
258 | "source": "node-5",
259 | "target": "node-6",
260 | "type": "custom-edge",
261 | "animated": true
262 | }
263 | ]
264 | }
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "knowledgebook-backend",
3 | "version": "1.0.0",
4 | "engines": {
5 | "node": ">=22.0.0"
6 | },
7 | "dependencies": {
8 | "@nxg-org/mineflayer-physics-util": "^1.5.8",
9 | "@xboxreplay/xboxlive-auth": "^4.1.0",
10 | "axios": "^1.8.4",
11 | "body-parser": "^1.20.2",
12 | "canvas": "^2.11.2",
13 | "dotenv": "^16.4.5",
14 | "express": "^4.19.2",
15 | "express-rate-limit": "^7.4.0",
16 | "minecraft-data": "^3.66.0",
17 | "mineflayer": "^4.20.1",
18 | "mineflayer-collectblock": "^1.4.1",
19 | "mineflayer-elytrafly": "^1.4.6",
20 | "mineflayer-pathfinder": "^2.4.5",
21 | "mineflayer-tool": "^1.2.0",
22 | "morgan": "^1.10.0",
23 | "node-fetch": "2.7.0",
24 | "openai": "^4.55.3",
25 | "pem": "^1.14.8",
26 | "prismarine-auth": "^2.7.0",
27 | "prismarine-viewer": "^1.28.0",
28 | "prompt-sync": "^4.2.0",
29 | "rsa-json": "^0.0.2",
30 | "sharp": "^0.33.4",
31 | "vec3": "^0.1.10",
32 | "ws": "^8.18.0"
33 | },
34 | "devDependencies": {
35 | "@types/node": "^22.5.4",
36 | "typescript": "^5.6.2"
37 | }, "scripts": {
38 | "start": "node main.js",
39 | "dev": "node main.js",
40 | "setup-https": "node scripts/setup-https.js"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/backend/scripts/setup-https.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { generateCertificate } = require('./config/ssl');
3 |
4 | async function setupHttps() {
5 | console.log('Setting up HTTPS development environment...\n');
6 |
7 | try {
8 | await generateCertificate();
9 |
10 | console.log('\n==========================================================');
11 | console.log('HTTPS Development Environment Setup Complete!');
12 | console.log('==========================================================\n');
13 | console.log('To use the secure backend:');
14 | console.log('1. Start the backend server: npm run dev');
15 | console.log('2. Access the API at: https://localhost:8080\n');
16 | console.log('Note: You may need to accept the self-signed certificate');
17 | console.log('in your browser when first accessing the backend API.');
18 | console.log('==========================================================');
19 | } catch (error) {
20 | console.error('Error setting up HTTPS environment:', error);
21 | process.exit(1);
22 | }
23 | }
24 |
25 | setupHttps();
26 |
--------------------------------------------------------------------------------
/backend/utils.js:
--------------------------------------------------------------------------------
1 | const nbt = require("prismarine-nbt");
2 |
3 | /**
4 | * @description Automatically equips the best tool for that bot
5 | * @param {import("mineflayer").Bot} bot
6 | * @param {import("prismarine-block").Block} block
7 | */
8 | async function autoTool(bot, block) {
9 | if (!block) return;
10 |
11 | const bestTool = bestHarvestTool(bot, block);
12 |
13 | if (!bestTool) return;
14 |
15 | const toolInInventory = await getItem(bot, bestTool.name);
16 |
17 | if (!toolInInventory) return;
18 |
19 | await bot.equip(bestTool);
20 | }
21 |
22 | /**
23 | *
24 | * @param {import("mineflayer").Bot} bot
25 | * @param {Bloimport("prismarine-block").Block} block
26 | * @returns
27 | */
28 | function bestHarvestTool(bot, block) {
29 | const availableTools = bot.inventory.items();
30 | const effects = bot.entity.effects;
31 |
32 | let fastest = Number.MAX_VALUE;
33 | let bestTool = null;
34 | for (const tool of availableTools) {
35 | const enchants =
36 | tool && tool.nbt ? nbt.simplify(tool.nbt).Enchantments : [];
37 | const digTime = block.digTime(
38 | tool ? tool.type : null,
39 | false,
40 | false,
41 | false,
42 | enchants,
43 | effects
44 | );
45 | if (digTime < fastest) {
46 | fastest = digTime;
47 | bestTool = tool;
48 | }
49 | }
50 |
51 | return bestTool;
52 | }
53 |
54 | /**
55 | *
56 | * @param {import("mineflayer").Bot} bot
57 | * @param {string} item
58 | */
59 | async function getItem(bot, item) {
60 | const itemInRegistry = bot.registry.itemsByName[item];
61 |
62 | if (!itemInRegistry) return;
63 |
64 | const ItemInInventory = bot.inventory
65 | .items()
66 | .find((iteme) => iteme.name === itemInRegistry.name);
67 |
68 | if (!ItemInInventory) return null;
69 |
70 | return ItemInInventory;
71 | }
72 |
73 | module.exports = {
74 | autoTool,
75 | bestHarvestTool,
76 | getItem,
77 | };
78 |
--------------------------------------------------------------------------------
/frontend/.env.development:
--------------------------------------------------------------------------------
1 | NEXT_PUBLIC_API_URL=https://localhost:8080
2 | NODE_TLS_REJECT_UNAUTHORIZED=0
3 |
--------------------------------------------------------------------------------
/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals",
3 | "env": {
4 | "browser": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/frontend/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "src/app/globals.css",
9 | "baseColor": "zinc",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
--------------------------------------------------------------------------------
/frontend/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | images: {
4 | remotePatterns: [
5 | {
6 | protocol: "https",
7 | hostname: "**",
8 | },
9 | ],
10 | },
11 | };
12 |
13 | export default nextConfig;
14 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@hookform/resolvers": "^3.8.0",
13 | "@radix-ui/react-alert-dialog": "^1.1.5",
14 | "@radix-ui/react-checkbox": "^1.1.4",
15 | "@radix-ui/react-collapsible": "^1.1.3",
16 | "@radix-ui/react-dialog": "^1.1.5",
17 | "@radix-ui/react-dropdown-menu": "^2.1.6",
18 | "@radix-ui/react-label": "^2.1.0",
19 | "@radix-ui/react-popover": "^1.1.3",
20 | "@radix-ui/react-scroll-area": "^1.1.3",
21 | "@radix-ui/react-select": "^2.1.3",
22 | "@radix-ui/react-separator": "^1.1.0",
23 | "@radix-ui/react-slider": "^1.2.0",
24 | "@radix-ui/react-slot": "^1.1.0",
25 | "@radix-ui/react-switch": "^1.1.0",
26 | "@radix-ui/react-toast": "^1.2.1",
27 | "@radix-ui/react-tooltip": "^1.1.2",
28 | "@tanstack/react-table": "^8.20.1",
29 | "@xyflow/react": "^12.0.4",
30 | "axios": "^1.7.3",
31 | "canvas-confetti": "^1.9.2",
32 | "class-variance-authority": "^0.7.0",
33 | "clsx": "^2.1.0",
34 | "cmdk": "^1.0.0",
35 | "cors": "^2.8.5",
36 | "framer-motion": "^11.0.8",
37 | "html-to-image": "^1.11.11",
38 | "input-otp": "^1.1.0",
39 | "ldrs": "^1.0.1",
40 | "lucide-react": "^0.488.0",
41 | "next": "^14.2.6",
42 | "next-themes": "^0.3.0",
43 | "react": "^18.2.0",
44 | "react-dom": "^18.2.0",
45 | "react-hook-form": "^7.51.0",
46 | "react-icons": "^5.0.1",
47 | "react-markdown": "^9.0.1",
48 | "react-resizable-panels": "^2.0.9",
49 | "react-skinview3d": "^5.0.3",
50 | "react-zoom-pan-pinch": "^3.4.2",
51 | "recharts": "^2.12.1",
52 | "socket.io-client": "^4.8.1",
53 | "vaul": "^1.0.0",
54 | "tailwind-merge": "^3.2.0",
55 | "tailwindcss-animate": "^1.0.7"
56 | },
57 | "devDependencies": {
58 | "@types/cors": "^2.8.17",
59 | "@types/node": "^20",
60 | "@types/react": "^18",
61 | "@types/react-dom": "^18",
62 | "eslint": "^8",
63 | "eslint-config-next": "14.2.5",
64 | "postcss": "^8",
65 | "tailwindcss": "^3.4.4",
66 | "typescript": "^5"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/frontend/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/frontend/public/fonts/MinecraftBold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/frontend/public/fonts/MinecraftBold.otf
--------------------------------------------------------------------------------
/frontend/public/fonts/MinecraftBoldItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/frontend/public/fonts/MinecraftBoldItalic.otf
--------------------------------------------------------------------------------
/frontend/public/fonts/MinecraftItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/frontend/public/fonts/MinecraftItalic.otf
--------------------------------------------------------------------------------
/frontend/public/fonts/MinecraftRegular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/frontend/public/fonts/MinecraftRegular.otf
--------------------------------------------------------------------------------
/frontend/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/public/pixel_images/book.crop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/frontend/public/pixel_images/book.crop.png
--------------------------------------------------------------------------------
/frontend/public/pixel_images/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/frontend/public/pixel_images/book.png
--------------------------------------------------------------------------------
/frontend/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilkePilon/KnowledgeBook/fb68e99dd93204efc9cb007b23dd3c1148f39577/frontend/src/app/favicon.ico
--------------------------------------------------------------------------------
/frontend/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @font-face {
6 | font-family: 'minecraft';
7 | src: url('/fonts/MinecraftRegular.otf');
8 | }
9 |
10 | @layer base {
11 | :root {
12 | --background: 0 0% 100%;
13 | --foreground: 240 10% 3.9%;
14 |
15 | --card: 0 0% 100%;
16 | --card-foreground: 240 10% 3.9%;
17 |
18 | --popover: 0 0% 100%;
19 | --popover-foreground: 240 10% 3.9%;
20 |
21 | --primary: 240 5.9% 10%;
22 | --primary-foreground: 0 0% 98%;
23 |
24 | --secondary: 240 4.8% 95.9%;
25 | --secondary-foreground: 240 5.9% 10%;
26 |
27 | --muted: 240 4.8% 95.9%;
28 | --muted-foreground: 240 3.8% 46.1%;
29 |
30 | --accent: 240 4.8% 95.9%;
31 | --accent-foreground: 240 5.9% 10%;
32 |
33 | --destructive: 0 84.2% 60.2%;
34 | --destructive-foreground: 0 0% 98%;
35 |
36 | --border: 240 5.9% 90%;
37 | --input: 240 5.9% 90%;
38 | --ring: 240 10% 3.9%;
39 |
40 | --radius: 0.5rem;
41 | }
42 |
43 | .dark {
44 | --background: 240 10% 3.9%;
45 | --foreground: 0 0% 98%;
46 |
47 | --card: 240 10% 3.9%;
48 | --card-foreground: 0 0% 98%;
49 |
50 | --popover: 240 10% 3.9%;
51 | --popover-foreground: 0 0% 98%;
52 |
53 | --primary: 0 0% 98%;
54 | --primary-foreground: 240 5.9% 10%;
55 |
56 | --secondary: 240 3.7% 15.9%;
57 | --secondary-foreground: 0 0% 98%;
58 |
59 | --muted: 240 3.7% 15.9%;
60 | --muted-foreground: 240 5% 64.9%;
61 |
62 | --accent: 240 3.7% 15.9%;
63 | --accent-foreground: 0 0% 98%;
64 |
65 | --destructive: 0 62.8% 30.6%;
66 | --destructive-foreground: 0 0% 98%;
67 |
68 | --border: 240 3.7% 15.9%;
69 | --input: 240 3.7% 15.9%;
70 | --ring: 240 4.9% 83.9%;
71 | }
72 | }
73 |
74 | @layer base {
75 | * {
76 | @apply border-border;
77 | font-family: 'minecraft';
78 | }
79 | body {
80 | @apply bg-background text-foreground;
81 | }
82 | }
83 |
84 | ::-webkit-scrollbar {
85 | display: none;
86 | }
87 | ::-webkit-scrollbar-thumb {
88 | display: none;
89 | }
90 | ::-webkit-scrollbar-track {
91 | display: none;
92 | }
93 | @keyframes scale-out-center {
94 | 0% {
95 | transform: scale(1);
96 | opacity: 1;
97 | }
98 | 100% {
99 | transform: scale(0);
100 | opacity: 0;
101 | }
102 | }
103 |
104 | .scale-out-center {
105 | animation: scale-out-center 0.3s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
106 | }
107 |
108 | .react-flow .react-flow__edge path,
109 | .react-flow__connectionline path {
110 | stroke-width: 4;
111 | }
112 |
113 | .SliderThumb {
114 | display: block;
115 | width: 20px;
116 | height: 20px;
117 | background-color: black;
118 | border-radius: 10%;
119 | }
--------------------------------------------------------------------------------
/frontend/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 | import { ThemeProvider } from "@/components/theme-provider";
5 | import { Toaster } from "@/components/ui/toaster";
6 | const inter = Inter({ subsets: ["latin"] });
7 | import { ReactFlowProvider } from "@xyflow/react";
8 |
9 | export const metadata: Metadata = {
10 | title: "Open Delivery Bot",
11 | description: "An open-source multi tool bot for minecraft servers",
12 | };
13 |
14 | export default function RootLayout({
15 | children,
16 | }: Readonly<{
17 | children: React.ReactNode;
18 | }>) {
19 | return (
20 |
21 |
22 |
28 | {children}
29 |
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/frontend/src/app/map/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .map-container {
6 | /* position: relative; */
7 | /* width: 500px;
8 | height: 500px; */
9 | margin: 0 auto;
10 | }
11 |
12 | .map-image {
13 | width: 100%;
14 | height: 100%;
15 | border-radius: 0.60rem;
16 | image-rendering: pixelated;
17 | }
18 |
19 | .entity {
20 | position: absolute;
21 | width: 6px;
22 | height: 6px;
23 | border-radius: 50%;
24 | transform: translate(-50%, -50%);
25 | }
26 |
27 | .entity.player {
28 | background-color: red;
29 | }
30 |
31 | .entity.mob {
32 | background-color: blue;
33 | }
34 |
35 | .tools {
36 | margin-bottom: 10px;
37 | }
38 |
39 | .tools button {
40 | margin: 0 5px;
41 | padding: 5px 10px;
42 | font-size: 16px;
43 | }
--------------------------------------------------------------------------------
/frontend/src/components/login.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import * as React from "react";
3 | import io from "socket.io-client";
4 | import {
5 | AlertDialog,
6 | AlertDialogCancel,
7 | AlertDialogContent,
8 | AlertDialogDescription,
9 | AlertDialogFooter,
10 | AlertDialogHeader,
11 | AlertDialogTitle,
12 | AlertDialogTrigger,
13 | } from "@/components/ui/alert-dialog";
14 | import { Separator } from "@/components/ui/separator";
15 |
16 | // import { bouncy } from "ldrs";
17 | import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp";
18 | import { useToast } from "@/components/ui/use-toast";
19 | import { Unplug, Copy } from "lucide-react";
20 | import { Button } from "@/components/ui/button";
21 | import {
22 | Select,
23 | SelectContent,
24 | SelectGroup,
25 | SelectItem,
26 | SelectTrigger,
27 | SelectValue,
28 | } from "@/components/ui/select";
29 | import {
30 | InputOTP,
31 | InputOTPGroup,
32 | InputOTPSeparator,
33 | InputOTPSlot,
34 | } from "@/components/ui/input-otp";
35 | import { set } from "react-hook-form";
36 | import { Input } from "@/components/ui/input";
37 | import { useApiIp } from "@/lib/utils/useApiIp";
38 | import { TbRefresh } from "react-icons/tb";
39 | import e from "cors";
40 |
41 | export function SetApiKeyDialog() {
42 | const { apiIp } = useApiIp();
43 | const socket = io(`${apiIp}`);
44 | const { toast } = useToast();
45 | const [apiKey, setApiKey] = React.useState("");
46 | const [isOpen, setIsOpen] = React.useState(false);
47 | const [isLoading, setIsLoading] = React.useState(false);
48 |
49 | const handleApiKeyChange = (e: React.ChangeEvent) => {
50 | setApiKey(e.target.value);
51 | };
52 |
53 | const setApiKey2 = async () => {
54 | setIsLoading(true);
55 | try {
56 | const response = await fetch(`${apiIp}/set-api-key`, {
57 | method: "POST",
58 | headers: {
59 | "Content-Type": "application/json",
60 | },
61 | body: JSON.stringify({ apiKey }),
62 | });
63 |
64 | if (!response.ok) {
65 | throw new Error(`HTTP error! status: ${response.status}`);
66 | }
67 |
68 | const data = await response.json();
69 | toast({
70 | title: "API Key Set",
71 | description: "Your API key has been set successfully.",
72 | });
73 | } catch (error) {
74 | console.error("Error setting API key:", error);
75 | toast({
76 | variant: "destructive",
77 | title: "Error",
78 | description: "Failed to set API key.",
79 | });
80 | } finally {
81 | setIsLoading(false);
82 | }
83 | };
84 |
85 | return (
86 | <>
87 | setIsOpen(open)}>
88 |
89 | setIsOpen(true)}
94 | >
95 | Set API Key
96 |
97 |
98 |
99 |
100 | Set NVIDIA API Key
101 |
102 | To use some parts of this application, you need an NVIDIA API key.
103 | Follow the instructions below to obtain and set your API key.
104 |
105 |
106 | Instructions to Obtain an NVIDIA API Key:
107 |
108 | 1. Visit{" "}
109 |
113 | nvidia playground
114 |
115 | .
116 |
117 | 2. Sign in or create an account.
118 |
119 | 3. Find the 'Get API Key' button above the example
120 | code
121 |
122 | 4. Click 'Generate Key'
123 |
124 | 5. Copy the key and paste it into the field below.
125 |
126 |
127 | Why is the API Key Useful?
128 |
129 | The API key is required to generate custom nodes based on a
130 | text input.
131 |
132 |
133 |
134 |
135 |
166 |
167 |
168 | >
169 | );
170 | }
171 |
172 | export function CreateBotDialog() {
173 | const { toast } = useToast();
174 | const { apiIp, setApiIp } = useApiIp();
175 | const [socket, setSocket] = React.useState(null);
176 | const [formData, setFormData] = React.useState({
177 | username: "",
178 | server: "",
179 | port: 25565,
180 | version: "",
181 | });
182 | const [msaCode, setMsaCode] = React.useState("");
183 | const [botSpawned, setBotSpawned] = React.useState(false);
184 | const [isOpen, setIsOpen] = React.useState(false);
185 | const [versions, setVersions] = React.useState([]);
186 | const [isButtonDisabled, setIsButtonDisabled] = React.useState(false);
187 | const [isLoading, setIsLoading] = React.useState(false);
188 | const [apiIpStatus, setApiIpStatus] = React.useState<
189 | "default" | "success" | "error"
190 | >("default");
191 |
192 | const debounceTimeout = React.useRef(null); // Ref to store the timeout
193 |
194 | const testApiIp = async (ip: string) => {
195 | try {
196 | const response = await fetch(`${ip}/bot-state`, {
197 | method: "GET",
198 | headers: {
199 | "Content-Type": "application/json",
200 | },
201 | });
202 |
203 | if (!response.ok) {
204 | throw new Error(`HTTP error! status: ${response.status}`);
205 | }
206 |
207 | const data = await response.json();
208 | setVersions(data.versions || []);
209 | setApiIpStatus("success");
210 | } catch (error) {
211 | console.error("Error fetching versions:", error);
212 | setApiIpStatus("error");
213 | setVersions([]);
214 | }
215 | };
216 |
217 | const handleApiIpChange = (e: React.ChangeEvent) => {
218 | const newApiIp = e.target.value;
219 | setApiIp(newApiIp);
220 | setApiIpStatus("default");
221 |
222 | if (debounceTimeout.current) {
223 | clearTimeout(debounceTimeout.current);
224 | }
225 |
226 | debounceTimeout.current = setTimeout(() => {
227 | testApiIp(newApiIp);
228 | }, 500);
229 | };
230 |
231 | const handleRefresh = () => {
232 | if (apiIp != "") {
233 | testApiIp(apiIp);
234 | } else {
235 | toast({
236 | variant: "destructive",
237 | title: "Error",
238 | description: "Please enter a valid API IP",
239 | });
240 | }
241 | };
242 |
243 | const handleChange = (e: React.ChangeEvent) => {
244 | const { name, value } = e.target;
245 | setFormData((prev) => ({ ...prev, [name]: value }));
246 | };
247 |
248 | const handleVersionChange = (value: string) => {
249 | setFormData({ ...formData, version: value });
250 | };
251 |
252 | const handleSubmit = async (e: React.FormEvent) => {
253 | e.preventDefault();
254 | setIsLoading(true);
255 | try {
256 | const response = await fetch(`${apiIp}/create-bot`, {
257 | method: "POST",
258 | headers: {
259 | "Content-Type": "application/json",
260 | },
261 | body: JSON.stringify(formData),
262 | });
263 |
264 | if (!response.ok) {
265 | throw new Error(`HTTP error! status: ${response.status}`);
266 | }
267 |
268 | const data = await response.json();
269 | console.log(data.message);
270 | } catch (error) {
271 | console.error("Error creating bot:", error);
272 | toast({
273 | variant: "destructive",
274 | title: "Error",
275 | description: "Failed to create bot",
276 | });
277 | } finally {
278 | setIsLoading(false);
279 | setIsOpen(false);
280 | }
281 | };
282 |
283 | React.useEffect(() => {
284 | if (apiIpStatus === "success" && !socket) {
285 | const newSocket = io(`${apiIp}`);
286 | setSocket(newSocket);
287 |
288 | newSocket.on("msaCode", (code: string) => {
289 | setMsaCode(code);
290 | setIsButtonDisabled(true);
291 | });
292 |
293 | newSocket.on("botSpawned", () => {
294 | localStorage.setItem("openSidebarOnLoad", "true");
295 | setBotSpawned(true);
296 | setIsOpen(false);
297 | const iframes = document.querySelectorAll("iframe");
298 | iframes.forEach((iframe) => {
299 | iframe.src = iframe.src;
300 | });
301 |
302 | toast({
303 | title: "Bot Spawned Successfully",
304 | description: msaCode
305 | ? "Logged in with new MSA code"
306 | : "Logged in with existing MSA code",
307 | });
308 | });
309 |
310 | return () => {
311 | newSocket.off("msaCode");
312 | newSocket.off("botSpawned");
313 | };
314 | }
315 | }, [apiIpStatus, apiIp, socket, msaCode, toast]);
316 |
317 | return (
318 | !botSpawned && setIsOpen(open)}
321 | >
322 |
323 | setIsOpen(true)}
328 | >
329 |
330 | Connect a bot
331 |
332 |
333 |
334 |
335 |
336 | Create a Minecraft Bot
337 |
338 |
339 | Enter the details to create and connect a Minecraft bot.
340 |
341 |
342 |
343 |
544 |
545 |
546 | );
547 | }
548 |
549 | export function StopBotDialog() {
550 | const { toast } = useToast();
551 | const { apiIp } = useApiIp();
552 | const handleDisconnect = async () => {
553 | try {
554 | const response = await fetch(`${apiIp}/stop-bot`, {
555 | method: "POST",
556 | headers: {
557 | "Content-Type": "application/json",
558 | },
559 | });
560 |
561 | if (response.ok) {
562 | const data = await response.json();
563 | console.log(data.message); // Log success message or handle UI update
564 | toast({
565 | title: "Bot Stopped Successfully",
566 | // description: "The bot has been stopped successfully",
567 | });
568 | } else {
569 | const errorData = await response.json();
570 | console.error(errorData.error); // Log error message or handle UI update
571 | toast({
572 | variant: "destructive",
573 | title: "Error Stopping Bot",
574 | description:
575 | "An error occurred while stopping the bot: " + errorData.error,
576 | });
577 | window.location.reload();
578 | }
579 | } catch (error) {
580 | console.error("Error stopping the bot:", error); // Handle network or other errors
581 | window.location.reload();
582 | }
583 | };
584 | return (
585 | <>
586 |
587 |
593 |
594 | Disconnect
595 |
596 |
or
597 |
598 |
599 |
600 | >
601 | );
602 | }
603 |
--------------------------------------------------------------------------------
/frontend/src/components/modeswitch.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { Moon, Sun } from "lucide-react";
5 | import { useTheme } from "next-themes";
6 |
7 | import { Button } from "@/components/ui/button";
8 | import {
9 | DropdownMenu,
10 | DropdownMenuContent,
11 | DropdownMenuItem,
12 | DropdownMenuTrigger,
13 | } from "@/components/ui/dropdown-menu";
14 | import {
15 | Tooltip,
16 | TooltipContent,
17 | TooltipProvider,
18 | TooltipTrigger,
19 | } from "@/components/ui/tooltip";
20 |
21 | export function ModeToggle() {
22 | const { theme, setTheme } = useTheme();
23 |
24 | return (
25 | <>
26 |
27 |
28 |
29 |
30 |
31 |
32 | <>
33 | {/* */}
34 | {theme === "light" && (
35 |
41 | )}
42 | {theme === "dark" && (
43 |
49 | )}
50 | >
51 | {/* */}
52 | Toggle theme
53 |
54 |
55 |
56 | setTheme("light")}>
57 | Light
58 |
59 | setTheme("dark")}>
60 | Dark
61 |
62 | {/* setTheme("system")}>
63 | System
64 | */}
65 |
66 |
67 |
68 |
69 | Select theme
70 |
71 |
72 |
73 | >
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/frontend/src/components/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { ThemeProvider as NextThemesProvider } from "next-themes";
5 | import { type ThemeProviderProps } from "next-themes/dist/types";
6 |
7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
8 | return {children} ;
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/alert-dialog.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
5 |
6 | import { cn } from "@/lib/utils"
7 | import { buttonVariants } from "@/components/ui/button"
8 |
9 | const AlertDialog = AlertDialogPrimitive.Root
10 |
11 | const AlertDialogTrigger = AlertDialogPrimitive.Trigger
12 |
13 | const AlertDialogPortal = AlertDialogPrimitive.Portal
14 |
15 | const AlertDialogOverlay = React.forwardRef<
16 | React.ElementRef,
17 | React.ComponentPropsWithoutRef
18 | >(({ className, ...props }, ref) => (
19 |
27 | ))
28 | AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
29 |
30 | const AlertDialogContent = React.forwardRef<
31 | React.ElementRef,
32 | React.ComponentPropsWithoutRef
33 | >(({ className, ...props }, ref) => (
34 |
35 |
36 |
44 |
45 | ))
46 | AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
47 |
48 | const AlertDialogHeader = ({
49 | className,
50 | ...props
51 | }: React.HTMLAttributes) => (
52 |
59 | )
60 | AlertDialogHeader.displayName = "AlertDialogHeader"
61 |
62 | const AlertDialogFooter = ({
63 | className,
64 | ...props
65 | }: React.HTMLAttributes) => (
66 |
73 | )
74 | AlertDialogFooter.displayName = "AlertDialogFooter"
75 |
76 | const AlertDialogTitle = React.forwardRef<
77 | React.ElementRef,
78 | React.ComponentPropsWithoutRef
79 | >(({ className, ...props }, ref) => (
80 |
85 | ))
86 | AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
87 |
88 | const AlertDialogDescription = React.forwardRef<
89 | React.ElementRef,
90 | React.ComponentPropsWithoutRef
91 | >(({ className, ...props }, ref) => (
92 |
97 | ))
98 | AlertDialogDescription.displayName =
99 | AlertDialogPrimitive.Description.displayName
100 |
101 | const AlertDialogAction = React.forwardRef<
102 | React.ElementRef,
103 | React.ComponentPropsWithoutRef
104 | >(({ className, ...props }, ref) => (
105 |
110 | ))
111 | AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
112 |
113 | const AlertDialogCancel = React.forwardRef<
114 | React.ElementRef,
115 | React.ComponentPropsWithoutRef
116 | >(({ className, ...props }, ref) => (
117 |
126 | ))
127 | AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
128 |
129 | export {
130 | AlertDialog,
131 | AlertDialogPortal,
132 | AlertDialogOverlay,
133 | AlertDialogTrigger,
134 | AlertDialogContent,
135 | AlertDialogHeader,
136 | AlertDialogFooter,
137 | AlertDialogTitle,
138 | AlertDialogDescription,
139 | AlertDialogAction,
140 | AlertDialogCancel,
141 | }
142 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13 | secondary:
14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15 | destructive:
16 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17 | outline: "text-foreground",
18 | },
19 | },
20 | defaultVariants: {
21 | variant: "default",
22 | },
23 | }
24 | )
25 |
26 | export interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | function Badge({ className, variant, ...props }: BadgeProps) {
31 | return (
32 |
33 | )
34 | }
35 |
36 | export { Badge, badgeVariants }
37 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive:
14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15 | outline:
16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17 | secondary:
18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19 | ghost: "hover:bg-accent hover:text-accent-foreground",
20 | link: "text-primary underline-offset-4 hover:underline",
21 | },
22 | size: {
23 | default: "h-10 px-4 py-2",
24 | sm: "h-9 rounded-md px-3",
25 | lg: "h-11 rounded-md px-8",
26 | icon: "h-10 w-10",
27 | },
28 | },
29 | defaultVariants: {
30 | variant: "default",
31 | size: "default",
32 | },
33 | }
34 | )
35 |
36 | export interface ButtonProps
37 | extends React.ButtonHTMLAttributes,
38 | VariantProps {
39 | asChild?: boolean
40 | }
41 |
42 | const Button = React.forwardRef(
43 | ({ className, variant, size, asChild = false, ...props }, ref) => {
44 | const Comp = asChild ? Slot : "button"
45 | return (
46 |
51 | )
52 | }
53 | )
54 | Button.displayName = "Button"
55 |
56 | export { Button, buttonVariants }
57 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Card = React.forwardRef<
6 | HTMLDivElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
17 | ))
18 | Card.displayName = "Card"
19 |
20 | const CardHeader = React.forwardRef<
21 | HTMLDivElement,
22 | React.HTMLAttributes
23 | >(({ className, ...props }, ref) => (
24 |
29 | ))
30 | CardHeader.displayName = "CardHeader"
31 |
32 | const CardTitle = React.forwardRef<
33 | HTMLParagraphElement,
34 | React.HTMLAttributes
35 | >(({ className, ...props }, ref) => (
36 |
44 | ))
45 | CardTitle.displayName = "CardTitle"
46 |
47 | const CardDescription = React.forwardRef<
48 | HTMLParagraphElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ))
57 | CardDescription.displayName = "CardDescription"
58 |
59 | const CardContent = React.forwardRef<
60 | HTMLDivElement,
61 | React.HTMLAttributes
62 | >(({ className, ...props }, ref) => (
63 |
64 | ))
65 | CardContent.displayName = "CardContent"
66 |
67 | const CardFooter = React.forwardRef<
68 | HTMLDivElement,
69 | React.HTMLAttributes
70 | >(({ className, ...props }, ref) => (
71 |
76 | ))
77 | CardFooter.displayName = "CardFooter"
78 |
79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
80 |
--------------------------------------------------------------------------------
/frontend/src/components/ui/chart.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as RechartsPrimitive from "recharts"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | // Format: { THEME_NAME: CSS_SELECTOR }
9 | const THEMES = { light: "", dark: ".dark" } as const
10 |
11 | export type ChartConfig = {
12 | [k in string]: {
13 | label?: React.ReactNode
14 | icon?: React.ComponentType
15 | } & (
16 | | { color?: string; theme?: never }
17 | | { color?: never; theme: Record }
18 | )
19 | }
20 |
21 | type ChartContextProps = {
22 | config: ChartConfig
23 | }
24 |
25 | const ChartContext = React.createContext(null)
26 |
27 | function useChart() {
28 | const context = React.useContext(ChartContext)
29 |
30 | if (!context) {
31 | throw new Error("useChart must be used within a ")
32 | }
33 |
34 | return context
35 | }
36 |
37 | const ChartContainer = React.forwardRef<
38 | HTMLDivElement,
39 | React.ComponentProps<"div"> & {
40 | config: ChartConfig
41 | children: React.ComponentProps<
42 | typeof RechartsPrimitive.ResponsiveContainer
43 | >["children"]
44 | }
45 | >(({ id, className, children, config, ...props }, ref) => {
46 | const uniqueId = React.useId()
47 | const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
48 |
49 | return (
50 |
51 |
60 |
61 |
62 | {children}
63 |
64 |
65 |
66 | )
67 | })
68 | ChartContainer.displayName = "Chart"
69 |
70 | const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
71 | const colorConfig = Object.entries(config).filter(
72 | ([_, config]) => config.theme || config.color
73 | )
74 |
75 | if (!colorConfig.length) {
76 | return null
77 | }
78 |
79 | return (
80 |