├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .prettierignore ├── .prettierrc.yaml ├── LICENSE ├── README.md ├── VERSION ├── docs ├── CNAME ├── css │ └── styles.css ├── files │ ├── background.svg │ ├── discord.svg │ ├── github.svg │ ├── icon-premium.png │ ├── icon.png │ └── info.svg ├── index.html └── js │ └── script.js ├── electron-builder.yml ├── electron.vite.config.mjs ├── package.json └── src ├── main ├── index.js └── js │ ├── misc │ ├── antiafk.js │ ├── customAuth.js │ └── utils.js │ └── proxy │ ├── proxycheck.js │ ├── proxyhandler.js │ └── proxyscrape.js ├── preload └── index.js └── renderer ├── assets ├── css │ ├── base.css │ └── main.css └── icons │ ├── alert.svg │ ├── background.svg │ ├── clock.svg │ ├── discord.svg │ ├── icon.png │ ├── min.svg │ ├── settings.svg │ ├── spinner.svg │ ├── user.svg │ └── x.svg ├── index.html └── src └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | .gitignore 5 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['eslint:recommended', '@electron-toolkit', '@electron-toolkit/eslint-config-prettier'] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | package-lock.json 5 | .DS_Store 6 | *.log* 7 | .vscode 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | pnpm-lock.yaml 4 | LICENSE.md 5 | tsconfig.json 6 | tsconfig.*.json 7 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | singleQuote: true 2 | semi: false 3 | printWidth: 100 4 | trailingComma: none 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 RattlesHyper 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 |

The best, free and open source Minecraft botting tool.

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

14 | 15 | 16 | ## Features 17 | TrafficerMC has a variety of features. Some of them are: 18 | - Anti AFK 19 | - Spam with Bypass 20 | - Name Generator 21 | - Full Inventory Control 22 | - Movement Control 23 | - EasyMC Support 24 | - Auto Reconnect 25 | - Killaura 26 | - [Scripting](#scripting) 27 | - [Proxies](#proxies) 28 | - and way more! 29 | 30 | #### Minecraft Version 1.8.x - 1.20 31 | 32 | ##### Check out [Build Guide](#build-guide) to build TrafficerMC for your machine. 33 | 34 | ## Media 35 | 36 | ![TrafficerMC v3.0](https://github.com/RattlesHyper/TrafficerMC/assets/83329088/f0d90bb4-ca3b-4a7c-acdc-b851e386d632) 37 | **TrafficerMC v3.0** 38 | 39 | ![Settings](https://github.com/RattlesHyper/TrafficerMC/assets/83329088/b1b103f1-1720-46fc-91ae-22e4ac186227) 40 | **Settings** 41 | 42 | ![Botting](https://github.com/RattlesHyper/TrafficerMC/assets/83329088/b1951a59-7c90-40a9-b273-1b0feab52d92) 43 | **Botting** 44 | 45 | ![Scripting](https://github.com/RattlesHyper/TrafficerMC/assets/83329088/2a25acd9-0c63-4042-8f11-68179aec0baa) 46 | **Scripting** 47 | 48 | ![Proxy](https://github.com/RattlesHyper/TrafficerMC/assets/83329088/36f7cf6d-80e3-4d63-bb4f-ed23e8df8df1) 49 | **Proxy** 50 | 51 | ## Tutorials 52 | [![](https://img.youtube.com/vi/lD3poymjVAk/maxresdefault.jpg)](https://www.youtube.com/watch?v=lD3poymjVAk) 53 | TrafficerMC v2.1 Tutorial Video 54 | 55 | [![](https://img.youtube.com/vi/eAe9m-d-el0/maxresdefault.jpg)](https://www.youtube.com/watch?v=eAe9m-d-el0) 56 | TrafficerMC v0.4 Preview Video (Outdated) 57 | 58 | # Scripting 59 | Scripting can allow the bot to do things automatically without any user input. It can also help if you are making multiple bots do the same things. All script executions will be shown on manual inputs, it can be used in scripting. 60 | 61 | ## What a script would look like 62 | ``` 63 | chat Hello from TrafficerMC! 64 | delay 1000 65 | useheld 66 | delay 2000 67 | winclick 36 0 68 | delay 1000 69 | disconnect 70 | ``` 71 | ## Features 72 | - [Chat](#chat) 73 | - [Use Held Item](#useheld) 74 | - [Set Hotbar Slot](#sethotbar) 75 | - [Click Inventory Item](#winclick) 76 | - [Close Window](#closewindow) 77 | - [Drop](#drop) 78 | - [Movement](#movement) 79 | - [Anti-AFK](#anti-afk) 80 | - [Disconnect](#disconnect) 81 | - [Reconnect](#reconnect) 82 | - [Loop](#startscript) 83 | - [Delay](#delay) 84 | 85 | ### Chat 86 | Sends a message to the servers chat. 87 | Usage: `chat ` 88 | 89 | Variables: Player Name: `{player}`, Random String: `{random}` 90 | 91 | Example: 92 | ``` 93 | chat Hello there! {player} 94 | chat /help 95 | ``` 96 | ### useHeld 97 | Uses the current held item. 98 | Usage: `useHeld` 99 | ### setHotbar 100 | Sets hotbar to the selected slot. 101 | Usage: `setHotbar ` 102 | 103 | Example: 104 | ``` 105 | setHotbar 0 106 | setHotbar 3 107 | ``` 108 | ### winClick 109 | Clicks on a window item. To left click, use `0`. Otherwise, to right click, use `1`. 110 | Usage: `winClick ` 111 | 112 | Example: 113 | ``` 114 | winClick 36 0 115 | winClick 24 1 116 | ``` 117 | **If you are struggling on what slots you should click, here are some images to represent!** 118 | 119 | **Every chest starts from `0`** 120 | 121 | #### Chest interface: 122 | 123 | ![Chest](https://github.com/RattlesHyper/TrafficerMC/assets/83329088/058fd1d2-69c2-41f6-bfd3-3b9665c037e5) 124 | 125 | #### Inventory interface: 126 | 127 | ![Inventory](https://github.com/RattlesHyper/TrafficerMC/assets/83329088/83f952e2-18f1-4c79-8a6d-e7a6880a6879) 128 | 129 | ### closeWindow 130 | Closes the current window. 131 | Usage: `closeWindow` 132 | ### Drop 133 | Drops the slot item if none specified drops all. 134 | Usage: `drop ` 135 | 136 | Example: 137 | ``` 138 | drop 139 | drop 36 140 | ``` 141 | ### Movement 142 | Controls the bot movement. 143 | Usage: `startMove/stopMove ` 144 | 145 | Reset controls: `resetMove` 146 | 147 | Example: 148 | ``` 149 | startControl forward 150 | startControl jump 151 | stopControl forward 152 | ``` 153 | ### Anti-AFK 154 | Enables/Disables Anti-AFK on the bot. 155 | Usage: `afkOn/afkOff` 156 | ### Disconnect 157 | Disconnects the Bot. 158 | Usage: `disconnect` 159 | ### Reconnect 160 | Reconnects the bot. 161 | Usage: `reconnect` 162 | ### startScript 163 | Starts the script. 164 | Usage: `startScript` 165 | ### Delay 166 | Delays the next task. 167 | Usage: `delay ` default 1000 168 | 169 | Example: 170 | ``` 171 | chat Hi 172 | delay 1200 173 | chat Hello 174 | ``` 175 | # Account File 176 | Account file must be a `.txt` file. Put the usernames of the accounts you want to use line by line. Microsoft accounts can be used the same way. 177 | 178 | **Please note that you can set Max Accounts in General Tab or it will use all the names** 179 | ## What an account file would look like 180 | ``` 181 | vampers 182 | nVoid 183 | Danilo764 184 | ``` 185 | # Proxies 186 | TrafficerMC currently support all protocols and Auth proxies, HTTP is premium only. 187 | ## What a proxy file would look like 188 | ``` 189 | ProxyIP:ProxyPORT:Username:Password 190 | 98.7.65.4:32101 191 | ``` 192 | 193 | # Build Guide 194 | 195 | **Requirements:** [NodeJS](https://nodejs.org/en/download) 196 | 197 | Clone TrafficerMC repo with Git Clone: `git clone https://github.com/RattlesHyper/TrafficerMC` or Download the Source Code, then navigate to the folder. and open Terminal/Command Prompt/PowerShell 198 | 199 | **Build Commands:** 200 | 201 | **Install Dependencies** `npm install` 202 | 203 | **Windows:** `build:win` 204 | 205 | **Mac:** `build:mac` 206 | 207 | **Linux:** `build:linux` 208 | 209 | If you want to run from source code use `npm run dev` 210 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 3.1 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | trafficermc.com -------------------------------------------------------------------------------- /docs/css/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap'); 2 | 3 | body { 4 | font-weight: 600; 5 | color: var(--text-primary); 6 | background: radial-gradient(circle, rgb(26, 9, 65) 0%, rgb(28, 8, 44) 100%); 7 | } 8 | 9 | .space-h { 10 | align-items: center; 11 | justify-content: space-between; 12 | } 13 | 14 | .flex { 15 | display: flex; 16 | } 17 | 18 | .center { 19 | align-items: center; 20 | } 21 | 22 | .center-items { 23 | display: flex; 24 | align-items: center; 25 | justify-content: center; 26 | } 27 | 28 | .p-4 { 29 | padding: 1rem; 30 | } 31 | 32 | .pl-1 { 33 | padding-left: 10px; 34 | } 35 | 36 | .space { 37 | margin-left: 5px; 38 | } 39 | 40 | .mu-8 { 41 | margin-top: 4rem; 42 | } 43 | 44 | .mu-4 { 45 | margin-top: 1rem; 46 | } 47 | 48 | .icon { 49 | height: 25px; 50 | width: 25px; 51 | } 52 | 53 | .icon-big { 54 | height: 35px; 55 | width: 35px; 56 | } 57 | 58 | .tip-sm { 59 | font-size: 12px; 60 | color: var(--text-secondary); 61 | } 62 | 63 | .background { 64 | width: 100%; 65 | top: 0px; 66 | left: 0px; 67 | } 68 | 69 | .main-container { 70 | overflow: hidden; 71 | min-height: 20rem; 72 | display: flex; 73 | align-items: center; 74 | background-size: cover; 75 | } 76 | 77 | .bg { 78 | background: url(../files/background.svg); 79 | } 80 | 81 | .container { 82 | padding: 0 4rem; 83 | width: 100%; 84 | } 85 | 86 | .options { 87 | justify-content: center; 88 | display: grid; 89 | grid-template-columns: repeat(auto-fit, 495px); 90 | } 91 | 92 | .title { 93 | display: flex; 94 | justify-content: center 95 | } 96 | 97 | .title-text { 98 | font-size: 2rem; 99 | } 100 | 101 | .section-2 { 102 | overflow: hidden; 103 | display: flex; 104 | align-items: center; 105 | justify-content: center; 106 | } 107 | 108 | .row { 109 | display: flex; 110 | padding: 0px 15px; 111 | justify-content: space-between; 112 | box-sizing: border-box; 113 | border-top: solid 2px var(--accent); 114 | background-color: var(--accent-secondary); 115 | } 116 | 117 | .row-first { 118 | width: 200px; 119 | } 120 | 121 | .row-second { 122 | width: 100px; 123 | } 124 | 125 | .row-third { 126 | margin-right: 30px; 127 | } 128 | 129 | .box { 130 | display: flex; 131 | justify-content: space-between; 132 | flex-direction: column; 133 | padding: 1rem; 134 | margin: 10px; 135 | border-radius: 10px; 136 | box-sizing: border-box; 137 | min-height: 13rem; 138 | background-color: var(--accent-secondary); 139 | box-shadow: var(--accent) 0px 0px 15px; 140 | } 141 | 142 | .button { 143 | background-color: var(--accent); 144 | width: fit-content; 145 | height: fit-content; 146 | cursor: pointer; 147 | border-radius: 5px; 148 | text-decoration: none; 149 | transition: all 0.3s ease 0s; 150 | } 151 | 152 | .button .inner { 153 | padding: 0px 25px; 154 | justify-content: space-between; 155 | width: 6rem; 156 | } 157 | 158 | .button:hover { 159 | background-color: var(--button-hover-bg); 160 | box-shadow: var(--text-primary) 0px 0px 4px; 161 | } 162 | 163 | .menu { 164 | position: sticky; 165 | top: calc(50% - 150px); 166 | height: 160px; 167 | list-style-type: none; 168 | padding: 0 10px 0 4rem; 169 | margin-top: 5rem; 170 | border-right: solid 2px var(--accent); 171 | } 172 | 173 | .sticky { 174 | position: sticky; 175 | top: 0; 176 | } 177 | 178 | .menu a { 179 | color: var(--text-disabled); 180 | text-decoration: none; 181 | transition: color 0.5s ease; 182 | } 183 | 184 | .menu .active { 185 | position: relative; 186 | left: 10px; 187 | color: var(--text-primary); 188 | } 189 | 190 | .menu a:hover { 191 | color: var(--text-primary); 192 | } 193 | 194 | .table-content { 195 | border-top: solid 4px var(--accent); 196 | margin: 0 4rem 100px 50px; 197 | line-height: 1; 198 | width: 100%; 199 | } 200 | 201 | .checkmark { 202 | margin: 15px 35px 0px 0px; 203 | width: 6px; 204 | height: 10px; 205 | border: solid var(--text-secondary); 206 | border-width: 0 3px 3px 0; 207 | -webkit-transform: rotate(45deg); 208 | -ms-transform: rotate(45deg); 209 | transform: rotate(45deg); 210 | } 211 | 212 | .checks { 213 | display: flex; 214 | justify-content: space-between; 215 | width: 210px; 216 | } 217 | 218 | .video { 219 | height: 320px; 220 | display: flex; 221 | width: fit-content; 222 | } 223 | 224 | .video-title { 225 | margin: 15px 0 0 0; 226 | font-size: 12px; 227 | } 228 | 229 | .footer { 230 | background-color: var(--accent); 231 | height: 100px; 232 | } 233 | 234 | .footer-text { 235 | margin-top: 80px; 236 | font-size: 10px; 237 | color: var(--text-disabled); 238 | } 239 | 240 | .text-disabled { 241 | color: var(--text-disabled); 242 | } 243 | 244 | .hidden { 245 | display: none; 246 | } 247 | 248 | :root { 249 | --accent: #140e0e; 250 | --accent-secondary: rgba(54, 41, 90, 0.3); 251 | --button-hover-bg: rgba(47, 1, 139, 0.1); 252 | --text-primary: white; 253 | --text-secondary: #E6E6E6; 254 | --text-disabled: #7b7b7b; 255 | --discord: #7388da; 256 | --paypal: #026fba; 257 | --github: #242a2e; 258 | --youtube: #ff0201; 259 | font-family: 'Poppins', sans-serif; 260 | } 261 | -------------------------------------------------------------------------------- /docs/files/background.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/files/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/files/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/files/icon-premium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RattlesHyper/TrafficerMC/e9bd554ffd695ab56781104d357363aa3442b347/docs/files/icon-premium.png -------------------------------------------------------------------------------- /docs/files/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RattlesHyper/TrafficerMC/e9bd554ffd695ab56781104d357363aa3442b347/docs/files/icon.png -------------------------------------------------------------------------------- /docs/files/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TrafficerMC - Botting Tool 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 |
18 | 19 |

