├── .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 | Node version 20 | javascript 21 | Hungarian 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 | [![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/tEXtJFuGw8Y/0.jpg)](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 | Star History Chart 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 | 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 |
{ 137 | e.preventDefault(); 138 | setApiKey2(); 139 | }} 140 | > 141 |
142 |
143 | 146 | 156 |
157 |
158 |
159 | 160 | Cancel 161 | 164 | 165 | 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 | 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 |
344 |
345 |
346 | 349 |
350 | 364 | 373 |
374 |
375 | {apiIpStatus === "error" && ( 376 | <> 377 | 378 | PS. If the input box above is showing red but the api is valid 379 | follow these steps:
380 | 1.{" "} 381 | 382 | Open the backend ip 383 | {" "} 384 | in a new tab in your preferred browser. 385 |
386 | 2. there should be an option to proceed to the site. (this is 387 | because the site is using a self signed certificate) 388 |
389 | 3. Also make shure there is no / at the end of the url 390 |
391 | 392 | )} 393 | {apiIpStatus === "success" && ( 394 | <> 395 |
396 | 399 | 407 |
408 |
409 | 412 | 420 |
421 |
422 | 425 | 434 |
435 |
436 | 439 |
440 | 463 |
464 |
465 | 466 | )} 467 | 468 | {msaCode && ( 469 | <> 470 | 471 |
472 |

MSA

473 |

474 | It appears to be your first time signing in to this account. 475 | To sign in, use a web browser to open the page{" "} 476 | 481 | https://www.microsoft.com/link 482 | {" "} 483 | and use the code below. 484 |

485 | 525 |
526 | 527 | Cancel 528 | {msaCode ? ( 529 | 532 | ) : ( 533 | 541 | )} 542 | 543 | 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 | 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 | 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 |