├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── config.json ├── input ├── bios.txt ├── proxies.txt ├── tokens.txt └── usernames.txt ├── main.py ├── messages.json ├── requirements.txt ├── scraped ├── massmention.txt └── members.txt ├── src ├── __init__.py ├── _captcha.py ├── _utility.py ├── discordsocket.py ├── global_variables.py ├── multitool.py └── scrapper.py └── update.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/75b47ec6336d02cdcf30db711b3f6ef5c4303f1d/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .DS_Store 3 | input/newtokas.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Shahzain Masood 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discord MultiTool V2 - Discontinued 2 | ## By Shahzain 3 |
  • This project was inspired by DMDGO by Vanshaj https://github.com/V4NSH4J/discord-mass-DM-GO
  • 4 | 5 | # About 6 | Discord Multi Tool-PY is a multi-threaded Discord Self Bot and it is used for many features such as the token joiner and MassDM 7 | 8 | # Why did this project get Discontinued? 9 | 10 | Well the main reason is that I don't really have time to work on this project, however if I get time, I would love to fix this project and get it working like it used to be. Some features of this project don't work as they used to work: 11 | 12 |
  • The joiner gets captcha all the time even if the tokens are good
  • 13 |
  • Mass DM is detected
  • 14 |
  • Documentation is not updated after the initial release of Multitool-V2
  • 15 | 16 | All the other features of this tool such as the server spammer still work like before. 17 | 18 | If you would like to help me with this project, please create a pull request it would be very appreciated. 19 | 20 | # Want to buy any custoom tool? Join the link below! 21 | 22 | https://t.me/shahzaintools 23 | 24 | # Whats the difference between multitool v1 and v2? 25 | 26 | Well multitool v1 had alot of performance issues and bugs.
    27 | MultiTool V2 has less bugs and is faster then v1. More features will be added soon! 28 | 29 | # Features 30 |
  • Proxyless/HTTP Proxy Support
  • 31 |
  • Token Joiner
  • 32 |
  • Token Bio Changer
  • 33 |
  • Token Username Changer
  • 34 |
  • Token Format Changer [Email:Pass:Token] => [Token]
  • 35 |
  • MassDM
  • 36 |
  • Single DM Spam
  • 37 |
  • Server Joiner(Captcha support)
  • 38 |
  • Server Leaver
  • 39 |
  • Member Scrapper
  • 40 |
  • Reaction adder
  • 41 |
  • Username Scrapper(Soon)
  • 42 |
  • Easy to use and setup
  • 43 |
  • Server spammer(Has MassMention)
  • 44 |
  • Token Checker
  • 45 |
  • This can send multiple messages and this can ping the user aswell which is pretty cool if you ask me
  • 46 |
  • Auto Update, This will automatically install all the newest updates so you dont have to download them manually 😃
  • 47 | 48 | ## Config 49 | | Name | Type | Description | 50 | |-------------------------------------------|------|----------------------------------------------------------------------------------------------------------------------------------------| 51 | | removeDeadTokens | bool | After checking MultiTool will remove all non-working tokens from input/tokens.txt | 52 | | captcha_api | str | The url of the captcha api you are gonna use to bypass captcha on join, capmonster.cloud and anti-captcha.com are currently supported. | 53 | | captcha_key | str | Your api key for captcha_api. | 54 | | useDelays | bool | If you want to use delays to avoid being rate limited and detected. | 55 | | save_failed_logs | bool | Saves failed logs to logs/logtype.txt | 56 | | bypass_membership_screening | bool | Set this to true if you want to bypass membership screening upon joining server. Setting this to true is recommended. | 57 | | bypass_reaction_verification | bool | Set this to true if your target server has a reaction verification. | 58 | | proxyless | bool | If you want to use the tool without proxies. | 59 | | proxyProtocol | str | Your proxy protocol, by default its http | 60 | | sendMultitpleMessages | bool | Set this to true if you want to send multiple messages to the target user | 61 | | friendBeforeDM | bool | Set this to true if you want to send a friend request to the target user before dm. | 62 | | request_timeout | int | Timeout for all requests in seconds. Increase if slow connection or proxies. | 63 | | online_before_dm | bool | Websockets the token before dm, makes it online which is more realistic. | 64 | | use_captcha_solver | bool | Set this to true if you want to use MultiTool's custom captcha-solver for solving the captcha on joiner. | 65 | 66 | ## Example Configuration 67 | ```json 68 | { 69 | "proxy": { 70 | "protocol": "http", 71 | "proxyless": true 72 | }, 73 | "captcha": { 74 | "api": "anti-captcha.com", 75 | "key": "YOUR CAPTCHA KEY" 76 | }, 77 | "joiner": { 78 | "useDelays": false, 79 | "delay": 10, 80 | "bypassMembershipScreening": true, 81 | "bypassReactionVerification": false 82 | }, 83 | "removeDeadTokens": true, 84 | "preferedThreads": 100000, 85 | "saveFailedLogs": true, 86 | "requestTimeout": 35, 87 | "traceback": false 88 | } 89 | ``` 90 | ## Need Help? 91 | Join our guilded server for help: https://www.guilded.gg/i/2Zv9o7L2 92 | ## How to use? 93 | Install all the requirements 94 |
    pip install -r requirements.txt
     95 | 
    96 | Fill in the config.json file and message.json file
    97 | And run the main.py file 98 |
    python main.py
     99 | 
    100 | ## Note: 101 | Shahzain345 will not be responsible for any damage caused by this script.
    102 | If you see anyone selling this tool report it to me asap! 103 | ## Thank You! 104 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "proxy": { 3 | "protocol": "http", 4 | "proxyless": true 5 | }, 6 | "captcha": { 7 | "api": "anti-captcha.com", 8 | "key": "YOUR CAPTCHA KEY" 9 | }, 10 | "joiner": { 11 | "useDelays": false, 12 | "delay": 10, 13 | "bypassMembershipScreening": true, 14 | "bypassReactionVerification": false 15 | }, 16 | "removeDeadTokens": true, 17 | "preferedThreads": 100000, 18 | "saveFailedLogs": true, 19 | "requestTimeout": 35, 20 | "traceback": false 21 | } -------------------------------------------------------------------------------- /input/bios.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/75b47ec6336d02cdcf30db711b3f6ef5c4303f1d/input/bios.txt -------------------------------------------------------------------------------- /input/proxies.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/75b47ec6336d02cdcf30db711b3f6ef5c4303f1d/input/proxies.txt -------------------------------------------------------------------------------- /input/tokens.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/75b47ec6336d02cdcf30db711b3f6ef5c4303f1d/input/tokens.txt -------------------------------------------------------------------------------- /input/usernames.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/75b47ec6336d02cdcf30db711b3f6ef5c4303f1d/input/usernames.txt -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2022 Shahzain Masood 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | """ 11 | print("MultiTool is starting...\nGetting discord buildNumber") 12 | from src import MultiTool, MPrint, Utility, scrape, global_variables, _captcha, DiscordSocket 13 | import json 14 | import threading 15 | import random 16 | import emoji 17 | import asyncio 18 | import os 19 | from update import lookforupdates 20 | from traceback import format_exc 21 | from typing import Union 22 | from colorama import Fore, Style 23 | from tasksio import TaskPool 24 | from multiprocessing.pool import ThreadPool 25 | # VARIABLES 26 | console = MPrint() 27 | goodtokens = [] 28 | seen = {} 29 | duplicates = 0 30 | notjoined = 0 31 | lockedtokens = 0 32 | invalidTokens = 0 33 | 34 | # SYNC functions 35 | def clearConsole(): return os.system( 36 | 'cls' if os.name in ('nt', 'dos') else 'clear') 37 | def showMenu(): 38 | print(f'{Style.BRIGHT}{Fore.BLUE}1: Check Tokens {Style.RESET_ALL}') 39 | print(f'{Style.BRIGHT}{Fore.BLUE}2: Join Server {Style.RESET_ALL}') 40 | print(f'{Style.BRIGHT}{Fore.BLUE}3: Server Spammer {Style.RESET_ALL}') 41 | print(f'{Style.BRIGHT}{Fore.BLUE}4: Server Leaver {Style.RESET_ALL}') 42 | print( 43 | f'{Style.BRIGHT}{Fore.BLUE}5: Format changer: [Email:Pass:Token] -> [Token] {Style.RESET_ALL}') 44 | print(f'{Style.BRIGHT}{Fore.BLUE}6: Single DM Spam {Style.RESET_ALL}') 45 | print(f'{Style.BRIGHT}{Fore.BLUE}7: Mass DM {Style.RESET_ALL}') 46 | print( 47 | f'{Style.BRIGHT}{Fore.BLUE}8: Username Changer [Email:Pass:Token] {Style.RESET_ALL}') 48 | print( 49 | f'{Style.BRIGHT}{Fore.BLUE}9: Bio Changer {Style.RESET_ALL}') 50 | print( 51 | f'{Style.BRIGHT}{Fore.BLUE}10: Friends Spammer {Style.RESET_ALL}') 52 | print( 53 | f'{Style.BRIGHT}{Fore.BLUE}11: Member scrapper {Style.RESET_ALL}') 54 | print( 55 | f'{Style.BRIGHT}{Fore.BLUE}12: VC spammer {Style.RESET_ALL}') 56 | print( 57 | f'{Style.BRIGHT}{Fore.BLUE}13: Exit {Style.RESET_ALL}') 58 | def changeFormat(token: str): 59 | if ":" not in token or len(token.split(":")) == 2: 60 | token = token if ":" not in token else token.split(":")[1] 61 | console.f_print( 62 | f"{token} format is not [Email:Pass:Token]") 63 | return token 64 | newToken = token.split(":")[2] 65 | console.s_print(f"{token} -> {newToken}") 66 | return newToken 67 | def setTitle(tokens: list): return os.system( 68 | f'title Discord MultiTool - Tokens: {len(tokens)} - Proxies: {len(open("input/proxies.txt").read().splitlines())} - By Shahzain' if os.name == "nt" else f'echo -n -e "\033]0;Discord MultiTool | Tokens {len(tokens)} | Proxies {len(open("input/proxies.txt").read().splitlines())} | Captcha Balance: $ {_captcha.Captcha().getBalance()} - By Shahzain\007"' 69 | ) 70 | 71 | #ASYNC FUNCTIONS 72 | async def scrapeMassMention(token, guildId, channelId): 73 | o = await buildMultiTool(token) 74 | res = await o.getGuild(guildId) 75 | if "name" not in res: 76 | return await scrapeMassMention(random.choice(open("input/tokens.txt").read().splitlines()), guildId, channelId) 77 | open("scraped/massmention.txt", "w").write("") 78 | console.s_print(f"Scraping in {guildId} with {token}") 79 | members = scrape(token, guildId, channelId) 80 | for member in members: 81 | console.s_print(f"Scraped {member}") 82 | open("scraped/massmention.txt", "a").write(member + "\n") 83 | console.s_print(f"Total Scrapped: {len(members)}") 84 | return True 85 | def vcSpammer(token: str, guild: str, channelId: str): 86 | while True: 87 | sock = DiscordSocket(token) 88 | sock.run(channelId, guild) 89 | console.s_print(f"Joined vc: {token}") 90 | async def usernameChanger(token: str, username: str): 91 | if ":" not in token: 92 | console.f_print(f"{token} is not [Email:Pass:Token]") 93 | return None 94 | spllited = token.split(":") 95 | password = spllited[1] 96 | token = spllited[2] 97 | m = await buildMultiTool(token) 98 | await m.usernameChange(username, password) 99 | 100 | async def bioChanger(token: str, bio: str): 101 | m = await buildMultiTool(token) 102 | await m.bioChange(bio) 103 | 104 | async def scrapeMembers(token, guildId, channelId): 105 | o = await buildMultiTool(token) 106 | res = await o.getGuild(guildId) 107 | if "name" not in res: 108 | return await scrapeMembers(random.choice(open("input/tokens.txt").read().splitlines()), guildId, channelId) 109 | open("scraped/massmention.txt", "w").write("") 110 | console.s_print(f"Scraping in {guildId} with {token}") 111 | members = scrape(token, guildId, channelId) 112 | for member in members: 113 | console.s_print(f"Scraped {member}") 114 | open("scraped/members.txt", "a").write(member + "\n") 115 | console.s_print(f"Total Scrapped: {len(members)}") 116 | return True 117 | 118 | async def leave(token: str, guildId: str): 119 | o = await buildMultiTool(token) 120 | await o.leave(guildId) 121 | 122 | async def buildMultiTool(token: str) -> MultiTool: 123 | try: 124 | m = MultiTool() 125 | await m._init(token) 126 | return m 127 | except Exception as e: 128 | console.f_print(f"Error while building Multitool Coroutine: {e}") 129 | return await buildMultiTool(token) 130 | 131 | async def sendMessage(token: str, channelId: str, message: str, massMention: bool, massMentionSize: int): 132 | m = await buildMultiTool(token) 133 | if m == None: 134 | return None 135 | while True: 136 | await m.sendMessageInChannel( 137 | message, channelId, massMention, massMentionSize) 138 | async def friendRequest(token: str, username: str, discrim: str): 139 | m = await buildMultiTool(token) 140 | await m.sendFriendRequest(username, discrim) 141 | 142 | async def spamMessages(token: str, userId: str, message: str): 143 | m = await buildMultiTool(token) 144 | if not m: 145 | return None 146 | while True: 147 | _, _res = await m.sendDirectMessage(userId, message) 148 | if _: 149 | console.s_print(f"{token} successfully sent dm to {userId}") 150 | else: 151 | console.f_print(f"{token} failed to send dm to {userId}") 152 | 153 | async def sendDm(token: str, userId: str, message: str): 154 | m = await buildMultiTool(token) 155 | _, _res = await m.sendDirectMessage(userId, message) 156 | if 'You are opening direct messages too fast' in _res.text: 157 | global_variables.qurantined_tokens.append(token) 158 | console.f_print(f"{token} got ratelimited. sleeping for 10 minutes.") 159 | threading.Thread( 160 | target=global_variables.removeFromQurantine, args=(token, )) 161 | elif 'Cannot send messages to this user' in _res.text: 162 | global_variables.blaclisted_users.append(userId) 163 | elif 'You need to verify your account in order to perform this action' in _res.text or '401: Unauthorized' in _res.text: 164 | global_variables.locked_tokens.append(token) 165 | global_variables.qurantined_tokens.append(token) 166 | console.f_print(f"{token} token got locked during mass dm") 167 | elif _: 168 | console.s_print(f"{token} successfully sent dm to {userId}") 169 | 170 | 171 | async def checkToken(token: str): 172 | global lockedtokens 173 | global invalidTokens 174 | m = await buildMultiTool(token) 175 | if m == None: 176 | return None 177 | res, typee = await m.checkToken() 178 | if typee == "LOCKED": 179 | lockedtokens += 1 180 | return False 181 | if typee == "INVALID": 182 | invalidTokens += 1 183 | if res: 184 | goodtokens.append(token) 185 | return True 186 | 187 | 188 | async def join(token: str, rawInvite: str, ctx: str, guildId: str, channelId: Union[str, None] = None, messageId: Union[str, None] = None): 189 | global notjoined 190 | m = await buildMultiTool(token) 191 | if m == None: 192 | return None 193 | res, req = await m.join(rawInvite, ctx) 194 | if not res: 195 | notjoined += 1 196 | return res 197 | if Utility().config["joiner"]["bypassMembershipScreening"] and "show_verification_form" in req.json(): 198 | await m.bypassScreening(guildId, rawInvite) 199 | if Utility().config["joiner"]["bypassReactionVerification"] and channelId != None: 200 | emojiObj = await m.getReactions(messageId, channelId) 201 | if emojiObj["id"] == None: 202 | emojiOut = emoji.demojize( 203 | emojiObj["name"] 204 | ) 205 | else: 206 | emojiOut = emojiObj["name"] + "%3A" + emojiObj["id"] 207 | if emojiOut[0] == ":" and emojiOut[len(emojiOut) - 1] == ":": 208 | emojiOut = emoji.emojize(emojiOut) 209 | await m.addReaction(messageId, channelId, emojiOut) 210 | return res 211 | async def menu(): 212 | global duplicates 213 | global notjoined 214 | global lockedtokens 215 | global invalidTokens 216 | try: 217 | tokens = open("input/tokens.txt").read().splitlines() 218 | setTitle(tokens) 219 | print("") 220 | showMenu() 221 | print("\n") 222 | choice = int(input( 223 | f"[>] {Fore.GREEN}{Style.BRIGHT}Enter your choice \n>> {Style.RESET_ALL}")) 224 | if choice == 1: 225 | console.s_print(f"Checking tokens...") 226 | async with TaskPool(10_000) as pool: 227 | for token in tokens: 228 | if token in seen: 229 | duplicates += 1 230 | continue 231 | seen[token] = True 232 | await pool.put(checkToken(token)) 233 | console.s_print( 234 | f"All tokens checked.\nDuplicates: {duplicates} | Valid: {len(goodtokens)} | Invalid: {invalidTokens} | Locked Tokens: {lockedtokens} | Total Bad Tokens(includes locked+invalid tokens): {invalidTokens + lockedtokens}") 235 | seen.clear() 236 | duplicates = 0 237 | lockedtokens = 0 238 | invalidTokens = 0 239 | if Utility().config["removeDeadTokens"]: 240 | open("input/tokens.txt", "w").write("") 241 | for goodtoken in goodtokens: 242 | open("input/tokens.txt", "a").write(f"{goodtoken}\n") 243 | goodtokens.clear() 244 | return await menu() 245 | if choice == 2: 246 | console.s_print(f"Server Joiner...") 247 | rawInvite = input(f"[>] {Fore.GREEN}{Style.BRIGHT}Enter your invite: {Style.RESET_ALL}").split( 248 | "discord.gg/")[1] 249 | try: 250 | req1 = Utility().getInviteInfo(rawInvite) 251 | if req1 == {"message": "Unknown Invite", "code": 10006}: 252 | console.f_print(f"https://discord.gg/{rawInvite} is INVALID") 253 | return await menu() 254 | except: 255 | console.f_print("Failed to get invite info, You are probably ratelimited, try using a VPN") 256 | req1 = {"guild": { "id": None }, "channel": { "id": None }} 257 | if Utility().config.get("proxy")["proxyless"]: # if proxyless is true, redirect back to `menu` otherwise keep joining 258 | return await menu() 259 | console.s_print(f"https://discord.gg/{rawInvite} is VALID") 260 | ctx = Utility().getContextProperties( 261 | req1["channel"]["id"], req1["guild"]["id"]) 262 | deley = Utility().config["joiner"]["delay"] 263 | useDelay = Utility().config["joiner"]["useDelays"] 264 | channelId = None 265 | messageId = None 266 | if Utility().config["joiner"]["bypassReactionVerification"]: 267 | channelId = input( 268 | f"{Fore.GREEN}{Style.BRIGHT}Reaction Verifier: Enter the channelId the message is on: {Style.RESET_ALL}") 269 | messageId = input( 270 | f"{Fore.GREEN}{Style.BRIGHT}Reaction Verifier: Enter the messageId of the message the reaction is on: {Style.RESET_ALL}") 271 | async with TaskPool(10_000) as pool: 272 | for token in tokens: 273 | if token in seen: 274 | duplicates += 1 275 | continue 276 | seen[token] = True 277 | await pool.put(join(token, rawInvite, ctx, req1["guild"]["id"], channelId, messageId)) 278 | if useDelay: 279 | await asyncio.sleep(deley) 280 | console.s_print( 281 | f"Tokens successfully joined https://discord.gg/{rawInvite} \nDuplicate Tokens: {duplicates} | Joined Tokens: {len(tokens) - notjoined} | Not Joined: {notjoined}") 282 | seen.clear() 283 | duplicates = 0 284 | return await menu() 285 | if choice == 3: 286 | console.s_print(f"Server spammer...") 287 | massMention = True if input( 288 | f"{Fore.GREEN}{Style.BRIGHT}Do you want to use massmention? (y/n){Style.RESET_ALL}").lower() == "y" else None 289 | massMentionSize = None if massMention != True else int(input( 290 | f"{Fore.GREEN}{Style.BRIGHT}Enter the ammount of people you want to mention in one message: {Style.RESET_ALL}")) 291 | message = input( 292 | f"{Fore.GREEN}{Style.BRIGHT}Please type the message: {Style.RESET_ALL}") 293 | channelId = input( 294 | f"{Fore.GREEN}{Style.BRIGHT}Enter the channel Id you want to spam: {Style.RESET_ALL}") 295 | m = await buildMultiTool(random.choice(tokens)) 296 | guildId = await m.getChannel(channelId) 297 | 298 | if massMention and input(f"{Fore.GREEN}{Style.BRIGHT}Do you want to use already scrapped members for massmention? (y/n) {Style.RESET_ALL}").lower() != "y": 299 | await scrapeMassMention(random.choice(tokens), guildId, channelId) 300 | async with TaskPool(10_000) as pool: 301 | for token in tokens: 302 | await pool.put(sendMessage(token, channelId, message, massMention, massMentionSize)) 303 | if choice == 4: 304 | console.s_print(f"Server leaver...") 305 | guildId = input( 306 | f"{Fore.GREEN}{Style.BRIGHT}Enter the server Id you want to leave: {Style.RESET_ALL}") 307 | async with TaskPool(10_000) as pool: 308 | for token in tokens: 309 | await pool.put(leave(token, guildId)) 310 | return await menu() 311 | if choice == 5: 312 | console.s_print(f"Format changer...") 313 | open("input/tokens.txt", "w").write("") 314 | for token in tokens: 315 | token = changeFormat(token) 316 | open("input/tokens.txt", "a").write(token + "\n") 317 | return await menu() 318 | if choice == 6: 319 | console.s_print(f"Single DM Spammer...") 320 | message = input( 321 | f"{Fore.GREEN}{Style.BRIGHT}Enter your message: {Style.RESET_ALL}") 322 | userId = input( 323 | f"{Fore.GREEN}{Style.BRIGHT}Enter the vitim's userId: {Style.RESET_ALL}") 324 | async with TaskPool(10_000) as pool: 325 | for token in tokens: 326 | await pool.put(spamMessages(token, userId, message)) 327 | return await menu() 328 | if choice == 7: 329 | console.f_print( 330 | f"Hey Multitool user. We are sorry but mass dm is currently disabled") 331 | return await menu() 332 | console.s_print(f"Mass DM...") 333 | members = open("scraped/members.txt").read().splitlines() 334 | if len(members) == 0: 335 | channelId = input( 336 | f"{Fore.GREEN}{Style.BRIGHT}Enter the channel Id you want to spam: {Style.RESET_ALL}") 337 | m = await buildMultiTool(random.choice(tokens)) 338 | guildId = await m.getChannel(channelId) 339 | await scrapeMembers(random.choice(tokens), guildId, channelId) 340 | members = open("scraped/members.txt").read().splitlines() 341 | msg: str = json.loads(open("messages.json").read())["content"] 342 | async with TaskPool(10_000) as pool: 343 | for member in members: 344 | variableReplaced = msg.replace("<@user>", f"<@{member}>") 345 | await pool.put(sendDm(global_variables.getGoodToken(), member, variableReplaced)) 346 | return await menu() 347 | if choice == 8: 348 | console.s_print(f"Username changer...") 349 | usernames = open("input/usernames.txt").read().splitlines() 350 | if len(usernames) == 0: 351 | console.f_print( 352 | f"Please input some usernames before using this feature") 353 | input("Press enter to return to menu") 354 | return await menu() 355 | async with TaskPool(10_000) as pool: 356 | for token in tokens: 357 | await pool.put(usernameChanger(token, random.choice(usernames))) 358 | return await menu() 359 | if choice == 9: 360 | console.s_print(f"Bio changer...") 361 | bios = open("input/bios.txt").read().splitlines() 362 | if len(usernames) == 0: 363 | console.f_print( 364 | f"Please input some bios before using this feature") 365 | input("Press enter to return to menu") 366 | return await menu() 367 | async with TaskPool(10_000) as pool: 368 | for token in tokens: 369 | await pool.put(bioChanger(token, random.choice(bios))) 370 | return await menu() 371 | if choice == 10: 372 | console.s_print(f"Friend spammer...") 373 | spllited = input( 374 | f"{Fore.GREEN}{Style.BRIGHT}Enter your username and tag: {Style.RESET_ALL}").split("#") 375 | username = spllited[0] 376 | discrim = spllited[1] 377 | async with TaskPool(10_000) as pool: 378 | for token in tokens: 379 | await pool.put(friendRequest(token, username, discrim)) 380 | if choice == 11: 381 | console.s_print("Member scrapper...") 382 | channelId = input( 383 | f"{Fore.GREEN}{Style.BRIGHT}Enter your channelId you want to scrape: {Style.RESET_ALL}") 384 | m = await buildMultiTool(random.choice(tokens)) 385 | guildId = await m.getChannel(channelId) 386 | await scrapeMembers(random.choice(tokens), guildId, channelId) 387 | return await menu() 388 | if choice == 12: 389 | console.s_print("VC Spammer...") 390 | channelId = input(f"{Fore.GREEN}{Style.BRIGHT}Enter your Voice channelId you want to spam: {Style.RESET_ALL}") 391 | m = await buildMultiTool(random.choice(tokens)) # build the multitool coroutine 392 | guildId = await m.getChannel(channelId=channelId) 393 | pool = ThreadPool(10_000) 394 | for token in tokens: 395 | if token in seen: 396 | continue 397 | seen[token] = True 398 | pool.apply_async(vcSpammer, (token, guildId, channelId, )) 399 | pool.join() 400 | return await menu() 401 | if choice == 13: 402 | exit() 403 | else: 404 | console.f_print("Invalid Choice") 405 | return await menu() 406 | 407 | except Exception as e: 408 | console.f_print(e) 409 | if Utility().config["traceback"]: 410 | print(format_exc()) 411 | return await menu() 412 | if __name__ == "__main__": 413 | clearConsole() 414 | print(Fore.BLUE + Style.BRIGHT + """ 415 | ██████╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ███╗ ███╗██╗ ██╗██╗ ████████╗██╗████████╗ ██████╗ ██████╗ ██╗ 416 | ██╔══██╗██║██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔══██╗ ████╗ ████║██║ ██║██║ ╚══██╔══╝██║╚══██╔══╝██╔═══██╗██╔═══██╗██║ 417 | ██║ ██║██║███████╗██║ ██║ ██║██████╔╝██║ ██║ ██╔████╔██║██║ ██║██║ ██║ ██║ ██║ ██║ ██║██║ ██║██║ 418 | ██║ ██║██║╚════██║██║ ██║ ██║██╔══██╗██║ ██║ ██║╚██╔╝██║██║ ██║██║ ██║ ██║ ██║ ██║ ██║██║ ██║██║ 419 | ██████╔╝██║███████║╚██████╗╚██████╔╝██║ ██║██████╔╝ ██║ ╚═╝ ██║╚██████╔╝███████╗ ██║ ██║ ██║ ╚██████╔╝╚██████╔╝███████╗ 420 | ╚═════╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ 421 | P Y T H O N 422 | """ + Style.RESET_ALL) 423 | print(f'{Style.BRIGHT}By Shahzain\n\n') 424 | if len(open("input/tokens.txt").read().splitlines()) == 0: 425 | console.f_print("Input some tokens before restarting...") 426 | input("Press Enter To Exit\n") 427 | exit() 428 | console.w_print("Mass DM is disabled") 429 | console.w_print( 430 | "Before using any feature, please check your tokens, To avoid errors.") 431 | if Utility().config["proxy"]["proxyless"]: 432 | console.w_print( 433 | "Proxyless is on. Switch to proxies for best performance") 434 | asyncio.run(menu()) 435 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": "Hello" 3 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | httpx 2 | 2captcha-python 3 | tasksio 4 | colorama 5 | websocket-client 6 | emoji -------------------------------------------------------------------------------- /scraped/massmention.txt: -------------------------------------------------------------------------------- 1 | 965585810739056710 2 | 866897478163103755 3 | 993300852376862770 4 | 848440716073631784 5 | 742272368537370674 6 | 993472788528640062 7 | 993472795956752444 8 | 776469239057481750 9 | 410861587956760577 10 | 769522023168344066 11 | 704720015311503521 12 | 826734124089868288 13 | 873506193233035314 14 | 989987759060045955 15 | 993473102371635230 16 | 559773726028070913 17 | 958707412456575046 18 | 839391299257368596 19 | 930979682982174801 20 | 832695337532522506 21 | 360305015492968449 22 | 651452087435132948 23 | 709193086282235946 24 | 984521053768388688 25 | 742543866892124270 26 | 993281698034888867 27 | 993280747169398935 28 | 254694862174158848 29 | 951308284067991633 30 | 826631953373593631 31 | 931750132385472533 32 | 794114995809681418 33 | 993281480606359613 34 | 548758644364279830 35 | 930811425360846859 36 | 787466019475685436 37 | 528513126207979524 38 | 943590472864116776 39 | 755458285460783135 40 | 989991879636549633 41 | 763152113798086677 42 | 872840672661635073 43 | 743113934294351922 44 | 985135510362161152 45 | 801460433936580638 46 | 946479280685215774 47 | 875757366522552411 48 | 993281937940688896 49 | 983142252580847626 50 | 993607124850593922 51 | 761633042540003328 52 | 795658956302581762 53 | 980838704098705448 54 | 674365262547386380 55 | 993287495053742121 56 | 864296572521807902 57 | 994181362972110849 58 | 989965645686910996 59 | 880484609073238148 60 | 994196706033934427 61 | 994209039561015386 62 | 947454611063722044 63 | 672871097452068884 64 | 885468019042701313 65 | 994223609612607538 66 | 735461726719311952 67 | 993600358473547816 68 | 993475245543534623 69 | 755140223469355008 70 | 705752193168310373 71 | 909065987968622693 72 | 877553889812025345 73 | 969391192049590332 74 | 755501978477658172 75 | 472344848058286090 76 | 994216746099675206 77 | 994187381139841034 78 | 791287221852307496 79 | 814057476894097418 80 | 989988331096014860 81 | 472004020186382347 82 | 994227577562603642 83 | 899299015915634708 84 | 873610466541326337 85 | 470992713794191360 86 | 986601840852496404 87 | 993283234102263828 88 | 758574414974681128 89 | 989527546624352276 90 | 992050584838479962 91 | 774692744533770300 92 | 381837686685827074 93 | 835857162080747581 94 | 704600124751478834 95 | 620287085324140554 96 | 874319985344938015 97 | 994198514903027732 98 | 990002602739646568 99 | 981184193046413313 100 | 816633712186753036 101 | 494899216280322049 102 | 918424838861901844 103 | 565810970203521034 104 | 702827423443582996 105 | 374514879228411905 106 | 976336004640309258 107 | 690626039478616084 108 | 718509261344211024 109 | 734361454311899155 110 | 765654983256571954 111 | 960958233802051594 112 | 777018488211505153 113 | 743073735015137287 114 | 605297023385862146 115 | 632722140294021150 116 | 511476326159482882 117 | 933792182425501767 118 | 888422856831152148 119 | 632466531476832268 120 | 538346960436985876 121 | 675606337685553172 122 | 993101643728027779 123 | 980809716261347338 124 | 990223214590443530 125 | 966075336670457876 126 | 971791282202611792 127 | 970519147484172288 128 | 986537935375585280 129 | 987032790203781131 130 | 199460555814076416 131 | 892751685443784736 132 | 972856121884835910 133 | 876359271535767634 134 | 897938748531154994 135 | 786085584145874954 136 | 716947777761968158 137 | 632613333199224833 138 | 831426328296423484 139 | 712017496294817902 140 | 692656986872873011 141 | 988158911230132276 142 | 545543328024756245 143 | 490465545481748482 144 | 621889833664774150 145 | 521984731382939678 146 | 971432606933606481 147 | 919191907379314739 148 | 159985870458322944 149 | 235148962103951360 150 | 282859044593598464 151 | 270904126974590976 152 | 302050872383242240 153 | 853583833747161091 154 | 547905866255433758 155 | 499595256270946326 156 | 818570692847992902 157 | 408785106942164992 158 | 155149108183695360 159 | 651095740390834176 160 | 458276816071950337 161 | 491769129318088714 162 | 247283454440374274 163 | 993621100560916510 164 | 929729323110367334 165 | 989978386732572732 166 | 662980710281248779 167 | 648097241915195403 168 | 993281283184672829 169 | 949982264438185995 170 | 981205213291614278 171 | 993604800866435092 172 | 989399766683242540 173 | 984568769827459162 174 | 785598063356018738 175 | 718927680820609104 176 | 567750811845787648 177 | 776116296147402773 178 | 994215545882824814 179 | 874620392742981652 180 | 912445243369087078 181 | 667753389660045313 182 | 982166848437104640 183 | 989996229373087834 184 | 979535919084363866 185 | 853216705109819462 186 | 739060750039121992 187 | 827090747538669579 188 | 899199374528221204 189 | 979738667503456306 190 | 852646576739975188 191 | 563027808473972748 192 | 979612821555925025 193 | 948277671157203004 194 | 986530755767312397 195 | 763396883237961788 196 | 886293469679722506 197 | 993282434890219530 198 | 811599168115703819 199 | 993285978875764756 200 | 774663397127225344 201 | 720906354797183006 202 | 992719827619819520 203 | 989977229532475424 204 | 970772753407963156 205 | 909674267447267329 206 | 837194987942445076 207 | 656996650354475018 208 | 714125743512092763 209 | 839562780071821342 210 | 984519921390223444 211 | 666346767977152556 212 | 761584991938412554 213 | 993287498656653472 214 | 956614455859482714 215 | 989998008609415270 216 | 984518788047327405 217 | 993614692666839105 218 | 989995039222558781 219 | 729050436580933692 220 | 831060081189191711 221 | 843393740579930123 222 | 993473315605848117 223 | 919992740165615676 224 | 980773528414613564 225 | 353832014153121803 226 | 993285141315190794 227 | 805731880401895446 228 | 993602380094525581 229 | 874952472660181062 230 | 993281687398142014 231 | 994189893301436508 232 | -------------------------------------------------------------------------------- /scraped/members.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/75b47ec6336d02cdcf30db711b3f6ef5c4303f1d/scraped/members.txt -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2022 Shahzain Masood 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | """ 11 | from ._utility import Utility, MPrint 12 | from .multitool import MultiTool 13 | from .scrapper import scrape 14 | from .discordsocket import DiscordSocket 15 | from .global_variables import * 16 | 17 | def getVersion(): 18 | return "2.0.0" -------------------------------------------------------------------------------- /src/_captcha.py: -------------------------------------------------------------------------------- 1 | import twocaptcha 2 | import httpx 3 | from ._utility import Utility, MPrint 4 | console = MPrint() 5 | 6 | 7 | class Captcha: 8 | def __init__(self): 9 | self._client = httpx.AsyncClient() 10 | self._utils = Utility() 11 | self.api = self._utils.config["captcha"]["api"] 12 | self.key = self._utils.config["captcha"]["key"] 13 | def getBalance(self): 14 | if self.api == "2captcha.com": 15 | captcha = twocaptcha.TwoCaptcha(self.key) 16 | return captcha.balance() 17 | elif self.api == "anti-captcha.com" or self.api == "capmonster.cloud": 18 | resp = httpx.post(f"https://api.{self.api}/getBalance", json={"clientKey": self.key}).json() 19 | if resp.get("errorId") > 0: 20 | console.f_print(f"Error while getting captcha balance: {resp.get('errorDescription')}") 21 | return 0.0 22 | return resp.get("balance") 23 | async def getCaptcha(self, sitekey: str, rqdata): 24 | if self.api == "2captcha.com": 25 | captcha = twocaptcha.TwoCaptcha(self.key) 26 | return captcha.hcaptcha(sitekey, "https://discord.com", { 27 | "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15", 28 | "data": rqdata 29 | })["code"] 30 | elif self.api == "anti-captcha.com" or self.api == "capmonster.cloud": 31 | taskId = await self._client.post(f"https://api.{self.api}/createTask", json={"clientKey": self.key, "task": {"type": "HCaptchaTaskProxyless", "websiteURL": "https://discord.com/", 32 | "websiteKey": sitekey, "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15", "enterprisePayload": { 33 | "rqdata": rqdata, 34 | "sentry": True 35 | }}}, timeout=30) 36 | taskId = taskId.json() 37 | if taskId.get("errorId") > 0: 38 | console.f_print( 39 | f"Captcha(createTask) - {taskId.get('errorDescription')}") 40 | return None 41 | taskId = taskId.get("taskId") 42 | solvedCaptcha = None 43 | while not solvedCaptcha: 44 | captchaData = await self._client.post(f"https://api.{self.api}/getTaskResult", json={ 45 | "clientKey": self.key, "taskId": taskId}, timeout=30) 46 | captchaData = captchaData.json() 47 | if captchaData["errorId"] > 0: 48 | console.f_print( 49 | f"Captcha(getTaskResult) - {captchaData['errorDescription']}") 50 | return None 51 | if captchaData.get("status") == "ready": 52 | solvedCaptcha = captchaData.get( 53 | "solution").get("gRecaptchaResponse") 54 | return solvedCaptcha 55 | -------------------------------------------------------------------------------- /src/_utility.py: -------------------------------------------------------------------------------- 1 | import random, json, re 2 | from httpx import Client 3 | from colorama import Fore, Style 4 | from base64 import b64encode as b 5 | import httpx 6 | class Utility: 7 | def __init__(self): 8 | self.config = self.getConfig() 9 | self.proxy = self.getProxy() 10 | def getConfig(self): 11 | return json.loads(open("config.json").read()) 12 | def getProxy(self): 13 | if self.config["proxy"]["proxyless"]: 14 | return None 15 | return f"{self.config['proxy']['protocol']}://{random.choice(open('input/proxies.txt').read().splitlines())}" 16 | def getBuildNum(self): 17 | """Gets the build number that discord is currently on, makes the x-super-properties header more realistic.""" 18 | #client = Client() 19 | #asset = re.compile(r'([a-zA-z0-9]+)\.js', re.I).findall(client.get(f'https://discord.com/app', headers={'User-Agent': 'Mozilla/5.0'}).read().decode('utf-8'))[-1] 20 | #fr = client.get(f'https://discord.com/assets/{asset}.js', headers={'User-Agent': 'Mozilla/5.0'}).read().decode('utf-8') 21 | #return str(re.compile('Build Number: [0-9]+, Version Hash: [A-Za-z0-9]+').findall(fr)[0].replace(' ', '').split(',')[0].split(':')[-1]).replace(' ', '') 22 | return "133852" 23 | def getContextProperties(self, guildId: str, channelId: str) -> str: 24 | return b(json.dumps({"location":"Join Guild","location_guild_id":guildId,"location_channel_id":channelId,"location_channel_type":0}, separators=(',', ':')).encode()).decode() 25 | def getInviteInfo(self, rawInvite): 26 | res = httpx.get(f'https://discord.com/api/v9/invites/{rawInvite}?with_counts=true', headers={ 27 | "Authorization": "undefined"}, timeout=30).json() 28 | return res 29 | 30 | class MPrint: 31 | def w_print(self, message: str): 32 | """Print warning""" 33 | print(f"[{Style.BRIGHT}{Fore.RED}WARN{Style.RESET_ALL}] {Style.BRIGHT}{Fore.YELLOW}{message}{Style.RESET_ALL}") 34 | def s_print(self, message: str): 35 | """Print SUCCESS""" 36 | print(f"[{Style.BRIGHT}{Fore.MAGENTA}SUCCESS{Style.RESET_ALL}] {Style.BRIGHT}{Fore.GREEN}{message}{Style.RESET_ALL}") 37 | def f_print(self, message: str): 38 | """Print FAIL""" 39 | print(f"[{Style.BRIGHT}{Fore.YELLOW}FAILED{Style.RESET_ALL}] {Style.BRIGHT}{Fore.RED}{message}{Style.RESET_ALL}") 40 | console = MPrint() 41 | -------------------------------------------------------------------------------- /src/discordsocket.py: -------------------------------------------------------------------------------- 1 | import websocket, threading, json, time 2 | class DiscordSocket(threading.Thread): 3 | def __init__(self, token: str) -> None: 4 | self.token = token 5 | threading.Thread.__init__(self) 6 | self.ws = websocket.WebSocket() 7 | self.running = True 8 | def login(self): 9 | self.ws.connect("wss://gateway.discord.gg/?encoding=json&v=9") 10 | interval = self.recieve()["d"]["heartbeat_interval"] / 1000 11 | threading.Thread(target=self.heartbeat, args=(interval,)).start() 12 | def heartbeat(self, interval: float): 13 | while self.running: 14 | time.sleep(interval) 15 | self.send_payload({"op": 1, "d": None}) 16 | def recieve(self): 17 | data = self.ws.recv() 18 | if data: 19 | return json.loads(data) 20 | def send_payload(self, data: dict): 21 | self.ws.send(json.dumps(data)) 22 | 23 | def online(self): 24 | self.send_payload( 25 | { 26 | "op": 2, 27 | "d": { 28 | "token": self.token, 29 | "capabilities": 125, 30 | "properties": { 31 | "os": "iOS", 32 | "browser": "Safari", 33 | "device": "iPhone", 34 | "system_locale": "en-US", 35 | "browser_user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Mobile/15E148 Safari/604.1", 36 | "browser_version": "15.1", 37 | "os_version": "15.1", 38 | "referrer": "", 39 | "referring_domain": "", 40 | "referrer_current": "", 41 | "referring_domain_current": "", 42 | "release_channel": "stable", 43 | "client_build_number": 140268, 44 | "client_event_source": None, 45 | }, 46 | "presence": { 47 | "status": "online", 48 | "since": 0, 49 | "activities": [ 50 | { 51 | "name": "Custom Status", 52 | "type": 4, 53 | "state": "I spam vcs", 54 | "emoji": None, 55 | } 56 | ], 57 | "afk": False, 58 | }, 59 | "compress": False, 60 | "client_state": { 61 | "guild_hashes": {}, 62 | "highest_last_message_id": "0", 63 | "read_state_version": 0, 64 | "user_guild_settings_version": -1, 65 | "user_settings_version": -1, 66 | }, 67 | }, 68 | } 69 | ) 70 | def join_vc(self, channel_id: str, guild_id: str): 71 | self.send_payload({"op": 4,"d": {"guild_id": guild_id,"channel_id": channel_id,"self_mute": True,"self_deaf": True, "self_stream?": True, "self_video": False}}) 72 | self.send_payload({"op": 18,"d": {"type": "guild","guild_id": guild_id,"channel_id": channel_id,"preferred_region": "singapore"}}) 73 | time.sleep(3) 74 | self.running = False 75 | self.ws.close() 76 | 77 | def run(self, channel_id: str, guild_id: str): 78 | self.login() 79 | self.online() 80 | self.join_vc(channel_id, guild_id) 81 | -------------------------------------------------------------------------------- /src/global_variables.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | ids_scraped: int = 0 4 | qurantined_tokens = [] 5 | locked_tokens = [] 6 | blaclisted_users = [] 7 | 8 | 9 | def removeFromQurantine(token): 10 | time.sleep(630) 11 | for _i in range(len(qurantined_tokens)): 12 | if qurantined_tokens[_i] == token: 13 | qurantined_tokens.pop(_i) 14 | 15 | 16 | def getGoodToken() -> str: 17 | while True: 18 | token = random.choice(open("input/tokens.txt").read().splitlines()) 19 | if (token in qurantined_tokens): 20 | continue 21 | if (token in locked_tokens): 22 | continue 23 | return token -------------------------------------------------------------------------------- /src/multitool.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2022 Shahzain Masood 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | """ 11 | from httpx import AsyncClient 12 | from ._utility import Utility, MPrint 13 | from ._captcha import Captcha 14 | from base64 import b64encode as encoder 15 | from colorama import Fore, Style 16 | from .discordsocket import DiscordSocket 17 | import websocket 18 | import random 19 | import time 20 | import json as jsonLib 21 | import string 22 | console = MPrint() 23 | BUILD_NUM = Utility().getBuildNum() 24 | console.s_print(f"Discord is currently on build: {BUILD_NUM}") 25 | time.sleep(1.5) 26 | 27 | class MultiTool: 28 | """ 29 | # Multitool main class 30 | """ 31 | async def _init(self, token: str): 32 | self._utility = Utility() 33 | self.client = AsyncClient(proxies=self._utility.proxy, cookies={"locale": "en-US"}, headers={ 34 | "Accept": "*/*", 35 | "Pragma": "no-cache", 36 | "Cache-Control": "no-cache", 37 | "Accept-Language": "en-us", 38 | "Host": "discord.com", 39 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15", 40 | "Referer": "https://discord.com/", 41 | "Accept-Encoding": "br, gzip, deflate", 42 | "Connection": "keep-alive" 43 | }, timeout=self._utility.config["requestTimeout"]) 44 | self.token = token 45 | self.client.headers["X-Track"] = self._build_trackers( 46 | trackerType="x-track") 47 | res = await self.client.get( 48 | "https://discord.com/api/v9/experiments") 49 | try: 50 | self.client.headers["X-Fingerprint"] = res.json().get("fingerprint") 51 | except: 52 | #self.client.headers["X-Fingerprint"] = "992405718051344425.40u0H3W3P2iOxVPP-50_HbyxbcI" 53 | None # do nothing if fingerprint aint found, cuz fingerprint isn't needed anyways just improves your success rate 54 | self.client.headers["Origin"] = "https://discord.com/" 55 | self.client.headers["Authorization"] = token 56 | self.client.headers["X-Debug-Options"] = "bugReporterEnabled" 57 | self.client.headers["X-Discord-Locale"] = "en-US" 58 | self.client.headers["Referer"] = "https://discord.com/channels/@me" 59 | del self.client.headers["X-Track"] 60 | self.client.headers["X-Super-Properties"] = self._build_trackers( 61 | trackerType="x-super-properties") 62 | self._captcha = Captcha() 63 | 64 | def _build_trackers(self, trackerType: str) -> str: 65 | """Builds the x-track/x-super-properties header""" 66 | if trackerType == "x-track": 67 | return encoder(jsonLib.dumps({"os": "Mac OS X", "browser": "Safari", "device": "", "system_locale": "en-us", "browser_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15", "browser_version": "13.1.2", "os_version": "10.13.6", "referrer": "", "referring_domain": "", "referrer_current": "", "referring_domain_current": "", "release_channel": "stable", "client_build_number": 9999, "client_event_source": None}, separators=(',', ':')).encode()).decode() 68 | elif trackerType == "x-super-properties": 69 | return encoder(jsonLib.dumps({"os": "Mac OS X", "browser": "Safari", "device": "", "system_locale": "en-us", "browser_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15", "browser_version": "13.1.2", "os_version": "10.13.6", "referrer": "", "referring_domain": "", "referrer_current": "", "referring_domain_current": "", "release_channel": "stable", "client_build_number": BUILD_NUM, "client_event_source": None}, separators=(',', ':')).encode()).decode() 70 | else: 71 | raise Exception( 72 | "Invalid tracker type. Currently support types('x-track', 'x-super-properties')") 73 | 74 | async def join(self, rawInvite: str, ctxProperties: str): 75 | self.client.headers["X-Context-Properties"] = ctxProperties 76 | req = await self.client.post( 77 | f"https://discord.com/api/v9/invites/{rawInvite}", json={}) 78 | if "captcha_key" not in req.json(): 79 | if req.json().get("message") == "The user is banned from this guild.": 80 | console.f_print(f"{self.token} is banned from this server.") 81 | return False, req 82 | console.s_print( 83 | f"{self.token} successfully joined discord.gg/{rawInvite}") 84 | return True, req 85 | console.w_print(f"{self.token} captcha detected. solving thru {self._utility.config.get('captcha').get('api')}") 86 | captcha_sitekey = req.json()["captcha_sitekey"] 87 | captcha_rqtoken = req.json()["captcha_rqtoken"] 88 | captcha_rqdata = req.json()["captcha_rqdata"] 89 | captcha_key = await self._captcha.getCaptcha(captcha_sitekey, captcha_rqdata) 90 | req = await self.client.post(f"https://discord.com/api/v9/invites/{rawInvite}", json={ 91 | "captcha_key": captcha_key, 92 | "captcha_rqtoken": captcha_rqtoken 93 | }) 94 | if req.status_code == 200: 95 | console.s_print( 96 | f"{self.token} successfully joined discord.gg/{rawInvite}") 97 | return True, req 98 | else: 99 | console.f_print( 100 | f"{self.token} failed to join discord.gg/{rawInvite}") 101 | return False, req 102 | 103 | async def bypassScreening(self, guildId: str, rawInvite: str): 104 | req = await self.client.get( 105 | f"https://discord.com/api/v9/guilds/{guildId}/member-verification?with_guild=false&invite_code={rawInvite}") 106 | if req.status_code != 200: 107 | console.f_print( 108 | f"{self.token} failed to bypass membership screening!") 109 | return False 110 | req = await self.client.put( 111 | f"https://discord.com/api/v9/guilds/{guildId}/requests/@me", json=req.json()) 112 | console.s_print( 113 | f"{self.token} bypassed membership screening for {guildId}(discord.gg/{rawInvite}) successfully!") 114 | return True 115 | 116 | def __random_nonce(self): 117 | """Returns a random str with numbers only, len=18, this is required for sending messages""" 118 | return "".join(random.choice(string.digits) for _ in range(18)) 119 | 120 | async def __send_message(self, payload: dict, channelId: str): 121 | return await self.client.post(f'https://discord.com/api/v9/channels/{channelId}/messages', json=payload) 122 | 123 | async def __open_dm(self, userId: str): 124 | req = await self.client.post( 125 | "https://discord.com/api/v9/users/@me/channels", json={"recipients": [userId]}) 126 | if req.status_code != 200: 127 | console.f_print(f"{self.token} failed to open dm with {userId}") 128 | return None, req 129 | else: 130 | return req.json()['id'], req 131 | 132 | async def sendDirectMessage(self, userId: str, message: str): 133 | """Sends a direct message to <@userId>""" 134 | channelId, req = await self.__open_dm(userId) 135 | if channelId == None: 136 | return None, req # do nothing if it failed to open dms with <@userId> 137 | payload = { 138 | "content": message, 139 | "tts": False, 140 | "nonce": self.__random_nonce() 141 | } 142 | res = await self.__send_message(payload, channelId) 143 | if res.status_code == 200: 144 | return True, res 145 | else: 146 | return False, res 147 | 148 | async def sendMessageInChannel(self, message: str, channelId: str, massMention: bool = False, massMentionSize: int = 6): 149 | scrappedMembers = open("scraped/massmention.txt").read().splitlines() 150 | if len(scrappedMembers) < 1 and massMention: 151 | console.f_print( 152 | f"{self.token} server has 0 members. thus mass mention wont work") 153 | return False 154 | payload = { 155 | "content": None, 156 | "tts": False, 157 | "nonce": self.__random_nonce() 158 | } 159 | if massMention: 160 | mentions = "".join( 161 | f'<@{random.choice(scrappedMembers)}> ' for _ in range(massMentionSize)) 162 | payload["content"] = f"{mentions}\n{message}" 163 | else: 164 | payload["content"] = message 165 | req = await self.__send_message(payload, channelId) 166 | if req.status_code == 200: 167 | console.s_print( 168 | f"{self.token} successfully sent message in {channelId}") 169 | return True 170 | else: 171 | console.f_print( 172 | f"{self.token} failed to send message in {channelId}") 173 | return False 174 | 175 | async def checkToken(self): 176 | req = await self.client.get( 177 | "https://discord.com/api/v9/users/@me/affinities/guilds") 178 | if req.status_code == 403: 179 | console.f_print(f"{self.token} is [LOCKED]") 180 | return False, "LOCKED" 181 | elif req.status_code == 401: 182 | console.f_print(f"{self.token} is {Fore.RESET}{Fore.YELLOW}[INVALID]{Fore.RESET}") 183 | return False, "INVALID" 184 | else: 185 | console.s_print(f"{self.token} is [VALID]") 186 | return True, "VALID" 187 | 188 | async def getGuild(self, guildId: str): 189 | req = await self.client.get(f"https://discord.com/api/v9//guilds/{guildId}") 190 | if "name" not in req.json(): 191 | console.f_print(f"{self.token} is probably not in {guildId}") 192 | return req.json() 193 | return req.json() 194 | 195 | async def getChannel(self, channelId): 196 | """Returns id of the server the channel is in""" 197 | req = await self.client.get( 198 | f"https://discord.com/api/v9/channels/{channelId}") 199 | if 'guild_id' not in req.json(): 200 | console.f_print(f"Server ID not found.") 201 | guildId = input( 202 | f"{Fore.GREEN}{Style.BRIGHT}Please enter the server Id: {Style.RESET_ALL}") 203 | return guildId 204 | guildId = req.json()["guild_id"] 205 | if not guildId: 206 | return None 207 | return guildId 208 | 209 | async def leave(self, guildId: str): 210 | req = await self.client.delete( 211 | f"https://discord.com/api/v9/users/@me/guilds/{guildId}") 212 | if req.status_code != 204: 213 | console.f_print(f"{self.token} failed to leave server: {guildId}") 214 | return False 215 | else: 216 | console.s_print(f"{self.token} left server: {guildId}") 217 | return True 218 | async def getMessage(self, messageId, channelId): 219 | """Gets the message, returns a message object 😃""" 220 | req = await self.client.get( 221 | f"https://discord.com/api/v9/channels/{channelId}/messages?limit=1&around={messageId}") 222 | return req.json() 223 | async def getReactions(self, messageId, channelId): 224 | try: 225 | mesasgeObject = await self.getMessage(messageId, channelId) 226 | mesasgeObject = mesasgeObject[0] 227 | reactions = list(mesasgeObject["reactions"]) 228 | if len(reactions) == 0: 229 | console.f_print("Message has 0 reactions.") 230 | return None 231 | firstEmoji = reactions[0]["emoji"] 232 | return firstEmoji 233 | except Exception as e: 234 | console.f_print(e) 235 | return None 236 | async def addReaction(self, messageId, channelId, emojiObj): 237 | try: 238 | req = await self.client.put( 239 | f"https://discord.com/api/v9/channels/{channelId}/messages/{messageId}/reactions/{emojiObj}/%40me") 240 | if req.status_code != 204: 241 | console.f_print(f"{self.token} failed to bypass reaction verification.") 242 | return req.json() 243 | else: 244 | console.s_print(f"{self.token} successfully bypassed reaction verification.") 245 | return req.json() 246 | except Exception as e: 247 | return None 248 | async def usernameChange(self, username: str, password: str): 249 | req = await self.client.patch("https://discord.com/api/v9/users/@me", json={ 250 | "password": password, 251 | "username": username 252 | }) 253 | if req.status_code == 200: 254 | console.s_print(f"{self.token} Username changed to {username}") 255 | else: 256 | console.f_print(f"{self.token} Failed to change username to {username}") 257 | async def bioChange(self, newBio: str): 258 | req = await self.client.patch("https://discord.com/api/v9/users/@me", json={ 259 | "bio": newBio 260 | }) 261 | if req.status_code != 200: 262 | console.f_print(f"{self.token} failed to change bio to {newBio}") 263 | else: 264 | console.s_print(f"{self.token} changed bio to {newBio}") 265 | async def sendFriendRequest(self, username, discrim): 266 | req = await self.client.post("https://discord.com/api/v9/users/@me/relationships", json={ 267 | "username": username, 268 | "discriminator": discrim 269 | }) 270 | if req.status_code != 204: 271 | console.f_print(f"{self.token} Failed to send friend request to {username}#{discrim}") 272 | return req 273 | else: 274 | console.s_print(f"{self.token} Sent friend request to {username}#{discrim}") 275 | return req -------------------------------------------------------------------------------- /src/scrapper.py: -------------------------------------------------------------------------------- 1 | # Credits to the orignal author: https://github.com/Aniell4/MassDN/blob/main/scrape.py 2 | import websocket 3 | import json 4 | import threading 5 | import time 6 | from .global_variables import * 7 | 8 | 9 | class Utils: 10 | def rangeCorrector(ranges): 11 | if [0, 99] not in ranges: 12 | ranges.insert(0, [0, 99]) 13 | return ranges 14 | 15 | def getRanges(index, multiplier, memberCount): 16 | initialNum = int(index*multiplier) 17 | rangesList = [[initialNum, initialNum+99]] 18 | if memberCount > initialNum+99: 19 | rangesList.append([initialNum+100, initialNum+199]) 20 | return Utils.rangeCorrector(rangesList) 21 | 22 | def parseGuildMemberListUpdate(response): 23 | memberdata = { 24 | "online_count": response["d"]["online_count"], 25 | "member_count": response["d"]["member_count"], 26 | "id": response["d"]["id"], 27 | "guild_id": response["d"]["guild_id"], 28 | "hoisted_roles": response["d"]["groups"], 29 | "types": [], 30 | "locations": [], 31 | "updates": [] 32 | } 33 | 34 | for chunk in response['d']['ops']: 35 | memberdata['types'].append(chunk['op']) 36 | if chunk['op'] in ('SYNC', 'INVALIDATE'): 37 | memberdata['locations'].append(chunk['range']) 38 | if chunk['op'] == 'SYNC': 39 | memberdata['updates'].append(chunk['items']) 40 | else: # invalidate 41 | memberdata['updates'].append([]) 42 | elif chunk['op'] in ('INSERT', 'UPDATE', 'DELETE'): 43 | memberdata['locations'].append(chunk['index']) 44 | if chunk['op'] == 'DELETE': 45 | memberdata['updates'].append([]) 46 | else: 47 | memberdata['updates'].append(chunk['item']) 48 | 49 | return memberdata 50 | 51 | 52 | class DiscordSocket(websocket.WebSocketApp): 53 | def __init__(self, token, guild_id, channel_id): 54 | self.token = token 55 | self.guild_id = guild_id 56 | self.channel_id = channel_id 57 | 58 | self.socket_headers = { 59 | "Accept-Encoding": "gzip, deflate, br", 60 | "Accept-Language": "en-US,en;q=0.9", 61 | "Cache-Control": "no-cache", 62 | "Pragma": "no-cache", 63 | "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits", 64 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15" 65 | } 66 | 67 | super().__init__("wss://gateway.discord.gg/?encoding=json&v=9", 68 | header=self.socket_headers, 69 | on_open=lambda ws: self.sock_open(ws), 70 | on_message=lambda ws, msg: self.sock_message(ws, msg), 71 | on_close=lambda ws, close_code, close_msg: self.sock_close( 72 | ws, close_code, close_msg) 73 | ) 74 | 75 | self.endScraping = False 76 | 77 | self.guilds = {} 78 | self.members = {} 79 | 80 | self.ranges = [[0, 0]] 81 | self.lastRange = 0 82 | self.packets_recv = 0 83 | 84 | def run(self): 85 | self.run_forever() 86 | return self.members 87 | 88 | def scrapeUsers(self): 89 | if self.endScraping == False: 90 | self.send('{"op":14,"d":{"guild_id":"' + self.guild_id + 91 | '","typing":true,"activities":true,"threads":true,"channels":{"' + self.channel_id + '":' + json.dumps(self.ranges) + '}}}') 92 | 93 | def sock_open(self, ws): 94 | #print("[Gateway]", "Connected to WebSocket.") 95 | self.send('{"op":2,"d":{"token":"' + self.token + '","capabilities":125,"properties":{"os":"Windows","browser":"Firefox","device":"","system_locale":"it-IT","browser_user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0","browser_version":"94.0","os_version":"10","referrer":"","referring_domain":"","referrer_current":"","referring_domain_current":"","release_channel":"stable","client_build_number":103981,"client_event_source":null},"presence":{"status":"online","since":0,"activities":[],"afk":false},"compress":false,"client_state":{"guild_hashes":{},"highest_last_message_id":"0","read_state_version":0,"user_guild_settings_version":-1,"user_settings_version":-1}}}') 96 | 97 | def heartbeatThread(self, interval): 98 | try: 99 | while True: 100 | #print("sending heartbeat") 101 | self.send('{"op":1,"d":' + str(self.packets_recv) + '}') 102 | time.sleep(interval) 103 | except Exception as e: 104 | pass # print(e) 105 | return # returns when socket is closed 106 | 107 | def sock_message(self, ws, message): 108 | decoded = json.loads(message) 109 | ids_scraped = len(self.members) 110 | 111 | if decoded is None: 112 | return 113 | 114 | if decoded["op"] != 11: 115 | self.packets_recv += 1 116 | 117 | if decoded["op"] == 10: 118 | threading.Thread(target=self.heartbeatThread, args=( 119 | decoded["d"]["heartbeat_interval"] / 1000, ), daemon=True).start() 120 | 121 | if decoded["t"] == "READY": 122 | for guild in decoded["d"]["guilds"]: 123 | self.guilds[guild["id"]] = { 124 | "member_count": guild["member_count"]} 125 | 126 | if decoded["t"] == "READY_SUPPLEMENTAL": 127 | self.ranges = Utils.getRanges( 128 | 0, 100, self.guilds[self.guild_id]["member_count"]) 129 | # print(self.ranges) 130 | self.scrapeUsers() 131 | 132 | elif decoded["t"] == "GUILD_MEMBER_LIST_UPDATE": 133 | parsed = Utils.parseGuildMemberListUpdate(decoded) 134 | 135 | if parsed['guild_id'] == self.guild_id and ('SYNC' in parsed['types'] or 'UPDATE' in parsed['types']): 136 | for elem, index in enumerate(parsed["types"]): 137 | if index == "SYNC": 138 | # and parsed['locations'][elem] in self.ranges[1:]: #checks if theres nothing in the SYNC data 139 | if len(parsed['updates'][elem]) == 0: 140 | self.endScraping = True 141 | break 142 | 143 | for item in parsed["updates"][elem]: 144 | if "member" in item: 145 | mem = item["member"] 146 | obj = {"tag": mem["user"]["username"] + "#" + 147 | mem["user"]["discriminator"], "id": mem["user"]["id"]} 148 | 149 | self.members[mem["user"]["id"]] = obj 150 | #print("", "synced", mem["user"]["id"]) 151 | 152 | elif index == "UPDATE": 153 | for item in parsed["updates"][elem]: 154 | if "member" in item: 155 | mem = item["member"] 156 | obj = {"tag": mem["user"]["username"] + "#" + 157 | mem["user"]["discriminator"], "id": mem["user"]["id"]} 158 | 159 | self.members[mem["user"]["id"]] = obj 160 | # print("", "synced", mem["user"]["id"]) # ah 1s 161 | 162 | # print(self.endScraping) 163 | # print(self.ranges) 164 | #print("parsed", len(self.members)) 165 | 166 | self.lastRange += 1 167 | self.ranges = Utils.getRanges( 168 | self.lastRange, 100, self.guilds[self.guild_id]["member_count"]) 169 | time.sleep(0.35) 170 | self.scrapeUsers() 171 | 172 | if self.endScraping: 173 | self.close() 174 | 175 | def sock_close(self, ws, close_code, close_msg): 176 | pass 177 | 178 | 179 | def scrape(token: str, guild_id: str, channel_id: str): 180 | sb = DiscordSocket(token, guild_id, channel_id) 181 | return sb.run() -------------------------------------------------------------------------------- /update.py: -------------------------------------------------------------------------------- 1 | # Multitool auto updater 2 | # This file will look for updates and if there is any it will automatically install it. 3 | # Please avoid changing anything in this file. 4 | from colorama import Fore, Style 5 | import httpx 6 | from src import getVersion 7 | APP_VERSION = getVersion() 8 | APP_NAME = "Discord MultiTool V2" 9 | 10 | BASE_URL = "http://localhost:3001" 11 | 12 | 13 | def lookforupdates(): 14 | req = httpx.post(f"{BASE_URL}/api/v2/update", json={ 15 | "v": APP_VERSION 16 | }, timeout=30).json() 17 | if req["current"] == True: 18 | print(f"{Fore.GREEN}{Style.BRIGHT}You are up to date!{Style.RESET_ALL}") 19 | if "message" in req: 20 | print( 21 | f"{Fore.YELLOW}{Style.BRIGHT}The api returned a message as well while looking for updates: \n{Style.RESET_ALL}{Style.BRIGHT}{Fore.GREEN}{req['message']}{Style.RESET_ALL}\n") 22 | else: 23 | try: 24 | CURRENT_VERSION = req["version"] 25 | print( 26 | f"{Fore.LIGHTBLUE_EX}{Style.BRIGHT}You are not up to date.{Style.RESET_ALL}") 27 | if "message" in req: 28 | print( 29 | f"{Fore.YELLOW}{Style.BRIGHT}The api returned a message as well while looking for updates: \n{Style.RESET_ALL}{Style.BRIGHT}{Fore.GREEN}{req['message']}{Style.RESET_ALL}\n") 30 | choice = input( 31 | f"Do you wish to install MultiTool Version: {CURRENT_VERSION}? This will reset all your configuration files (y/n). \n>> ").lower() 32 | if choice != "y": 33 | return None 34 | client = httpx.Client() 35 | req = client.get( 36 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/main.py") 37 | open("main.py", 'w', encoding="utf-8").write(req.text) 38 | req = client.get( 39 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/src/multitool.py").text 40 | open("src/multitool.py", 'w', encoding="utf-8").write(req) 41 | req = client.get( 42 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/src/scrapper.py").text 43 | open("src/scrapper.py", 'w', encoding="utf-8").write(req) 44 | req = client.get( 45 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/src/__init__.py").text 46 | open("src/__init__.py", 'w', encoding="utf-8").write(req) 47 | req = client.get( 48 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/src/_captcha.py").text 49 | open("src/_captcha.py", 'w', encoding="utf-8").write(req) 50 | req = client.get( 51 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/src/_utility.py").text 52 | open("src/_utility.py", 'w', encoding="utf-8").write(req) 53 | req = client.get( 54 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/src/global_variables.py").text 55 | open("src/global_variables.py", 'w', encoding="utf-8").write(req) 56 | req = client.get( 57 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/config.json").text 58 | open("config.json", 'w', encoding="utf-8").write(req) 59 | req = client.get( 60 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/update.py").text 61 | open("update.py", 'w', encoding="utf-8").write(req) 62 | req = client.get( 63 | "https://raw.githubusercontent.com/shahzain345/Discord-MultiToolPY/main/messages.json").text 64 | open("messages.json", 'w', encoding="utf-8").write(req) 65 | print(f"{Fore.GREEN}{Style.BRIGHT}Installed MultiTool version {CURRENT_VERSION}\nPlease restart the tool.{Style.RESET_ALL}") 66 | exit() 67 | except: 68 | print( 69 | f"{Fore.RED}Failed to install update, please install it manually.{Style.RESET_ALL}") 70 | --------------------------------------------------------------------------------