TrafficerMC

20 |
21 |

Access Your Botting Arsenal Here!

22 |

Best Free and Open Source Minecraft Botting Tool

23 | 43 |
44 |
45 | 51 |
52 |
53 |
54 |
55 | 56 |

TrafficerMC

57 |
58 |

Everything you need to get started. It comes with all the essential features for your 59 | basic botting needs.

60 |
61 | 62 |
63 | 64 |

Download

65 |
66 |
67 |
68 |
69 |
70 |
71 | 72 |

TrafficerMC Premium

73 |
74 |

Enjoy better stability and advanced botting capabilities. With enhanced performance and 75 | exclusive features.

76 |
77 | 78 |
79 | 80 |

Contact

81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |

Videos

89 |
90 |
91 |
92 |
93 |
94 | 98 |

TrafficerMC Premium v1.0 Preview

99 |
100 |
101 | 105 |

TrafficerMC v2.1 Usage Tutorial

106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |

Feature List

114 |
115 |
116 |
117 | 137 |
138 |
139 |
140 |

Features

141 |
142 |

Premium

143 |

Free

144 |
145 |
146 |
147 |

General

148 |
149 |
150 |

Name Generator

151 |
152 | 153 | 154 |
155 |
156 |
157 |

EasyMC

158 |
159 | 160 | 161 |
162 |
163 |
164 |

TheAltening

165 |
166 | 167 |
168 |
169 |
170 |

Minimal Mode

171 |
172 | 173 | 174 |
175 |
176 |
177 |

Botting

178 |
179 |
180 |

Chat

181 |
182 | 183 | 184 |
185 |
186 |
187 |

Spam

188 |
189 | 190 | 191 |
192 |
193 |
194 |

Chat Variables

195 |
196 | 197 | 198 |
199 |
200 |
201 |

Hotbar

202 |
203 | 204 | 205 |
206 |
207 |
208 |

Inventory

209 |
210 | 211 | 212 |
213 |
214 |
215 |

Move

216 |
217 | 218 | 219 |
220 |
221 |
222 |

Look

223 |
224 | 225 | 226 |
227 |
228 |
229 |

Anti AFK

230 |
231 | 232 | 233 |
234 |
235 |
236 |

KillAura

237 |
238 | 239 | 240 |
241 |
242 |
243 |

Interact

244 |
245 | 246 |
247 |
248 |
249 |

Nuker

250 |
251 | 252 |
253 |
254 |
255 |

Pathfinder

256 |
257 | 258 |
259 |
260 |
261 |

Scripting

262 |
263 |
264 |

Full Functionality

265 |
266 | 267 | 268 |
269 |
270 |
271 |

Proxies

272 |
273 |
274 |

Proxy Scrape

275 |
276 | 277 | 278 |
279 |
280 |
281 |

Proxy Checker

282 |
283 | 284 | 285 |
286 |
287 |
288 |

SOCKS4

289 |
290 | 291 | 292 |
293 |
294 |
295 |

SOCKS5

296 |
297 | 298 | 299 |
300 |
301 |
302 |

HTTP

303 |
304 | 305 |
306 |
307 |
308 |

Webhooks

309 |
310 |
311 |

Discord Webhook

312 |
313 | 314 |
315 |
316 |
317 |

Settings

318 |
319 |
320 |

Linear Delay

321 |
322 | 323 | 324 |
325 |
326 |
327 |

Auto Reconnect

328 |
329 | 330 | 331 |
332 |
333 |
334 |

Map Image Viewer

335 |
336 | 337 |
338 |
339 |
340 |

Accept Resource Pack

341 |
342 | 343 |
344 |
345 |
346 |

Selective Chat Logs

