├── .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 |
--------------------------------------------------------------------------------