347 |
348 | 349 |
350 |
351 |
352 |
353 |
354 |
355 | 356 |
357 | 358 | 359 | 360 | 361 | -------------------------------------------------------------------------------- /docs/js/script.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | const ul = document.getElementById('menu') 3 | const links = ul.getElementsByTagName('a') 4 | 5 | for (let i = 0; i < links.length; i++) { 6 | links[i].addEventListener('click', function (event) { 7 | for (let j = 0; j < links.length; j++) { 8 | links[j].classList.remove('active') 9 | } 10 | this.classList.add('active') 11 | }) 12 | } 13 | 14 | document.addEventListener('mousemove', (e) => { 15 | const { clientX: mouseX, clientY: mouseY } = e 16 | const { innerWidth: windowWidth, innerHeight: windowHeight } = window 17 | 18 | const percentX = mouseX / windowWidth 19 | const percentY = mouseY / windowHeight 20 | 21 | const moveX = (percentX - 0.5) * 50 22 | const moveY = (percentY - 0.5) * 50 23 | 24 | const background = document.getElementById('container') 25 | background.style.backgroundPosition = `${50 + moveX}% ${50 + moveY}%` 26 | }) 27 | 28 | fetch('https://api.github.com/repos/RattlesHyper/TrafficerMC/releases') 29 | .then((response) => response.json()) 30 | .then((data) => { 31 | let totalDownloadCount = 0 32 | data.forEach((release) => { 33 | release.assets.forEach((asset) => { 34 | totalDownloadCount += asset.download_count 35 | }) 36 | }) 37 | const downloadTitle = document.getElementById('download-title') 38 | const count = document.getElementById('downloadcount') 39 | downloadTitle.className = '' 40 | count.innerHTML = totalDownloadCount 41 | }) 42 | .catch((error) => console.error('Error:', error)) 43 | -------------------------------------------------------------------------------- /electron-builder.yml: -------------------------------------------------------------------------------- 1 | appId: com.rattleshyper.trafficermc 2 | productName: TrafficerMC 3 | directories: 4 | buildResources: build 5 | files: 6 | - '!**/.vscode/*' 7 | - '!src/*' 8 | - '!electron.vite.config.{js,ts,mjs,cjs}' 9 | - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' 10 | - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' 11 | asarUnpack: 12 | - resources/** 13 | win: 14 | target: portable 15 | icon: src/renderer/assets/icons/icon.png 16 | nsis: 17 | artifactName: ${name}-${version}-setup.${ext} 18 | shortcutName: ${productName} 19 | uninstallDisplayName: ${productName} 20 | createDesktopShortcut: always 21 | mac: 22 | entitlementsInherit: build/entitlements.mac.plist 23 | extendInfo: 24 | - NSCameraUsageDescription: Application requests access to the device's camera. 25 | - NSMicrophoneUsageDescription: Application requests access to the device's microphone. 26 | - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. 27 | - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. 28 | notarize: false 29 | dmg: 30 | artifactName: ${name}-${version}.${ext} 31 | linux: 32 | target: 33 | - AppImage 34 | - snap 35 | - deb 36 | maintainer: electronjs.org 37 | category: Utility 38 | appImage: 39 | artifactName: ${name}-${version}.${ext} 40 | npmRebuild: false 41 | publish: 42 | provider: generic 43 | url: 44 | -------------------------------------------------------------------------------- /electron.vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'electron-vite' 2 | 3 | export default defineConfig({ 4 | main: { 5 | plugins: [] 6 | }, 7 | preload: { 8 | plugins: [] 9 | }, 10 | renderer: {} 11 | }) 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "trafficermc", 3 | "version": "3.1.0", 4 | "description": "A Minecraft botting tool with Anti-AFK, Chat spammer, Inventory/Chest manager features.", 5 | "main": "./out/main/index.js", 6 | "author": "RattlesHyper", 7 | "scripts": { 8 | "format": "prettier --write .", 9 | "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix", 10 | "start": "electron-vite preview", 11 | "dev": "electron-vite dev", 12 | "build": "electron-vite build", 13 | "postinstall": "electron-builder install-app-deps", 14 | "build:unpack": "npm run build && electron-builder --dir", 15 | "build:win": "npm run build && electron-builder --win", 16 | "build:mac": "npm run build && electron-builder --mac", 17 | "build:linux": "npm run build && electron-builder --linux" 18 | }, 19 | "dependencies": { 20 | "@electron-toolkit/preload": "^3.0.0", 21 | "@electron-toolkit/utils": "^3.0.0", 22 | "electron-store": "^8.2.0", 23 | "mineflayer": "^4.20.1", 24 | "socks": "^2.8.3" 25 | }, 26 | "devDependencies": { 27 | "@electron-toolkit/eslint-config": "^1.0.2", 28 | "@electron-toolkit/eslint-config-prettier": "^2.0.0", 29 | "electron": "^28.2.0", 30 | "electron-builder": "^24.9.1", 31 | "electron-vite": "^2.0.0", 32 | "eslint": "^8.56.0", 33 | "prettier": "^3.2.4", 34 | "vite": "^5.0.12" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-case-declarations */ 2 | import { app, shell, BrowserWindow, ipcMain, dialog } from 'electron' 3 | import { join } from 'path' 4 | import crypto from 'crypto' 5 | import { electronApp, optimizer, is } from '@electron-toolkit/utils' 6 | import fs from 'fs' 7 | import { connection } from './js/proxy/proxyhandler' 8 | import { checkProxy } from './js/proxy/proxycheck' 9 | import { scrapeProxy } from './js/proxy/proxyscrape' 10 | import { 11 | salt, 12 | delay, 13 | genName, 14 | botMode, 15 | sendEvent, 16 | proxyEvent, 17 | notify, 18 | cleanText 19 | } from './js/misc/utils' 20 | import { easyMcAuth } from './js/misc/customAuth' 21 | import EventEmitter from 'node:events' 22 | const Store = require('electron-store') 23 | const mineflayer = require('mineflayer') 24 | import { antiafk } from './js/misc/antiafk' 25 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' 26 | 27 | const botApi = new EventEmitter() 28 | botApi.setMaxListeners(0) 29 | const store = new Store() 30 | 31 | let stopBot = false 32 | let stopScript = false 33 | let stopProxyTest = false 34 | let currentProxy = 0 35 | let proxyUsed = 0 36 | 37 | function storeinfo() { 38 | return store.get('config') 39 | } 40 | 41 | let clientVersion = 3.1 42 | 43 | let playerList = [] 44 | 45 | function createMainWindow() { 46 | const mainWindow = new BrowserWindow({ 47 | width: 1000, 48 | height: 500, 49 | show: false, 50 | autoHideMenuBar: true, 51 | frame: false, 52 | resizable: is.dev, 53 | maximizable: is.dev, 54 | webPreferences: { 55 | devTools: is.dev, 56 | preload: join(__dirname, '../preload/index.js'), 57 | sandbox: false 58 | } 59 | }) 60 | 61 | ipcMain.on('loaded', () => { 62 | store.set('version', { 63 | current: clientVersion 64 | }) 65 | mainWindow.webContents.send('setConfig', store.get('config'), store.get('version')) 66 | if (!storeinfo()) { 67 | mainWindow.webContents.send('initConfig') 68 | } 69 | if (store.get('config.namefile')) { 70 | mainWindow.webContents.send('fileSelected', 'nameFileLabel', store.get('config.namefile')) 71 | } 72 | mainWindow.show() 73 | }) 74 | 75 | ipcMain.on('playerList', (event, list) => { 76 | playerList = list 77 | }) 78 | 79 | ipcMain.on('open', (event, id, name) => { 80 | dialog 81 | .showOpenDialog(mainWindow, { 82 | title: name, 83 | filters: [{ name: 'Text File', extensions: ['txt'] }], 84 | properties: ['openFile', 'multiSelections'] 85 | }) 86 | .then((result) => { 87 | if (!result.canceled) { 88 | store.set('config.namefile', result.filePaths[0]) 89 | mainWindow.webContents.send('fileSelected', id, result.filePaths[0]) 90 | } 91 | }) 92 | .catch((error) => { 93 | console.log(error) 94 | return 95 | }) 96 | }) 97 | 98 | mainWindow.webContents.setWindowOpenHandler((details) => { 99 | shell.openExternal(details.url) 100 | return { action: 'deny' } 101 | }) 102 | 103 | if (is.dev && process.env['ELECTRON_RENDERER_URL']) { 104 | mainWindow.loadURL(`${process.env['ELECTRON_RENDERER_URL']}/index.html`) 105 | } else { 106 | mainWindow.loadFile(join(__dirname, '../renderer/index.html')) 107 | } 108 | } 109 | 110 | app.whenReady().then(() => { 111 | electronApp.setAppUserModelId('com.rattleshyper.trafficermc') 112 | 113 | app.on('browser-window-created', (_, window) => { 114 | optimizer.watchWindowShortcuts(window) 115 | optimizer.registerFramelessWindowIpc(window) 116 | }) 117 | 118 | createMainWindow() 119 | }) 120 | 121 | ipcMain.on('setConfig', (event, type, id, value) => { 122 | store.set(`config.${type}.${id}`, value) 123 | }) 124 | 125 | ipcMain.on('deleteConfig', () => { 126 | store.delete('config') 127 | }) 128 | 129 | ipcMain.on('checkboxClick', (event, id, state) => { 130 | switch (id) { 131 | case 'test': 132 | console.log(state) 133 | break 134 | default: 135 | } 136 | }) 137 | 138 | ipcMain.on('btnClick', (event, btn) => { 139 | switch (btn) { 140 | case 'btnStart': 141 | connectBot() 142 | break 143 | case 'btnStop': 144 | stopBot = true 145 | notify('Info', 'Stopped sending bots.', 'success') 146 | break 147 | case 'btnChat': 148 | exeAll('chat ' + storeinfo().value.chatMsg) 149 | break 150 | case 'btnDisconnect': 151 | exeAll('disconnect') 152 | break 153 | case 'btnSetHotbar': 154 | exeAll('sethotbar ' + storeinfo().value.hotbarSlot) 155 | break 156 | case 'btnUseheld': 157 | exeAll('useheld') 158 | break 159 | case 'btnWinClickRight': 160 | exeAll('winclick ' + storeinfo().value.invSlot + ' 1') 161 | break 162 | case 'btnWinClickLeft': 163 | exeAll('winclick ' + storeinfo().value.invSlot + ' 0') 164 | break 165 | case 'btnDropSlot': 166 | exeAll('drop ' + storeinfo().value.invSlot) 167 | break 168 | case 'btnDropAll': 169 | exeAll('dropall') 170 | break 171 | case 'btnCloseWindow': 172 | exeAll('closewindow') 173 | break 174 | case 'btnStartMove': 175 | exeAll('startmove ' + storeinfo().value.moveType) 176 | break 177 | case 'btnStopMove': 178 | exeAll('stopmove ' + storeinfo().value.moveType) 179 | break 180 | case 'btnResetMove': 181 | exeAll('resetmove') 182 | break 183 | case 'btnLook': 184 | exeAll('look ' + storeinfo().value.lookDirection) 185 | break 186 | case 'btnAfkOn': 187 | exeAll('afkon') 188 | break 189 | case 'btnAfkOff': 190 | exeAll('afkoff') 191 | break 192 | case 'runScript': 193 | playerList.forEach((username) => { 194 | startScript(username) 195 | }) 196 | break 197 | case 'stopScript': 198 | stopScript = true 199 | break 200 | case 'proxyTestStart': 201 | testProxy(storeinfo().value.proxyList) 202 | break 203 | case 'proxyTestStop': 204 | stopProxyTest = true 205 | proxyEvent('', 'stop', '', '') 206 | break 207 | case 'proxyScrape': 208 | if (storeinfo().value.proxyType === 'none') 209 | return notify('Error', 'Select proxy type', 'error') 210 | notify('Info', 'Scraping proxies...', 'success') 211 | setProxy() 212 | break 213 | default: 214 | break 215 | } 216 | }) 217 | 218 | function setProxy() { 219 | scrapeProxy(storeinfo().value.proxyType) 220 | .then((result) => { 221 | proxyEvent('', 'scraped', result, '') 222 | }) 223 | .catch((err) => { 224 | console.log(err) 225 | notify('Error', 'Failed to scrape proxies', 'error') 226 | }) 227 | } 228 | 229 | async function testProxy(list) { 230 | stopProxyTest = false 231 | const server = storeinfo().value.server 232 | const [serverHost, serverPort] = server.split(':') 233 | if (!serverHost) return notify('Error', 'Invalid server address', 'error') 234 | if (!list) return notify('Error', 'Please enter proxy list', 'error') 235 | if (storeinfo().value.proxyType === 'none') return notify('Error', 'Select proxy type', 'error') 236 | notify('Info', 'Testing proxies...', 'success') 237 | proxyEvent('', 'start', '', '') 238 | const lines = list.split(/\r?\n/) 239 | for (let i = 0; i < lines.length; i++) { 240 | if (stopProxyTest) break 241 | const count = `${i + 1}/${lines.length}` 242 | const [host, port, username, password] = lines[i].split(':') 243 | checkProxy( 244 | storeinfo().value.proxyType, 245 | host, 246 | port, 247 | username, 248 | password, 249 | serverHost, 250 | serverPort || 25565, 251 | storeinfo().value.proxyCheckTimeout || 5000 252 | ) 253 | .then((result) => { 254 | proxyEvent(result.proxy, 'success', '', count) 255 | }) 256 | .catch((error) => { 257 | proxyEvent(error.proxy, 'fail', error.reason, count) 258 | }) 259 | if (lines.length == i + 1) { 260 | proxyEvent('', 'stop', '', '') 261 | } 262 | await delay(storeinfo().value.proxyCheckDelay || 100) 263 | } 264 | } 265 | 266 | async function startScript(username) { 267 | stopScript = false 268 | if (!storeinfo().value.scriptText) return 269 | const scriptLines = storeinfo().value.scriptText.split(/\r?\n/) 270 | for (let i = 0; i < scriptLines.length; i++) { 271 | if (stopScript) break 272 | const args = scriptLines[i].split(' ') 273 | const command = args.shift().toLowerCase() 274 | switch (command) { 275 | case 'delay': 276 | await delay(parseInt(args[0])) 277 | break 278 | default: 279 | botApi.emit('botEvent', username, command, args.slice(0)) 280 | } 281 | } 282 | } 283 | 284 | async function exeAll(command) { 285 | if (!command) return 286 | const list = playerList 287 | const cmd = command.split(' ') 288 | if (list.length == 0) return notify('Error', 'No bots selected', 'error') 289 | for (let i = 0; i < list.length; i++) { 290 | botApi.emit('botEvent', list[i], cmd[0], cmd.slice(1)) 291 | if (storeinfo().boolean.isLinear) { 292 | await delay(storeinfo().value.linearDelay || 100) 293 | } 294 | } 295 | sendEvent('Executed', 'chat', 'Script: ' + command) 296 | } 297 | 298 | async function startFile() { 299 | BrowserWindow.getAllWindows()[0].webContents.send('showBottab') 300 | const filePath = storeinfo().namefile 301 | const lines = fs.readFileSync(filePath, 'utf-8').split(/\r?\n/) 302 | const count = storeinfo().value.botMax || lines.length 303 | 304 | for (let i = 0; i < count; i++) { 305 | if (stopBot) break 306 | newBot(getBotInfo(lines[i])) 307 | await delay(storeinfo().value.joinDelay || 1000) 308 | } 309 | } 310 | 311 | async function connectBot() { 312 | stopBot = false 313 | currentProxy = 0 314 | proxyUsed = 0 315 | const count = storeinfo().value.botMax || 1 316 | 317 | if (storeinfo().value.nameType === 'file' && storeinfo().namefile) { 318 | BrowserWindow.getAllWindows()[0].webContents.send('showBottab') 319 | } else if (storeinfo().value.nameType !== 'file' && storeinfo().value.nameType !== 'default') { 320 | BrowserWindow.getAllWindows()[0].webContents.send('showBottab') 321 | } 322 | 323 | for (let i = 0; i < count; i++) { 324 | if (stopBot) break 325 | 326 | let botInfo 327 | 328 | switch (storeinfo().value.nameType) { 329 | case 'random': 330 | botInfo = getBotInfo(salt(10)) 331 | break 332 | case 'legit': 333 | botInfo = getBotInfo(genName()) 334 | break 335 | case 'file': 336 | if (!storeinfo().namefile) { 337 | notify('Error', 'Please select name file', 'error') 338 | } else { 339 | startFile() 340 | } 341 | return 342 | default: 343 | if (!storeinfo().value.username) return notify('Error', 'Please insert username', 'error') 344 | const username = 345 | count == 1 ? storeinfo().value.username : storeinfo().value.username + '_' + i 346 | botInfo = getBotInfo(username) 347 | if (i == 0) BrowserWindow.getAllWindows()[0].webContents.send('showBottab') 348 | } 349 | 350 | newBot(botInfo) 351 | await delay(storeinfo().value.joinDelay || 1000) 352 | } 353 | } 354 | 355 | function getBotInfo(botName) { 356 | const server = storeinfo().value.server || 'localhost:25565' 357 | const [serverHost, serverPort] = server.split(':') 358 | const parsedPort = parseInt(serverPort) || 25565 359 | 360 | const options = { 361 | host: serverHost, 362 | port: parsedPort, 363 | username: botName, 364 | version: storeinfo().value.version, 365 | auth: storeinfo().value.authType, 366 | hideErrors: true, 367 | joinMessage: storeinfo().value.joinMessage, 368 | ...botMode(storeinfo().value.botMode), 369 | ...getProxy(storeinfo().value.proxyType) 370 | } 371 | 372 | if (options.auth === 'easymc') { 373 | options.auth = easyMcAuth 374 | options.sessionServer = 'https://sessionserver.easymc.io' 375 | } 376 | 377 | return options 378 | } 379 | 380 | function getProxy(proxyType) { 381 | if (proxyType === 'none' || !storeinfo().value.proxyList) return 382 | 383 | const proxyList = storeinfo().value.proxyList.split(/\r?\n/) 384 | const randomIndex = crypto.randomInt(0, proxyList.length) 385 | 386 | const proxyPerBot = storeinfo().value.proxyPerBot 387 | 388 | if (proxyUsed >= proxyPerBot) { 389 | proxyUsed = 0 390 | currentProxy++ 391 | if (currentProxy >= proxyList.length) { 392 | currentProxy = 0 393 | } 394 | } 395 | 396 | proxyUsed++ 397 | 398 | const index = storeinfo().boolean.randomizeOrder ? randomIndex : currentProxy 399 | const [host, port, username, password] = proxyList[index].split(':') 400 | return { 401 | protocol: proxyType, 402 | proxyHost: host, 403 | proxyPort: port, 404 | proxyUsername: username, 405 | proxyPassword: password 406 | } 407 | } 408 | 409 | function newBot(options) { 410 | let bot 411 | 412 | if (options.auth === 'easymc') { 413 | if (options.easyMcToken?.length !== 20) { 414 | return sendEvent(options.username, 'easymcAuth') 415 | } 416 | options.auth = easyMcAuth 417 | options.sessionServer ||= 'https://sessionserver.easymc.io' 418 | } 419 | 420 | const connectProxy = async (client) => { 421 | try { 422 | const socket = await connection( 423 | storeinfo().value.proxyType, 424 | options.proxyHost, 425 | options.proxyPort, 426 | options.proxyUsername, 427 | options.proxyPassword, 428 | options.host, 429 | options.port 430 | ) 431 | client.setSocket(socket) 432 | client.emit('connect') 433 | } catch (error) { 434 | if (storeinfo().boolean.proxyLogChat) { 435 | sendEvent( 436 | client.username, 437 | 'chat', 438 | options.proxyHost + ':' + options.proxyPort + ' ' + error 439 | ) 440 | } 441 | return 442 | } 443 | } 444 | 445 | if (storeinfo().value.proxyType !== 'none') { 446 | options.connect = connectProxy 447 | } 448 | 449 | bot = mineflayer.createBot({ 450 | ...options, 451 | plugins: { 452 | anvil: false, 453 | book: false, 454 | boss_bar: false, 455 | breath: false, 456 | chest: false, 457 | command_block: false, 458 | craft: false, 459 | creative: false, 460 | enchantment_table: false, 461 | experience: false, 462 | explosion: false, 463 | fishing: false, 464 | furnace: false, 465 | generic_place: false, 466 | painting: false, 467 | particle: false, 468 | place_block: false, 469 | place_entity: false, 470 | rain: false, 471 | ray_trace: false, 472 | scoreboard: false, 473 | sound: false, 474 | spawn_point: false, 475 | tablist: false, 476 | team: false, 477 | time: false, 478 | title: false, 479 | villager: false 480 | }, 481 | onMsaCode: (data) => { 482 | sendEvent(options.username, 'authmsg', data.user_code) 483 | } 484 | }) 485 | 486 | let hitTimer = 0 487 | 488 | bot.once('login', () => { 489 | sendEvent(bot._client.username, 'login') 490 | if (storeinfo().boolean.runOnConnect) { 491 | startScript(bot._client.username) 492 | } 493 | if (storeinfo().value.joinMessage) { 494 | bot.chat(storeinfo().value.joinMessage) 495 | } 496 | }) 497 | bot.once('spawn', () => { 498 | bot.loadPlugin(antiafk) 499 | }) 500 | bot.on('spawn', () => { 501 | if (storeinfo().boolean.runOnSpawn) { 502 | startScript(bot._client.username) 503 | } 504 | }) 505 | bot.on('messagestr', (msg) => { 506 | sendEvent(bot._client.username, 'chat', msg) 507 | }) 508 | bot.on('windowOpen', (window) => { 509 | sendEvent( 510 | bot._client.username, 511 | 'chat', 512 | `Window Opened ' ${window.title ? ':' + window.title : ''}` 513 | ) 514 | }) 515 | bot.on('windowClose', (window) => { 516 | sendEvent( 517 | bot._client.username, 518 | 'chat', 519 | `Window Closed ' ${window.title ? ':' + window.title : ''}` 520 | ) 521 | }) 522 | bot.once('kicked', (reason) => { 523 | const parsed = JSON.parse(reason) 524 | sendEvent(bot._client.username, 'kicked', cleanText(parsed)) 525 | }) 526 | bot.once('end', (reason) => { 527 | sendEvent(bot._client.username, 'end', reason) 528 | if (storeinfo().boolean.autoReconnect) { 529 | setTimeout(() => { 530 | newBot(options) 531 | }, storeinfo().value.reconnectDelay || 1000) 532 | } 533 | }) 534 | 535 | bot.on('physicTick', () => { 536 | if (storeinfo().boolean.killauraToggle && playerList.includes(bot._client.username)) { 537 | killaura() 538 | } 539 | }) 540 | 541 | function killaura() { 542 | if (hitTimer <= 0) { 543 | hit( 544 | storeinfo().boolean.targetPlayer, 545 | storeinfo().boolean.targetVehicle, 546 | storeinfo().boolean.targetMob, 547 | storeinfo().boolean.targetAnimal, 548 | storeinfo().value.killauraRange, 549 | storeinfo().boolean.killauraRotate 550 | ) 551 | hitTimer = storeinfo().value.killauraDelay || 10 552 | } else { 553 | hitTimer-- 554 | } 555 | } 556 | 557 | function hit(player, vehicle, mob, animal, maxDistance, rotate) { 558 | let targetEntities = [] 559 | const entities = Object.values(bot.entities) 560 | entities.forEach((entity) => { 561 | const distance = bot.entity.position.distanceTo(entity.position) 562 | if (distance >= parseFloat(maxDistance)) return 563 | if (entity.type === 'player' && entity.username !== bot.username && player) { 564 | targetEntities.push(entity) 565 | } 566 | if (entity.kind === 'Vehicles' && vehicle) { 567 | targetEntities.push(entity) 568 | } 569 | if (entity.kind === 'Hostile mobs' && mob) { 570 | targetEntities.push(entity) 571 | } 572 | if (entity.kind === 'Passive mobs' && animal) { 573 | targetEntities.push(entity) 574 | } 575 | }) 576 | targetEntities.forEach((entity) => { 577 | if (rotate) { 578 | bot.lookAt(entity.position, true) 579 | bot.attack(entity) 580 | } else { 581 | bot.attack(entity) 582 | } 583 | }) 584 | } 585 | 586 | botApi.on('botEvent', (target, event, ...options) => { 587 | if (target !== bot._client.username) return 588 | const optionsArray = options[0] 589 | switch (event) { 590 | case 'disconnect': 591 | bot.quit() 592 | break 593 | case 'chat': 594 | const bypass = storeinfo().boolean.bypassChat ? ' ' + salt(crypto.randomInt(2, 6)) : '' 595 | bot.chat( 596 | optionsArray 597 | .join(' ') 598 | .replaceAll('{random}', salt(4)) 599 | .replaceAll('{player}', bot._client.username) + bypass 600 | ) 601 | break 602 | case 'notify': 603 | notify( 604 | 'Bot', 605 | bot._client.username + 606 | ': ' + 607 | optionsArray 608 | .join(' ') 609 | .replaceAll('{random}', salt(4)) 610 | .replaceAll('{player}', bot._client.username), 611 | 'success' 612 | ) 613 | break 614 | case 'sethotbar': 615 | bot.setQuickBarSlot(parseInt(optionsArray[0] ? optionsArray[0] : 0)) 616 | break 617 | case 'useheld': 618 | bot.activateItem() 619 | break 620 | case 'winclick': 621 | bot.clickWindow(parseInt(optionsArray[0]), parseInt(optionsArray[1]), 0) 622 | break 623 | case 'drop': 624 | bot.clickWindow(-999, 0, 0) 625 | bot.clickWindow(parseInt(optionsArray[0]), 0, 0) 626 | bot.clickWindow(-999, 0, 0) 627 | break 628 | case 'dropall': 629 | ;(async () => { 630 | const itemCount = bot.inventory.items().length 631 | for (var i = 0; i < itemCount; i++) { 632 | if (bot.inventory.items().length === 0) return 633 | const item = bot.inventory.items()[0] 634 | bot.tossStack(item) 635 | await delay(10) 636 | } 637 | })() 638 | break 639 | case 'closewindow': 640 | bot.closeWindow(bot.currentWindow || '') 641 | break 642 | case 'startmove': 643 | bot.setControlState(optionsArray[0], true) 644 | break 645 | case 'stopmove': 646 | bot.setControlState(optionsArray[0], false) 647 | break 648 | case 'resetmove': 649 | bot.clearControlStates() 650 | break 651 | case 'look': 652 | bot.look(parseFloat(optionsArray[0]), 0, true) 653 | break 654 | case 'afkon': 655 | bot.afk.start() 656 | break 657 | case 'afkoff': 658 | bot.afk.stop() 659 | break 660 | case 'hit': 661 | const player = optionsArray[0] 662 | const vehicle = optionsArray[1] 663 | const mob = optionsArray[2] 664 | const animal = optionsArray[3] 665 | const maxDistance = parseFloat(optionsArray[4]) 666 | const rotate = optionsArray[5] 667 | hit(player, vehicle, mob, animal, maxDistance, rotate) 668 | break 669 | default: 670 | } 671 | }) 672 | } 673 | 674 | process.on('uncaughtException', (err) => { 675 | console.log(err) 676 | }) 677 | process.on('UnhandledPromiseRejectionWarning', (err) => { 678 | console.log(err) 679 | }) 680 | -------------------------------------------------------------------------------- /src/main/js/misc/antiafk.js: -------------------------------------------------------------------------------- 1 | async function rotate(bot) { 2 | let yaw = 2 * Math.random() * Math.PI - 0.5 * Math.PI 3 | let pitch = Math.random() * Math.PI - 0.5 * Math.PI 4 | await bot.look(yaw, pitch, false) 5 | } 6 | 7 | function jump(bot) { 8 | return new Promise((resolve) => { 9 | bot.setControlState('jump', true) 10 | if (!bot.entity.isInWater) bot.setControlState('jump', false) 11 | setTimeout(resolve, 1000) 12 | }) 13 | } 14 | async function swingArm(bot) { 15 | let arm = Math.random() < 0.5 ? 'right' : 'left' 16 | await bot.swingArm(arm) 17 | } 18 | async function start(bot) { 19 | if (bot.afk.stopping) { 20 | bot.afk.stopped = true 21 | return 22 | } 23 | if (bot._client.state != 'play') { 24 | bot.once('spawn', () => start(bot)) 25 | return 26 | } 27 | if (bot.entity.isInWater) bot.setControlState('jump', true) 28 | await bot.afk[bot.afk.config.actions[Math.floor(Math.random() * 3)]]() 29 | start(bot) 30 | } 31 | 32 | function setOptions(bot) { 33 | let config = bot.afk.config 34 | config.actions = ['rotate', 'jump', 'swingArm'] 35 | } 36 | 37 | function stop(bot) { 38 | bot.afk.stopping = true 39 | return new Promise((resolve) => { 40 | if (!bot.afk.enabled) resolve('nothing to stop') 41 | setInterval(() => { 42 | if (bot.afk.stopped) { 43 | bot.afk.stopping = null 44 | bot.afk.stopped = null 45 | bot.afk.enabled = false 46 | resolve('stopped successfully') 47 | } 48 | }, 500) 49 | }) 50 | } 51 | 52 | export function antiafk(bot) { 53 | bot.afk = { 54 | config: {}, 55 | enabled: false, 56 | start: async () => { 57 | bot.afk.enabled = true 58 | await start(bot) 59 | }, 60 | stop: async () => await stop(bot), 61 | setOptions: (opt) => 62 | setOptions(bot, { 63 | ...opt 64 | }), 65 | rotate: async () => await rotate(bot), 66 | jump: async () => await jump(bot), 67 | swingArm: async () => await swingArm(bot) 68 | } 69 | bot.afk.setOptions() 70 | } 71 | -------------------------------------------------------------------------------- /src/main/js/misc/customAuth.js: -------------------------------------------------------------------------------- 1 | import { sendEvent } from './utils' 2 | 3 | export async function easyMcAuth(client, options) { 4 | if (options.username?.length !== 20) return sendEvent(options.username, 'easymcAuth') 5 | const fetchOptions = { 6 | method: 'POST', 7 | headers: { 'Content-Type': 'application/json' }, 8 | body: `{"token":"${options.username}"}` 9 | } 10 | try { 11 | const res = await fetch('https://api.easymc.io/v1/token/redeem', fetchOptions) 12 | const resJson = await res.json() 13 | if (resJson.error) return sendEvent('EasyMC', 'chat', `${resJson.error}`) 14 | if (!resJson) return sendEvent('EasyMC', 'chat', 'Empty response from EasyMC.') 15 | if (resJson.session?.length !== 43 || resJson.mcName?.length < 3 || resJson.uuid?.length !== 36) 16 | return sendEvent('EasyMC', 'chat', 'Invalid response from EasyMC.') 17 | const session = { 18 | accessToken: resJson.session, 19 | selectedProfile: { 20 | name: resJson.mcName, 21 | id: resJson.uuid 22 | } 23 | } 24 | options.haveCredentials = true 25 | client.session = session 26 | client.username = session.selectedProfile.name 27 | options.accessToken = session.accessToken 28 | client.emit('session', session) 29 | } catch (error) { 30 | return client.emit('error', error.message) 31 | } 32 | options.connect(client) 33 | } 34 | -------------------------------------------------------------------------------- /src/main/js/misc/utils.js: -------------------------------------------------------------------------------- 1 | import { BrowserWindow } from 'electron' 2 | 3 | export function salt(length) { 4 | var result = '' 5 | var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' 6 | for (var i = 0; i < length; i++) { 7 | result += characters.charAt(Math.floor(Math.random() * characters.length)) 8 | } 9 | return result 10 | } 11 | 12 | export function delay(ms) { 13 | return new Promise((res) => setTimeout(res, ms)) 14 | } 15 | 16 | export function botMode(mode) { 17 | switch (mode) { 18 | case 'minimal': 19 | return { 20 | physicsEnabled: false, 21 | viewDistance: 'tiny', 22 | plugins: { 23 | anvil: false, 24 | block_actions: false, 25 | blocks: false, 26 | book: false, 27 | boss_bar: false, 28 | breath: false, 29 | chest: false, 30 | command_block: false, 31 | conversions: false, 32 | craft: false, 33 | creative: false, 34 | digging: false, 35 | enchantment_table: false, 36 | entities: false, 37 | experience: false, 38 | explosion: false, 39 | fishing: false, 40 | furnace: false, 41 | generic_place: false, 42 | health: false, 43 | inventory: false, 44 | loader: false, 45 | painting: false, 46 | particle: false, 47 | place_block: false, 48 | place_entity: false, 49 | physics: false, 50 | rain: false, 51 | ray_trace: false, 52 | resource_pack: false, 53 | scoreboard: false, 54 | simple_inventory: false, 55 | sound: false, 56 | spawn_point: false, 57 | tablist: false, 58 | team: false, 59 | time: false, 60 | title: false, 61 | villager: false 62 | } 63 | } 64 | default: 65 | return 66 | } 67 | } 68 | 69 | export function genName() { 70 | const adjectives = [ 71 | 'Red', 72 | 'Hot', 73 | 'Big', 74 | 'Old', 75 | 'New', 76 | 'Dry', 77 | 'Wet', 78 | 'Tall', 79 | 'Soft', 80 | 'Loud', 81 | 'Cold', 82 | 'Warm', 83 | 'Dark', 84 | 'Fair', 85 | 'Blue', 86 | 'Gray', 87 | 'Rich', 88 | 'Poor', 89 | 'Fast', 90 | 'Slow', 91 | 'Thin', 92 | 'Tiny', 93 | 'Wide', 94 | 'High', 95 | 'Deep', 96 | 'Dear', 97 | 'Neat', 98 | 'Cool', 99 | 'Fine', 100 | 'Lame', 101 | 'Sharp', 102 | 'Dull', 103 | 'Cute', 104 | 'Long', 105 | 'Short', 106 | 'Hard', 107 | 'Mean', 108 | 'Kind', 109 | 'Sick', 110 | 'Weak', 111 | 'Pure', 112 | 'Evil', 113 | 'Bold', 114 | 'Mild', 115 | 'Wild', 116 | 'Mad', 117 | 'Calm', 118 | 'Wise', 119 | 'Dumb', 120 | 'Slim', 121 | 'Thick', 122 | 'Pale', 123 | 'Stale', 124 | 'Ugly' 125 | ] 126 | const nouns = [ 127 | '_', 128 | 'Dog', 129 | 'Cat', 130 | 'Pen', 131 | 'Car', 132 | 'Cup', 133 | 'Hat', 134 | 'Sun', 135 | 'Bed', 136 | 'Box', 137 | 'Key', 138 | 'Arm', 139 | 'Ball', 140 | 'Book', 141 | 'Cake', 142 | 'Duck', 143 | 'Fish', 144 | 'Fork', 145 | 'Hand', 146 | 'Bird', 147 | 'Moon', 148 | 'Star', 149 | 'Tree', 150 | 'Ring', 151 | 'Shoe', 152 | 'Bear', 153 | 'Coat', 154 | 'Flag', 155 | 'Lamp', 156 | 'Leaf', 157 | 'Desk', 158 | 'Nail', 159 | 'Sock', 160 | 'Rose', 161 | 'Boat', 162 | 'Frog', 163 | 'Pipe', 164 | 'Rock', 165 | 'Seal', 166 | 'Boot', 167 | 'Worm', 168 | 'Bat', 169 | 'Bell', 170 | 'Belt', 171 | 'Door', 172 | 'Drum', 173 | 'Gate', 174 | 'Hair', 175 | 'Head', 176 | 'Heart', 177 | 'Kiss', 178 | 'Lady', 179 | 'Lark', 180 | 'Lion', 181 | 'Bowl', 182 | 'Deer', 183 | 'Goat', 184 | 'Nose', 185 | 'Bone', 186 | 'Bull', 187 | 'Food', 188 | 'Gown', 189 | 'Gulf', 190 | 'Horn', 191 | 'Joke', 192 | 'Jute', 193 | 'Milk', 194 | 'Mole', 195 | 'Navy', 196 | 'Pony', 197 | 'Queen', 198 | 'Rope', 199 | 'Ruff', 200 | 'Shin', 201 | 'Tong', 202 | 'Light', 203 | 'Trot', 204 | 'Vase', 205 | 'Wren', 206 | 'Yoke', 207 | 'Zulu' 208 | ] 209 | const adjectivesLength = adjectives.length 210 | const nounsLength = nouns.length 211 | let name = '' 212 | 213 | while (name.length < 6 || name.length + 1 > 16) { 214 | const adjectiveIndex = Math.floor(Math.random() * adjectivesLength) 215 | const nounIndex = Math.floor(Math.random() * nounsLength) 216 | const newName = `${adjectives[adjectiveIndex]}${nouns[nounIndex]}` 217 | 218 | if (newName.length + name.length > 16) { 219 | break 220 | } 221 | 222 | name += newName 223 | 224 | if (Math.random() < 0.15) { 225 | const num = Math.floor(Math.random() * 999 + 1) 226 | if (name.length + num.toString().length > 16) { 227 | break 228 | } 229 | name += num 230 | } 231 | } 232 | 233 | const lowerCaseName = name.toLowerCase() 234 | const hasLowerCase = name !== lowerCaseName 235 | name += hasLowerCase ? lowerCaseName.slice(0, 16 - name.length) : '' 236 | 237 | return name 238 | } 239 | 240 | export function sendEvent(username, event, message) { 241 | const info = { 242 | id: username, 243 | event: event, 244 | message: message 245 | } 246 | BrowserWindow.getAllWindows()[0].webContents.send('botEvent', info) 247 | } 248 | 249 | export function proxyEvent(proxy, event, message, count) { 250 | const info = { 251 | proxy: proxy, 252 | event: event, 253 | message: message, 254 | count: count 255 | } 256 | BrowserWindow.getAllWindows()[0].webContents.send('proxyEvent', info) 257 | } 258 | 259 | export function notify(title, body, type, img, keep) { 260 | BrowserWindow.getAllWindows()[0].webContents.send('notify', title, body, type, img, keep) 261 | } 262 | 263 | export function cleanText(string) { 264 | let texts = [] 265 | 266 | function recurse(obj) { 267 | if (obj.text) { 268 | texts.push(obj.text.replace(/\n/g, ' ')) 269 | } 270 | if (obj.extra) { 271 | for (let item of obj.extra) { 272 | recurse(item) 273 | } 274 | } 275 | } 276 | 277 | recurse(string) 278 | return texts.join('').replaceAll(' ', ' ') 279 | } 280 | -------------------------------------------------------------------------------- /src/main/js/proxy/proxycheck.js: -------------------------------------------------------------------------------- 1 | const mc = require('minecraft-protocol') 2 | import { connection } from './proxyhandler' 3 | import { salt } from '../misc/utils' 4 | 5 | export function checkProxy( 6 | proxyType, 7 | proxyHost, 8 | proxyPort, 9 | proxyUsername, 10 | proxyPassword, 11 | dHost, 12 | dPort, 13 | timeout 14 | ) { 15 | return new Promise((resolve, reject) => { 16 | const bot = mc.createClient({ 17 | host: dHost, 18 | port: parseInt(dPort), 19 | username: salt(10), 20 | auth: 'offline', 21 | connect: async (client) => { 22 | try { 23 | const socket = await connection( 24 | proxyType, 25 | proxyHost, 26 | proxyPort, 27 | proxyUsername, 28 | proxyPassword, 29 | dHost, 30 | dPort 31 | ) 32 | client.setSocket(socket) 33 | client.emit('connect') 34 | } catch (error) { 35 | const info = { 36 | reason: 'bad', 37 | error: error, 38 | proxy: proxyHost + ':' + proxyPort 39 | } 40 | return reject(info) 41 | } 42 | } 43 | }) 44 | 45 | setTimeout(() => { 46 | bot.end() 47 | const info = { 48 | reason: 'timeout', 49 | proxy: proxyHost + ':' + proxyPort 50 | } 51 | return reject(info) 52 | }, timeout) 53 | 54 | bot.on('connect', () => { 55 | bot.end() 56 | const info = { 57 | reason: 'success', 58 | proxy: `${proxyHost}:${proxyPort}${proxyUsername ? `:${proxyUsername}` : ''}${proxyPassword ? `:${proxyPassword}` : ''}` 59 | } 60 | return resolve(info) 61 | }) 62 | 63 | bot.on('error', (error) => { 64 | const info = { 65 | reason: 'bad', 66 | error: error.message, 67 | proxy: proxyHost + ':' + proxyPort 68 | } 69 | return reject(info) 70 | }) 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /src/main/js/proxy/proxyhandler.js: -------------------------------------------------------------------------------- 1 | import { SocksClient } from 'socks' 2 | import { Socket } from 'net' 3 | 4 | export function connection( 5 | proxyType, 6 | proxyHost, 7 | proxyPort, 8 | proxyUsername, 9 | proxyPassword, 10 | dHost, 11 | dPort 12 | ) { 13 | return new Promise((resolve, reject) => { 14 | if (proxyType === 'socks5' || proxyType === 'socks4') { 15 | SocksClient.createConnection( 16 | { 17 | proxy: { 18 | host: proxyHost, 19 | port: parseInt(proxyPort), 20 | userId: proxyUsername, 21 | password: proxyPassword, 22 | type: proxyType === 'socks5' ? 5 : 4 23 | }, 24 | command: 'connect', 25 | destination: { 26 | host: dHost, 27 | port: parseInt(dPort) 28 | } 29 | }, 30 | (err, info) => { 31 | if (err) { 32 | return reject(err.message) 33 | } 34 | resolve(info.socket) 35 | } 36 | ) 37 | } else { 38 | const socket = new Socket().connect({ 39 | host: dHost, 40 | port: parseInt(dPort) 41 | }) 42 | socket.on('connect', () => { 43 | resolve(socket) 44 | }) 45 | socket.on('error', (err) => { 46 | return reject(err.message) 47 | }) 48 | } 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /src/main/js/proxy/proxyscrape.js: -------------------------------------------------------------------------------- 1 | export async function scrapeProxy(proxyType) { 2 | const proxyLink = `https://raw.githubusercontent.com/RattlesHyper/proxy/main/${proxyType}` 3 | const proxySources = await fetchList(proxyLink) 4 | const promises = proxySources.map(fetchList) 5 | const proxyLists = await Promise.all(promises) 6 | let list = '' 7 | for (const proxies of proxyLists) { 8 | list += proxies.join('\n') 9 | } 10 | return list 11 | } 12 | 13 | async function fetchList(link) { 14 | const res = await fetch(link, { cache: 'force-cache' }) 15 | const text = await res.text() 16 | const result = [] 17 | let start = 0 18 | let i = 0 19 | while (i !== -1) { 20 | i = text.indexOf('\n', start) 21 | if (i === -1) { 22 | if (start < text.length) result.push(text.slice(start)) 23 | } else { 24 | result.push(text.slice(start, i)) 25 | start = i + 1 26 | } 27 | } 28 | return result 29 | } 30 | -------------------------------------------------------------------------------- /src/preload/index.js: -------------------------------------------------------------------------------- 1 | import { contextBridge } from 'electron' 2 | import { electronAPI } from '@electron-toolkit/preload' 3 | 4 | const api = {} 5 | if (process.contextIsolated) { 6 | try { 7 | contextBridge.exposeInMainWorld('electron', electronAPI) 8 | contextBridge.exposeInMainWorld('api', api) 9 | } catch (error) { 10 | console.error(error) 11 | } 12 | } else { 13 | window.electron = electronAPI 14 | window.api = api 15 | } 16 | -------------------------------------------------------------------------------- /src/renderer/assets/css/base.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ev-c-white: #ffffff; 3 | --ev-c-white-soft: #f8f8f8; 4 | --ev-c-white-mute: #f2f2f2; 5 | 6 | --ev-c-black: rgb(27, 27, 31); 7 | --ev-c-black-soft: rgb(34, 34, 34); 8 | --ev-c-black-mute: rgb(40, 40, 40); 9 | 10 | --ev-c-gray-1: #515c67; 11 | --ev-c-gray-2: #414853; 12 | --ev-c-gray-3: #32363f; 13 | 14 | --ev-c-blue-1: #50928f; 15 | --ev-c-blue-2: #3e7f88; 16 | 17 | --ev-c-success: #f8f8f8; 18 | --ev-c-warning: #ffaa00; 19 | --ev-c-error: #ff5555; 20 | 21 | --ev-c-text-1: rgba(255, 255, 245, 0.86); 22 | --ev-c-text-2: rgba(235, 235, 245, 0.6); 23 | --ev-c-text-3: rgba(235, 235, 245, 0.38); 24 | 25 | --ev-button-alt-border: transparent; 26 | --ev-button-alt-text: var(--ev-c-text-1); 27 | --ev-button-alt-bg: var(--ev-c-gray-3); 28 | --ev-button-alt-hover-border: transparent; 29 | --ev-button-alt-hover-text: var(--ev-c-text-1); 30 | --ev-button-alt-hover-bg: var(--ev-c-gray-2); 31 | } 32 | 33 | :root { 34 | --color-background: var(--ev-c-black); 35 | --color-background-soft: var(--ev-c-black-soft); 36 | --color-background-mute: var(--ev-c-black-mute); 37 | --color-text: var(--ev-c-text-1); 38 | } 39 | 40 | *, 41 | *::before, 42 | *::after { 43 | box-sizing: border-box; 44 | margin: 0; 45 | font-weight: normal; 46 | } 47 | 48 | ul { 49 | list-style: none; 50 | } 51 | 52 | body { 53 | min-height: 100vh; 54 | color: var(--color-text); 55 | background: var(--color-background); 56 | line-height: 1.6; 57 | font-family: 58 | Inter, 59 | -apple-system, 60 | BlinkMacSystemFont, 61 | 'Segoe UI', 62 | Roboto, 63 | Oxygen, 64 | Ubuntu, 65 | Cantarell, 66 | 'Fira Sans', 67 | 'Droid Sans', 68 | 'Helvetica Neue', 69 | sans-serif; 70 | text-rendering: optimizeLegibility; 71 | -webkit-font-smoothing: antialiased; 72 | -moz-osx-font-smoothing: grayscale; 73 | } 74 | -------------------------------------------------------------------------------- /src/renderer/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | 3 | body { 4 | display: flex; 5 | overflow: hidden; 6 | background-image: url('../icons/background.svg'); 7 | background-size: cover; 8 | user-select: none; 9 | } 10 | 11 | code { 12 | font-weight: 600; 13 | padding: 3px 5px; 14 | border-radius: 2px; 15 | background-color: var(--color-background-mute); 16 | font-family: 17 | ui-monospace, 18 | SFMono-Regular, 19 | SF Mono, 20 | Menlo, 21 | Consolas, 22 | Liberation Mono, 23 | monospace; 24 | font-size: 85%; 25 | } 26 | 27 | .disabled { 28 | color: var(--ev-c-text-3); 29 | } 30 | 31 | .content { 32 | width: 725px; 33 | height: 400px; 34 | } 35 | 36 | .topbar { 37 | width: 725px; 38 | height: 30px; 39 | background-color: rgba(40, 40, 40, 0.8); 40 | display: flex; 41 | justify-content: space-between; 42 | align-items: center; 43 | padding: 0 20px; 44 | } 45 | 46 | .sidebar { 47 | position: fixed; 48 | top: 30px; 49 | left: 0px; 50 | height: 440px; 51 | width: 110px; 52 | background-color: rgba(40, 40, 40, 0.8); 53 | 54 | list-style-type: none; 55 | } 56 | 57 | .sidebar li { 58 | cursor: pointer; 59 | position: relative; 60 | left: 15px; 61 | font-size: 20px; 62 | font-weight: 700; 63 | } 64 | 65 | .sidebar li:hover { 66 | color: var(--ev-c-text-2); 67 | } 68 | 69 | .sidebar li.selected { 70 | left: 2px; 71 | padding-left: 10px; 72 | border-left: #e8eaed 3px solid; 73 | } 74 | 75 | .notification { 76 | position: absolute; 77 | max-height: 440px; 78 | bottom: 35px; 79 | right: 10px; 80 | display: flex; 81 | flex-direction: column-reverse; 82 | overflow: hidden; 83 | } 84 | 85 | .n-message { 86 | word-wrap: break-word; 87 | } 88 | 89 | .n-progress { 90 | position: relative; 91 | top: 10px; 92 | right: 10px; 93 | height: 4px; 94 | background: var(--ev-c-white-soft); 95 | animation: toastProgress 3s ease-in-out forwards; 96 | } 97 | 98 | .notification li { 99 | overflow: hidden; 100 | margin-top: 5px; 101 | width: 300px; 102 | max-width: 300px; 103 | padding: 0.5rem; 104 | border-radius: 4px; 105 | background: var(--color-background-mute); 106 | animation: slideInRight 0.3s ease-in-out forwards; 107 | opacity: 0.9; 108 | } 109 | 110 | .notification li:hover { 111 | opacity: 1; 112 | } 113 | 114 | .notification .fade { 115 | animation: fadeOut 0.3s ease-in-out forwards; 116 | } 117 | 118 | .notification .success { 119 | border-left: solid 1px var(--ev-c-success); 120 | } 121 | .notification .warning { 122 | border-left: solid 1px var(--ev-c-warning); 123 | } 124 | .notification .error { 125 | border-left: solid 1px var(--ev-c-error); 126 | } 127 | 128 | .control-tab { 129 | text-align: center; 130 | } 131 | 132 | .controlArea { 133 | position: fixed; 134 | top: 30px; 135 | left: 110px; 136 | height: 440px; 137 | width: 890px; 138 | } 139 | 140 | .containerCheckbox { 141 | text-align: start; 142 | display: block; 143 | position: relative; 144 | padding-left: 30px; 145 | cursor: pointer; 146 | font-weight: 600; 147 | } 148 | 149 | .containerCheckbox input { 150 | opacity: 0; 151 | height: 0; 152 | width: 0; 153 | } 154 | 155 | .checkmark { 156 | position: absolute; 157 | top: 0; 158 | left: 0; 159 | height: 25px; 160 | width: 25px; 161 | background-color: transparent; 162 | background-color: transparent; 163 | border: solid 1px var(--ev-c-gray-1); 164 | } 165 | 166 | .containerCheckbox:hover input~.checkmark { 167 | background-color: var(--ev-c-gray-2); 168 | } 169 | 170 | .containerCheckbox input:checked~.checkmark { 171 | background-color: var(--ev-c-gray-1); 172 | } 173 | 174 | .checkmark:after { 175 | content: ""; 176 | position: absolute; 177 | display: none; 178 | } 179 | 180 | .containerCheckbox input:checked~.checkmark:after { 181 | display: block; 182 | } 183 | 184 | .containerCheckbox .checkmark:after { 185 | left: 9px; 186 | top: 6px; 187 | width: 6px; 188 | height: 10px; 189 | border: solid var(--ev-c-white); 190 | border-width: 0 3px 3px 0; 191 | -webkit-transform: rotate(45deg); 192 | -ms-transform: rotate(45deg); 193 | transform: rotate(45deg); 194 | } 195 | 196 | .slidecontainer { 197 | width: 100%; 198 | } 199 | 200 | .slider { 201 | appearance: none; 202 | position: relative; 203 | left: 5px; 204 | top: 5px; 205 | height: 10px; 206 | background: var(--ev-c-gray-2); 207 | border-radius: 4px; 208 | } 209 | 210 | .slider:hover { 211 | background: var(--ev-c-gray-1); 212 | } 213 | 214 | .slider::-webkit-slider-thumb { 215 | -webkit-appearance: none; 216 | appearance: none; 217 | width: 10px; 218 | height: 15px; 219 | border-radius: 2px; 220 | background: var(--ev-c-white); 221 | cursor: pointer; 222 | } 223 | 224 | ::-webkit-scrollbar { 225 | width: 5px; 226 | height: 0px; 227 | } 228 | 229 | ::-webkit-scrollbar-thumb { 230 | background-color: var(--ev-c-gray-1); 231 | } 232 | 233 | ::-webkit-scrollbar-thumb:hover { 234 | background-color: var(--ev-c-gray-2); 235 | } 236 | 237 | .downbar { 238 | background-color: #1b3c6e; 239 | } 240 | 241 | .area { 242 | display: flex; 243 | justify-content: space-between; 244 | padding: 10px 20px; 245 | } 246 | 247 | .textinput { 248 | background-color: rgba(40, 40, 40, 0.8); 249 | border: none; 250 | color: #e8eaed; 251 | word-wrap: break-word; 252 | outline: none; 253 | display: flex; 254 | padding: 10px; 255 | margin-top: 10px; 256 | font-size: 14px; 257 | border-bottom: 1px solid #B0B3B9; 258 | -webkit-tap-highlight-color: transparent; 259 | } 260 | 261 | .textbox { 262 | resize: none; 263 | color: var(--ev-c-text-1); 264 | font-weight: 600; 265 | padding: 3px 5px; 266 | border-radius: 2px; 267 | background-color: var(--color-background-mute); 268 | font-family: 269 | ui-monospace, 270 | SFMono-Regular, 271 | SF Mono, 272 | Menlo, 273 | Consolas, 274 | Liberation Mono, 275 | monospace; 276 | font-size: 85%; 277 | opacity: 0.9; 278 | } 279 | 280 | input { 281 | width: 165px; 282 | color: var(--ev-c-white); 283 | background-color: transparent; 284 | border: none; 285 | 286 | font-size: 14px; 287 | color: var(--ev-c-text-1); 288 | font-weight: 600; 289 | } 290 | 291 | input[type=number] { 292 | border-bottom: solid 1px var(--ev-c-gray-1); 293 | } 294 | 295 | input[type=text] { 296 | border-bottom: solid 1px var(--ev-c-gray-1); 297 | } 298 | 299 | input::placeholder { 300 | color: var(--ev-c-gray-1); 301 | } 302 | 303 | input[type=number]::-webkit-inner-spin-button, 304 | input[type=number]::-webkit-outer-spin-button { 305 | -webkit-appearance: none; 306 | margin: 0; 307 | } 308 | 309 | input[type=file]::-webkit-file-upload-button { 310 | display: none; 311 | } 312 | 313 | input[type=file]::before { 314 | content: 'Select files'; 315 | display: inline-block; 316 | border: 1px solid #999; 317 | border-radius: 3px; 318 | padding: 2px 5px; 319 | margin: 5px; 320 | cursor: pointer; 321 | } 322 | 323 | .input-file { 324 | border: solid 1px var(--ev-c-gray-1); 325 | color: var(--ev-c-text-1); 326 | font-size: 14px; 327 | font-weight: 600; 328 | padding: 0px 5px 0px 5px; 329 | border-radius: 3px; 330 | background-color: transparent; 331 | cursor: pointer; 332 | } 333 | 334 | *:focus { 335 | outline: none; 336 | } 337 | 338 | ul { 339 | padding: 0px; 340 | } 341 | 342 | .settings-tab { 343 | position: fixed; 344 | left: 0; 345 | top: 30px; 346 | bottom: 30px; 347 | width: 100%; 348 | overflow: auto; 349 | background-color: rgba(0, 0, 0, 0.4); 350 | display: none; 351 | } 352 | 353 | .settings-tab-content { 354 | position: fixed; 355 | background-color: var(--color-background-mute); 356 | margin-top: 50px; 357 | margin-left: 10%; 358 | padding: 20px; 359 | border: 1px solid var(--ev-c-gray-2); 360 | overflow: scroll; 361 | width: 80%; 362 | height: 350px; 363 | } 364 | 365 | #chatBox { 366 | width: 340px; 367 | height: 380px; 368 | overflow: scroll; 369 | } 370 | 371 | #chatBox li { 372 | padding: 5px 10px; 373 | word-wrap: break-word; 374 | border: solid 1px var(--ev-c-gray-2); 375 | animation: fadeIn 0.3s; 376 | } 377 | 378 | #proxyLogbox { 379 | height: 122px; 380 | overflow: scroll; 381 | } 382 | 383 | #proxyLogbox li { 384 | padding: 5px 10px; 385 | word-wrap: break-word; 386 | border: solid 1px var(--ev-c-gray-2); 387 | } 388 | 389 | #proxyLogbox .success { 390 | background: -webkit-linear-gradient(180deg, #2a5038 5%, #2d535100); 391 | } 392 | 393 | #proxyLogbox .timeout { 394 | background: -webkit-linear-gradient(180deg, #4e572e 5%, #2d535100); 395 | } 396 | 397 | #proxyLogbox .fail { 398 | background: -webkit-linear-gradient(180deg, #572d2b 5%, #2d535100); 399 | } 400 | 401 | #controlList { 402 | height: 220px; 403 | overflow: scroll; 404 | } 405 | 406 | #controlList li { 407 | text-align: center; 408 | font-size: 14px; 409 | cursor: pointer; 410 | font-weight: 600; 411 | position: relative; 412 | left: 1px; 413 | padding: 2px 0px 2px 0px; 414 | word-wrap: break-word; 415 | overflow-wrap: break-word; 416 | border: 1px solid var(--ev-c-gray-1); 417 | } 418 | 419 | #controlList li.selected { 420 | position: relative; 421 | left: -1px; 422 | border-right: none; 423 | border-left: #e8eaed 6px solid; 424 | } 425 | 426 | #controlList li:hover { 427 | background-color: var(--ev-c-gray-2); 428 | } 429 | 430 | #botList { 431 | width: 200px; 432 | overflow: scroll; 433 | list-style-type: none; 434 | } 435 | 436 | #botList li { 437 | font-weight: 600; 438 | padding: 0px 0px 3px 10px; 439 | word-wrap: break-word; 440 | overflow-wrap: break-word; 441 | } 442 | 443 | #botList li:hover { 444 | background-color: var(--ev-c-gray-2); 445 | } 446 | 447 | #botList .selected { 448 | border-right: solid 2px var(--ev-c-text-2); 449 | border-left: solid 2px var(--ev-c-text-2); 450 | background: var(--ev-c-gray-1); 451 | } 452 | 453 | #botList .selected:hover { 454 | border-right: solid 2px var(--ev-c-text-2); 455 | border-left: solid 2px var(--ev-c-text-2); 456 | background: var(--ev-c-gray-2); 457 | } 458 | 459 | 460 | select { 461 | background-color: transparent; 462 | border: none; 463 | font-size: 14px; 464 | color: var(--ev-c-text-1); 465 | font-weight: 600; 466 | padding: 0px 0px 0px 10px; 467 | background-color: transparent; 468 | cursor: pointer; 469 | } 470 | 471 | select option { 472 | background: var(--color-background-mute); 473 | font-size: 14px; 474 | color: var(--ev-c-text-1); 475 | font-weight: 600; 476 | } 477 | 478 | button { 479 | background: var(--ev-c-gray-3); 480 | color: var(--ev-c-text-1); 481 | font-size: 14px; 482 | font-weight: 600; 483 | padding: 5px 30px; 484 | cursor: pointer; 485 | border: solid 1px var(--ev-c-gray-2); 486 | opacity: 0.9; 487 | } 488 | 489 | button:hover { 490 | background-color: var(--ev-c-gray-2); 491 | opacity: 0.9; 492 | } 493 | 494 | .drag { 495 | -webkit-app-region: drag; 496 | } 497 | 498 | .border { 499 | border: solid 1px var(--ev-c-gray-2); 500 | } 501 | 502 | .logo { 503 | margin-bottom: 20px; 504 | -webkit-user-drag: none; 505 | height: 128px; 506 | width: 128px; 507 | will-change: filter; 508 | transition: filter 300ms; 509 | } 510 | 511 | .logo:hover { 512 | filter: drop-shadow(0 0 1.2em #6988e6aa); 513 | } 514 | 515 | .creator { 516 | font-size: 14px; 517 | line-height: 16px; 518 | color: var(--ev-c-text-2); 519 | font-weight: 600; 520 | margin-bottom: 10px; 521 | } 522 | 523 | .space-h { 524 | justify-content: space-between; 525 | display: flex; 526 | } 527 | 528 | .space-h-f { 529 | width: 100%; 530 | display: flex; 531 | } 532 | 533 | .text { 534 | font-size: 28px; 535 | font-weight: 700; 536 | color: var(--ev-c-text-1); 537 | } 538 | 539 | .text-sm { 540 | font-size: 16px; 541 | color: var(--ev-c-text-1); 542 | font-weight: 600; 543 | } 544 | 545 | .text-sm-2 { 546 | font-size: 12px; 547 | color: var(--ev-c-text-1); 548 | font-weight: 600; 549 | } 550 | 551 | .tip { 552 | font-size: 16px; 553 | color: var(--ev-c-text-2); 554 | font-weight: 600; 555 | } 556 | 557 | .tip-sm { 558 | font-size: 12px; 559 | color: var(--ev-c-text-2); 560 | font-weight: 600; 561 | } 562 | 563 | .pointer { 564 | cursor: pointer; 565 | } 566 | 567 | .link { 568 | color: var(--ev-c-blue-1); 569 | } 570 | 571 | .vl { 572 | border-left: 2px solid var(--ev-c-gray-2); 573 | } 574 | 575 | img { 576 | -webkit-user-drag: none; 577 | } 578 | 579 | .icon-sm { 580 | position: relative; 581 | top: 4px; 582 | width: 18px; 583 | height: 18px; 584 | margin-right: 3px; 585 | -webkit-app-region: no-drag; 586 | } 587 | 588 | .icon-sm:hover { 589 | filter: blur(1px) brightness(1.5); 590 | } 591 | 592 | .gradient { 593 | background: -webkit-linear-gradient(180deg, #c1b5c7 5%, #332c96); 594 | background-clip: text; 595 | -webkit-background-clip: text; 596 | -webkit-text-fill-color: transparent; 597 | font-weight: 700; 598 | } 599 | 600 | .flex { 601 | display: flex; 602 | } 603 | 604 | .flex-c { 605 | display: flex; 606 | flex-direction: column; 607 | } 608 | 609 | .m-4 { 610 | margin: 1rem; 611 | } 612 | 613 | .mu-4 { 614 | margin-top: 1rem; 615 | } 616 | 617 | .mu-2 { 618 | margin-top: 0.5rem; 619 | } 620 | 621 | .mu-1 { 622 | margin-top: 0.2rem; 623 | } 624 | 625 | .pl-4 { 626 | padding-left: 1rem; 627 | } 628 | 629 | .pl-2 { 630 | padding-left: 0.5rem; 631 | } 632 | 633 | .pr-4 { 634 | padding-right: 0.50rem; 635 | } 636 | 637 | .action { 638 | flex-shrink: 0; 639 | padding: 10px 0px; 640 | } 641 | 642 | .float-right { 643 | float: right; 644 | } 645 | 646 | .float-left { 647 | float: left; 648 | } 649 | 650 | .action a { 651 | cursor: pointer; 652 | text-decoration: none; 653 | display: inline-block; 654 | border: 1px solid transparent; 655 | text-align: center; 656 | font-weight: 600; 657 | white-space: nowrap; 658 | border-radius: 3px; 659 | padding: 0 20px; 660 | line-height: 25px; 661 | font-size: 14px; 662 | border-color: var(--ev-button-alt-border); 663 | color: var(--ev-button-alt-text); 664 | background-color: var(--ev-button-alt-bg); 665 | } 666 | 667 | .action a:hover { 668 | border-color: var(--ev-button-alt-hover-border); 669 | color: var(--ev-button-alt-hover-text); 670 | background-color: var(--ev-button-alt-hover-bg); 671 | } 672 | 673 | .versions { 674 | position: absolute; 675 | bottom: 30px; 676 | margin: 0 auto; 677 | padding: 15px 0; 678 | font-family: 'Menlo', 'Lucida Console', monospace; 679 | display: inline-flex; 680 | overflow: hidden; 681 | align-items: center; 682 | border-radius: 22px; 683 | background-color: #202127; 684 | backdrop-filter: blur(24px); 685 | } 686 | 687 | .versions li { 688 | display: block; 689 | float: left; 690 | border-right: 1px solid var(--ev-c-gray-1); 691 | padding: 0 20px; 692 | font-size: 14px; 693 | line-height: 14px; 694 | opacity: 0.8; 695 | 696 | &:last-child { 697 | border: none; 698 | } 699 | } 700 | 701 | @media (max-width: 720px) { 702 | .text { 703 | font-size: 20px; 704 | } 705 | } 706 | 707 | @media (max-width: 620px) { 708 | .versions { 709 | display: none; 710 | } 711 | } 712 | 713 | @media (max-width: 350px) { 714 | 715 | .tip, 716 | .actions { 717 | display: none; 718 | } 719 | } 720 | 721 | @keyframes slideInRight { 722 | 0% { 723 | transform: translateX(110%); 724 | } 725 | 726 | 100% { 727 | transform: translateX(0%); 728 | } 729 | } 730 | 731 | @keyframes fadeOut { 732 | 0% { 733 | opacity: 0.9; 734 | } 735 | 736 | 100% { 737 | opacity: 0; 738 | } 739 | } 740 | 741 | @keyframes fadeIn { 742 | 0% { 743 | opacity: 0; 744 | } 745 | 746 | 100% { 747 | opacity: 1; 748 | } 749 | } 750 | 751 | @keyframes toastProgress { 752 | 0% { 753 | width: 300px; 754 | } 755 | 756 | 100% { 757 | width: 0px; 758 | } 759 | } 760 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/alert.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/background.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RattlesHyper/TrafficerMC/e9bd554ffd695ab56781104d357363aa3442b347/src/renderer/assets/icons/icon.png -------------------------------------------------------------------------------- /src/renderer/assets/icons/min.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | 15 |

TrafficerMC

16 |

17 |
18 |
19 | 20 | 21 |
22 |
23 |
24 | 32 | 33 |
34 | 35 |
36 | 37 |
38 | 39 |
40 | 41 |
42 | 43 |
44 | 45 |
46 | 47 |
48 |
49 |

Username:

50 | 51 |
52 |
53 | 54 |
55 |
56 | 57 |
58 | 59 |
60 |

Server:

61 | 62 | 63 |
64 | 65 |
66 |

Max accounts:

67 | 68 |
69 | 70 |
71 |

Join delay:

72 | 73 |
74 | 75 |
76 |

Join message:

77 | 78 |
79 | 80 |
81 | 82 | 83 |
84 | 85 |
86 | 87 |
88 |

Options

89 | 90 |
91 |

Name:

92 | 93 | 99 |
100 | 101 | 107 | 108 |
109 |

Auth:

110 | 116 |
117 | 118 |
119 |

Version:

120 | 164 |
165 | 166 |
167 |

Mode:

168 | 172 |
173 | 174 |
175 | 176 |
177 | 178 |
179 | 180 | 606 | 607 | 641 | 642 | 720 | 721 | 791 | 792 | 814 | 815 |
816 |
817 | 818 |
    819 | 820 | 914 | 915 |
    916 |
    917 |
    918 | 919 |

    0

    920 |
    921 |
    922 | 923 |

    00:00

    924 |
    925 | 931 |
    932 |
    933 | 934 |
    935 |
    936 | 937 |
    938 | 939 | 940 | 1004 | 1005 | 1006 | 1007 | -------------------------------------------------------------------------------- /src/renderer/src/index.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('DOMContentLoaded', () => { 2 | window.electron?.ipcRenderer.send('loaded') 3 | 4 | window.electron?.ipcRenderer.on('setConfig', (event, config, version) => { 5 | setConfigValues(config) 6 | fetch('https://raw.githubusercontent.com/RattlesHyper/TrafficerMC/main/VERSION', { 7 | method: 'GET' 8 | }) 9 | .then((response) => response.text()) 10 | .then((result) => { 11 | const liveVersion = parseFloat(result) 12 | const currentVersion = version.current 13 | if (currentVersion != liveVersion) { 14 | notify('Warning', 'New version available. Please update your client', 'warning') 15 | } 16 | }) 17 | document.getElementById('versionString').innerHTML = `v${version.current}` 18 | }) 19 | 20 | window.electron?.ipcRenderer.on('fileSelected', (event, id, path) => { 21 | const filename = path.match(/[^\\]+$/)[0] 22 | document.getElementById(id).innerHTML = filename 23 | }) 24 | 25 | window.electron?.ipcRenderer.on('showBottab', () => { 26 | document.getElementById('bottingTab').click() 27 | }) 28 | 29 | const valueElements = document.querySelectorAll( 30 | 'input[type="text"], input[type="number"], input[type="range"], select, textarea' 31 | ) 32 | valueElements.forEach((select) => { 33 | select.addEventListener('change', valueChange) 34 | }) 35 | 36 | const checkboxElements = document.querySelectorAll('input[type="checkbox"]') 37 | checkboxElements.forEach((check) => { 38 | check.addEventListener('click', checkboxClick) 39 | }) 40 | 41 | const buttonElements = document.querySelectorAll('button, .button') 42 | buttonElements.forEach((button) => { 43 | button.addEventListener('click', buttonClick) 44 | }) 45 | 46 | const tabElements = document.querySelectorAll('.tab, .tab-2') 47 | tabElements.forEach((tab) => { 48 | tab.addEventListener('click', navClick) 49 | }) 50 | 51 | window.electron?.ipcRenderer.on('initConfig', () => { 52 | valueElements.forEach((select) => { 53 | if (!select.id) return 54 | window.electron?.ipcRenderer.send('setConfig', 'value', select.id, select.value) 55 | }) 56 | checkboxElements.forEach((check) => { 57 | if (!check.id) return 58 | window.electron?.ipcRenderer.send('setConfig', 'boolean', check.id, check.checked) 59 | }) 60 | }) 61 | 62 | window.electron?.ipcRenderer.on('notify', (event, title, body, type, img, keep) => { 63 | notify(title, body, type, img, keep) 64 | }) 65 | 66 | window.electron?.ipcRenderer.on('proxyEvent', (event, info) => { 67 | if (info.event === 'scraped') { 68 | logProxy('Scraped', 'success', '') 69 | } else { 70 | logProxy(info.proxy, info.event, info.message) 71 | } 72 | document.getElementById('proxyCheckStatusCount').innerHTML = info.count 73 | switch (info.event) { 74 | case 'start': 75 | document.getElementById('proxyCheckStatus').style.display = 'block' 76 | document.getElementById('proxyList').value = '' 77 | break 78 | case 'stop': 79 | document.getElementById('proxyCheckStatus').style.display = 'none' 80 | notify('Info', 'Stopped proxy test.', 'success') 81 | updateProxyList() 82 | break 83 | case 'success': 84 | document.getElementById('proxyList').value += `${info.proxy}\n` 85 | updateProxyList() 86 | break 87 | case 'scraped': 88 | document.getElementById('proxyList').value += `\n${info.message}\n` 89 | clearProxyEmpty() 90 | updateProxyList() 91 | break 92 | default: 93 | } 94 | }) 95 | 96 | window.electron?.ipcRenderer.on('botEvent', (event, info) => { 97 | switch (info.event) { 98 | case 'login': 99 | addPlayer(info.id) 100 | logChat('Bot', info.id, 'Connected to the server.') 101 | break 102 | case 'authmsg': 103 | directChat( 104 | `

    ${info.id}

    First time signing in. Use a web browser to open the page https://www.microsoft.com/link and enter the code: ${info.message} [click to copy]

    ` 105 | ) 106 | break 107 | case 'easymcAuth': 108 | directChat( 109 | `

    Authentication

    EasyMC authentication requires an alt token. Check https://easymc.io/get to get a token.

    ` 110 | ) 111 | break 112 | case 'chat': 113 | logChat('Bot', info.id, info.message) 114 | break 115 | case 'kicked': 116 | logChat('Bot', info.id, 'Kicked: ' + info.message) 117 | removePlayer(info.id) 118 | break 119 | case 'end': 120 | logChat('Bot', info.id, 'Connection: ' + info.message) 121 | removePlayer(info.id) 122 | break 123 | default: 124 | } 125 | }) 126 | // setInterval(() => { 127 | // logChat('Bot', 'Username', 'TEST') 128 | // notify('TEST', 'Welcome back User', 'success') 129 | // logProxy('proxy:port', 'fail', 'Test') 130 | // }, 1000) 131 | }) 132 | 133 | function valueChange(event) { 134 | const selectedValue = event.target.value 135 | const selectId = event.target.id 136 | window.electron?.ipcRenderer.send('setConfig', 'value', selectId, selectedValue) 137 | 138 | switch (selectId) { 139 | case 'nameType': 140 | checkUsername() 141 | break 142 | default: 143 | } 144 | } 145 | 146 | function buttonClick(event) { 147 | const buttonId = event.target.id 148 | switch (buttonId) { 149 | case 'minimize': 150 | window.electron?.ipcRenderer.send('win:invoke', 'min') 151 | break 152 | case 'close': 153 | window.electron?.ipcRenderer.send('win:invoke', 'close') 154 | break 155 | case 'resetConfig': 156 | window.electron?.ipcRenderer.send('deleteConfig') 157 | notify('Info', 'Config has been reset. Please restart the app', 'success') 158 | break 159 | case 'nameFileLabel': 160 | window.electron?.ipcRenderer.send('open', 'nameFileLabel', 'Name File') 161 | break 162 | case 'selectAll': 163 | selectAll() 164 | break 165 | case 'proxyClearDupe': 166 | clearDupe() 167 | notify('Info', 'Cleared duplicate proxies', 'success') 168 | break 169 | default: 170 | window.electron?.ipcRenderer.send('btnClick', buttonId) 171 | break 172 | } 173 | } 174 | 175 | function checkboxClick(event) { 176 | const checkId = event.target.id 177 | const state = event.target.checked 178 | window.electron?.ipcRenderer.send('setConfig', 'boolean', checkId, state) 179 | window.electron?.ipcRenderer.send('checkboxClick', checkId, state) 180 | } 181 | 182 | function navClick(event) { 183 | const classes = event.target.classList 184 | const navName = event.target.innerText.toLowerCase() 185 | const tabContent = document.getElementsByClassName(classes[1]) 186 | 187 | Array.from(tabContent).forEach((content) => { 188 | if (!content.classList.contains(classes[0])) { 189 | content.style.display = 'none' 190 | } 191 | }) 192 | 193 | const selectedContent = document.getElementById(navName) 194 | selectedContent.style.display = 'block' 195 | 196 | const tabs = document.getElementsByClassName(classes[0]) 197 | Array.from(tabs).forEach((tab) => { 198 | tab.classList.remove('selected') 199 | }) 200 | 201 | event.currentTarget.classList.add('selected') 202 | } 203 | 204 | function checkUsername() { 205 | const nameType = document.getElementById('nameType') 206 | const fileDiv = document.getElementById('nameFileDiv') 207 | 208 | const isFileBased = nameType.value === 'file' 209 | fileDiv.style.display = isFileBased ? 'block' : 'none' 210 | } 211 | 212 | function setConfigValues(obj) { 213 | for (const keyType in obj) { 214 | const keys = Object.keys(obj[keyType]) 215 | for (const key of keys) { 216 | const element = document.getElementById(key) 217 | if (element) { 218 | if (keyType === 'value') { 219 | element.value = obj.value[key] 220 | } else if (keyType === 'boolean') { 221 | element.checked = obj.boolean[key] 222 | } 223 | } 224 | } 225 | } 226 | checkUsername() 227 | } 228 | 229 | function notify(title, body, type, img, keep) { 230 | const notification = document.createElement('li') 231 | notification.className = type 232 | 233 | const top = document.createElement('div') 234 | top.className = 'space-h' 235 | 236 | const topbar = document.createElement('div') 237 | topbar.className = 'flex' 238 | 239 | const titleText = document.createElement('p') 240 | titleText.className = 'text-sm' 241 | titleText.innerHTML = title 242 | topbar.appendChild(titleText) 243 | 244 | const closeDiv = document.createElement('div') 245 | const closeBtn = document.createElement('p') 246 | closeBtn.className = 'text-sm' 247 | closeBtn.innerHTML = 'X' 248 | closeBtn.onclick = () => rmNotification() 249 | closeDiv.appendChild(closeBtn) 250 | 251 | top.appendChild(topbar) 252 | top.appendChild(closeDiv) 253 | 254 | const bodyDiv = document.createElement('div') 255 | bodyDiv.className = 'n-message' 256 | const bodyText = document.createElement('p') 257 | bodyText.className = 'tip-sm' 258 | bodyText.innerText = body 259 | bodyDiv.appendChild(bodyText) 260 | if (img) { 261 | const imgTag = document.createElement('img') 262 | imgTag.src = img 263 | bodyDiv.appendChild(imgTag) 264 | } 265 | 266 | notification.appendChild(top) 267 | notification.appendChild(bodyDiv) 268 | 269 | document.getElementById('notifications').appendChild(notification) 270 | 271 | if (!keep) { 272 | const progress = document.createElement('div') 273 | progress.className = 'n-progress' 274 | notification.appendChild(progress) 275 | setTimeout(() => { 276 | rmNotification() 277 | }, 3000) 278 | } 279 | function rmNotification() { 280 | notification.classList.add('fade') 281 | setTimeout(() => { 282 | notification.remove() 283 | }, 300) 284 | } 285 | } 286 | 287 | function addPlayer(name) { 288 | const list = document.getElementById('botList') 289 | const auto = document.getElementById('autoSelect').checked 290 | const b = document.createElement('li') 291 | b.className = 'botListItem' 292 | b.innerHTML = name 293 | b.onclick = () => { 294 | b.classList.toggle('selected') 295 | updateSelected() 296 | } 297 | list.appendChild(b) 298 | list.scrollTop = list.scrollHeight 299 | updateBotCount() 300 | if (auto) { 301 | selectAll('auto') 302 | } 303 | } 304 | 305 | function removePlayer(name) { 306 | const list = document.querySelectorAll('.botListItem') 307 | list.forEach((bot) => { 308 | if (bot.innerHTML === name) { 309 | bot.remove() 310 | updateSelected() 311 | } 312 | }) 313 | updateBotCount() 314 | } 315 | 316 | function updateBotCount() { 317 | const count = document.getElementById('botCount') 318 | const list = document.getElementById('botList') 319 | count.innerHTML = list.children.length 320 | } 321 | 322 | function selectAll(auto) { 323 | const list = document.getElementById('botList') 324 | const allSelected = Array.from(list.children).every((li) => li.classList.contains('selected')) 325 | Array.from(list.children).forEach((bot) => { 326 | if (auto) { 327 | bot.classList.toggle('selected', true) 328 | } else { 329 | bot.classList.toggle('selected', !allSelected) 330 | } 331 | }) 332 | updateSelected() 333 | } 334 | 335 | function updateSelected() { 336 | const list = document.getElementById('botList') 337 | const selectedBots = Array.from(list.children).filter((bot) => bot.classList.contains('selected')) 338 | window.electron?.ipcRenderer.send( 339 | 'playerList', 340 | selectedBots.map((bot) => bot.innerHTML) 341 | ) 342 | } 343 | 344 | function logProxy(proxy, type, message) { 345 | const scroll = document.getElementById('autoScrollProxy').checked 346 | const logBox = document.getElementById('proxyLogbox') 347 | const li = document.createElement('li') 348 | li.className = type 349 | const updiv = document.createElement('div') 350 | updiv.className = 'space-h' 351 | 352 | const ddiv = document.createElement('div') 353 | const msg = document.createElement('p') 354 | msg.className = 'text-sm-2 mu-1' 355 | msg.style = 'user-select: text;' 356 | msg.innerHTML = message 357 | ddiv.appendChild(msg) 358 | 359 | const pl = document.createElement('p') 360 | pl.style = 'user-select: text;' 361 | pl.className = 'text-sm' 362 | pl.innerHTML = proxy 363 | updiv.appendChild(pl) 364 | 365 | const pr = document.createElement('p') 366 | pr.className = 'text-sm' 367 | pr.innerHTML = type 368 | 369 | updiv.appendChild(pr) 370 | 371 | li.appendChild(updiv) 372 | li.appendChild(ddiv) 373 | 374 | logBox.appendChild(li) 375 | if (scroll) { 376 | logBox.scrollTop = logBox.scrollHeight 377 | } 378 | } 379 | 380 | function clearProxyEmpty() { 381 | const textarea = document.getElementById('proxyList') 382 | const lines = textarea.value.split('\n') 383 | const nonEmptyLines = lines.filter(function (line) { 384 | return line.trim() !== '' 385 | }) 386 | textarea.value = nonEmptyLines.join('\n') 387 | } 388 | 389 | function clearDupe() { 390 | const textarea = document.getElementById('proxyList') 391 | const lines = textarea.value.split('\n') 392 | const uniqueLines = {} 393 | for (let i = 0; i < lines.length; i++) { 394 | const line = lines[i].trim() 395 | uniqueLines[line] = true 396 | } 397 | const uniqueLinesArray = Object.keys(uniqueLines) 398 | const result = uniqueLinesArray.join('\n') 399 | textarea.value = result 400 | } 401 | 402 | function updateProxyList() { 403 | window.electron?.ipcRenderer.send( 404 | 'setConfig', 405 | 'value', 406 | 'proxyList', 407 | document.getElementById('proxyList').value 408 | ) 409 | } 410 | 411 | function logChat(prefix, name, text) { 412 | const enable = document.getElementById('enableChat').checked 413 | if (!enable) return 414 | const chatBox = document.getElementById('chatBox') 415 | const scroll = document.getElementById('autoScrollChat').checked 416 | 417 | const li = document.createElement('li') 418 | 419 | const spaceHDiv = document.createElement('div') 420 | spaceHDiv.className = 'space-h' 421 | 422 | const flexDiv = document.createElement('div') 423 | flexDiv.className = 'flex' 424 | 425 | const prefixP = document.createElement('p') 426 | prefixP.className = 'text-sm link' 427 | prefixP.textContent = prefix 428 | 429 | flexDiv.appendChild(prefixP) 430 | 431 | const spaceHFDiv = document.createElement('div') 432 | spaceHFDiv.className = 'space-h-f pl-2' 433 | 434 | const nameP = document.createElement('p') 435 | nameP.className = 'text-sm' 436 | nameP.style = 'user-select: text;' 437 | nameP.textContent = name 438 | 439 | spaceHFDiv.appendChild(nameP) 440 | 441 | spaceHDiv.appendChild(flexDiv) 442 | spaceHDiv.appendChild(spaceHFDiv) 443 | 444 | const textP = document.createElement('p') 445 | textP.className = 'text-sm-2' 446 | textP.style = 'user-select: text;' 447 | textP.textContent = text 448 | 449 | li.appendChild(spaceHDiv) 450 | li.appendChild(textP) 451 | 452 | chatBox.appendChild(li) 453 | 454 | if (scroll) { 455 | chatBox.scrollTop = chatBox.scrollHeight 456 | } 457 | } 458 | 459 | function directChat(string) { 460 | const chatBox = document.getElementById('chatBox') 461 | const li = document.createElement('li') 462 | li.innerHTML = string 463 | chatBox.appendChild(li) 464 | } 465 | --------------------------------------------------------------------------------