├── requirements.txt ├── .gitignore ├── README.md ├── emojiscraper.py └── stickerscraper.py /requirements.txt: -------------------------------------------------------------------------------- 1 | apnggif 2 | requests 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Emojis/* 2 | Stickers/* 3 | 4 | 5 | time 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discord-Emoji-Scraper 2 | 3 | Scrapes emojis of a discord server/guild and downloads them to your machine, in case you lost the original emojis for your own server or would like to download emojis for any other server to use elsewhere. Supports animated and standard emojis. 4 | 5 | # Installation 6 | 7 | 1. You will need to install python 3.x, PIP, and git in order to use this program. These tools come preinstalled in most MacOS and Linux systems. If you are running Windows, you can either install them seperately by searching for the installers on the internet, or install WSL (Windows subsystem for Linux) 8 | 2. Open a shell window, (Command Prompt on Windows or Terminal on MacOS) 9 | 3. Type the following commands (these commands will install the software) 10 | 11 | `$ git clone https://github.com/PreciousWarrior/Discord-Emoji-Scraper.git` 12 | 13 | `$ cd Discord-Emoji-Scraper` 14 | 15 | `# pip install -r requirements.txt` 16 | 17 | # Usage 18 | 19 | To scrape stickers, run: `python stickerscraper.py` 20 | 21 | To scrape emojis, run: `python emojiscraper.py` 22 | 23 | Follow any instructions that you see. 24 | 25 | # Contributing 26 | 27 | If you have any questions create an issue in the github repo. 28 | If you are facing any errors, bugs or having suggestions, create an issue in the GitHub repo. 29 | If you would like to work on any of the TODO points in the code, feel free to work on them and add a PR 30 | 31 | **NOTE ABOUT STICKERS:** Stickers can be both static and animated picture aswell, however animated pictures use the APNG format (Animated PNG), animated stickers will end under the ".anpg", you should use convert them to gif/webp if you want to use them OR when prompted to convert stickers from apng to gif then accept it. 32 | 33 | # WARNING! READ THIS BEFORE USING 34 | 35 | Using this tool will break the Discord TOS since you will be scraping their website. 36 | This tool may not work properly with MFA tokens (if you have two factor authentication enabled), so if you get a failed to fetch guild name error try using an alt account that has 2FA disabled. 37 | By using this tool, you agree that the creator of this tool has no responsibility for any action taken 38 | against your account or against you legally. 39 | I strongly recommend you to use an alt account's token with this tool. 40 | I strongly recommend you to use Tor, VPN or a proxy along with this tool. 41 | 42 | # TODO 43 | 44 | - [x] Error handling 45 | - [x] Add cooldown for x seconds after x amount of emoji(s) were downloaded 46 | - [x] Sticker support 47 | - [x] (Stickers only) Automatic conversion to conver APNG format to GIF/WEBP 48 | - [ ] Add support for all guilds of a uid, or selection from guilds 49 | - [ ] Built-in proxy support 50 | - [ ] Add versions compiled to executables (like .exe for Windows, elf binaries for Linux, etc) (A GitHub deployment workflow could work here) 51 | - [ ] UI support? 52 | - [ ] Rewrite the code itself to be much more cleaner 53 | - [ ] Merge both emoji and sticker scraper in a single python file 54 | - [ ] Multithreading support 55 | 56 | # Tutorial videos on some stuff 57 | 58 | - How to get guild ID: https://youtu.be/6dqYctHmazc 59 | - How to get discord account token: https://youtu.be/YEgFvgg7ZPI 60 | -------------------------------------------------------------------------------- /emojiscraper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, time 3 | import re, requests 4 | import platform 5 | 6 | warning_info = ''' 7 | WARNING! Using this tool will break the Discord TOS since you will be scraping their website. 8 | By continuing, you agree that the creator of this tool has no responsibility for any action taken 9 | against your account or against you legally. 10 | I strongly recommend you to use an alt account's token with this tool. 11 | I strongly recommend you to use Tor, VPN or a proxy along with this tool. 12 | To start scraping, please type "I UNDERSTAND" and press enter. 13 | ''' 14 | 15 | info = ''' 16 | This bot is made by PreciousWarrior. 17 | You can probably find the repository somewhere in my GitHub profile with a name along the lines of "DiscordEmojiScraper". 18 | If you have any questions create an issue in the github repo. 19 | If you are facing any errors, bugs or having suggestions, create an issue in the GitHub repo. 20 | If you would like to work on any of the TODO points in the code, feel free to work on them and add a PR 21 | ''' 22 | 23 | def is_none_empty_whitespace(any_string): 24 | if any_string == None: 25 | return True 26 | if any_string.strip(" ") == "": 27 | return True 28 | return False 29 | 30 | def get_list_of_emojis(guild_id, token): 31 | result=requests.get(url=f"https://discordapp.com/api/v7/guilds/{guild_id}/emojis", headers={"authorization":token}) 32 | return result.json() 33 | 34 | def scrape_emoji(id, proxy=None): 35 | if is_none_empty_whitespace(proxy): 36 | result=requests.get(url=f"https://cdn.discordapp.com/emojis/{id}") 37 | return result.content 38 | 39 | def get_guild_name(guild_id, token): 40 | result=requests.get(url=f"https://discordapp.com/api/v7/guilds/{guild_id}", headers={"authorization":token}) 41 | if platform.system() == "Windows": 42 | return re.sub(r"[<>:\"/\\|?*]", '-', result.json().get("name")) 43 | else: 44 | return result.json().get("name") 45 | 46 | def get_image_file_extension_from_bytes(image_bytes): 47 | if str(image_bytes)[0:7].find("PNG") != -1: 48 | return ".png" 49 | if str(image_bytes)[0:7].find("GIF") != -1: 50 | return ".gif" 51 | return ".png" 52 | 53 | def save(img_bytes, path): 54 | imagefile = open(path, 'wb') 55 | imagefile.write(img_bytes) 56 | imagefile.close() 57 | 58 | def make_server_dir(server, config): 59 | dir_path = os.path.join(config.get("path"), server) 60 | if not os.path.isdir(dir_path): 61 | os.mkdir(dir_path) 62 | 63 | def try_scraping(guild_name, emoji): 64 | id = emoji.get("id") 65 | name = emoji.get("name") 66 | print(f"Attempting to download :{name}: from {guild_name}") 67 | emoji_bytes = None 68 | while True: 69 | try: 70 | emoji_bytes = scrape_emoji(id) 71 | except Exception as exception: 72 | if exception == KeyboardInterrupt or exception == SystemExit: 73 | print("KeyboardInterrupt/SystemExit caught! Terminating.") 74 | raise 75 | else: 76 | print(f"Failed to get emoji :{name}: from {guild_name} , retrying!") 77 | else: 78 | print(f"Successfully got emoji :{name}: from {guild_name}") 79 | break 80 | return emoji_bytes 81 | 82 | def is_int(str): 83 | try: 84 | int(str) 85 | return True 86 | except ValueError: 87 | return False 88 | 89 | def scrape(config): 90 | if not os.path.isdir(config.get("path")): 91 | os.mkdir(config.get("path")) 92 | servers = config.get("guilds") 93 | for server in servers: 94 | emojis = get_list_of_emojis(server, config.get("token")) 95 | guild_name = get_guild_name(server, config.get("token")) 96 | if guild_name == None or emojis == None: 97 | print(f"Guild {server} is incorrect/doesn't exist/fail to fetch guild name or stickers.") 98 | continue 99 | cooldownsec = config.get("cooldownsec") 100 | make_server_dir(guild_name, config) #TODO technically if the server name had special characters (not-ASCII chars) it's gonna break so add support for that "somehow" 101 | count = 0 102 | print(f"Starting emoji download for server {guild_name}") 103 | for emoji in emojis: 104 | if (not config.get("cooldownperemoji") <= 0) & (count >= config.get("cooldownperemoji")): 105 | print(f"\nCooldown reached! Cooling down for {cooldownsec} seconds.") 106 | time.sleep(cooldownsec) 107 | count = 0 108 | print("Continuing from cooldown\n") 109 | emoji_bytes = try_scraping(guild_name, emoji) 110 | save(emoji_bytes, os.path.join(config.get("path"), guild_name, (emoji.get("name") + get_image_file_extension_from_bytes(emoji_bytes)))) 111 | count += 1 112 | print(f"Finished downloading emojis from {guild_name}") 113 | 114 | def main(): 115 | print(info) 116 | while True: 117 | config = {"token":"", "guilds":[], "path":os.getcwd()+"/Emojis", "cooldownsec":0, "cooldownperemoji":0} 118 | config["token"] = input("Please enter your discord token (https://youtu.be/YEgFvgg7ZPI for help): ") 119 | 120 | while True: 121 | id = input("Enter the guild/server ID you want to scrape for emojis (when you are done, just press enter, https://youtu.be/6dqYctHmazc for help): ") 122 | if is_none_empty_whitespace(id): break 123 | elif not is_int(id): print("Incorrect guild id.") 124 | else: config["guilds"].append(id) 125 | 126 | file_path = input("Where would you like to save your emojis? (Press enter to use the current directory): ") 127 | if not is_none_empty_whitespace(file_path): 128 | config["path"] = file_path 129 | 130 | cooldownperemoji = input("Enter cooldown trigger per emoji (type nothing or 0 to continue without cooldown): ") 131 | if (is_int(cooldownperemoji)): config["cooldownperemoji"] = int(cooldownperemoji) 132 | 133 | if config["cooldownperemoji"] != 0: 134 | cooldownsec = input("Enter the cooldown time in seconds: ") 135 | if (not is_none_empty_whitespace(cooldownsec)) & (not is_int(cooldownsec)): break 136 | else: config["cooldownsec"] = int(cooldownsec) 137 | 138 | print("\n") 139 | print(config) 140 | if is_none_empty_whitespace(input("Are these settings correct? (press enter to continue or anything else to cancel): ")): 141 | if input(warning_info) == "I UNDERSTAND": 142 | scrape(config) 143 | print("Thanks for using the discord emoji scraper! You can find your files at-: " + config["path"]) 144 | break 145 | print("Verification failed.") 146 | 147 | main() 148 | -------------------------------------------------------------------------------- /stickerscraper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, time 3 | import re, requests 4 | from apnggif import apnggif 5 | import platform 6 | 7 | warning_info = ''' 8 | WARNING! Using this tool will break the Discord TOS since you will be scraping their website. 9 | By continuing, you agree that the creator of this tool has no responsibility for any action taken 10 | against your account or against you legally. 11 | I strongly recommend you to use an alt account's token with this tool. 12 | I strongly recommend you to use Tor, VPN or a proxy along with this tool. 13 | To start scraping, please type "I UNDERSTAND" and press enter. 14 | ''' 15 | 16 | info = ''' 17 | This bot is made by PreciousWarrior. 18 | You can probably find the repository somewhere in my GitHub profile with a name along the lines of "DiscordEmojiScraper". 19 | If you have any questions create an issue in the github repo. 20 | If you are facing any errors, bugs or having suggestions, create an issue in the GitHub repo. 21 | If you would like to work on any of the TODO points in the code, feel free to work on them and add a PR 22 | ''' 23 | 24 | def is_none_empty_whitespace(any_string): 25 | if any_string == None: 26 | return True 27 | if any_string.strip(" ") == "": 28 | return True 29 | return False 30 | 31 | def get_list_of_stickers(guild_id, token): 32 | result=requests.get(url=f"https://discordapp.com/api/v9/guilds/{guild_id}/stickers", headers={"authorization":token}) 33 | return result.json() 34 | 35 | def scrape_sticker(id, proxy=None): 36 | if is_none_empty_whitespace(proxy): 37 | result=requests.get(url=f"https://media.discordapp.net/stickers/{id}") 38 | return result.content 39 | 40 | def get_guild_name(guild_id, token): 41 | result=requests.get(url=f"https://discordapp.com/api/v7/guilds/{guild_id}", headers={"authorization":token}) 42 | if platform.system() == "Windows": 43 | return re.sub(r"[<>:\"/\\|?*]", '-', result.json().get("name")) 44 | else: 45 | return result.json().get("name") 46 | 47 | def save(img_bytes, path): 48 | imagefile = open(path, 'wb') 49 | imagefile.write(img_bytes) 50 | imagefile.close() 51 | 52 | def make_server_dir(server, config): 53 | dir_path = os.path.join(config.get("path"), server) 54 | if not os.path.isdir(dir_path): 55 | os.mkdir(dir_path) 56 | 57 | def is_int(str): 58 | try: 59 | int(str) 60 | return True 61 | except ValueError: 62 | return False 63 | 64 | def try_scraping(guild_name, sticker): 65 | id = sticker.get("id") 66 | name = sticker.get("name") 67 | print(f"Attempting to download '{name}' from {guild_name}") 68 | sticker_bytes = None 69 | while True: 70 | try: 71 | sticker_bytes = scrape_sticker(id) 72 | except Exception as exception: 73 | if exception == KeyboardInterrupt or exception == SystemExit: 74 | print("KeyboardInterrupt/SystemExit caught! Terminating.") 75 | raise 76 | else: 77 | print(f"Failed to get emoji '{name}' from {guild_name} , retrying!") 78 | else: 79 | print(f"Successfully got emoji '{name}' from {guild_name}") 80 | break 81 | return sticker_bytes 82 | 83 | def scrape(config): 84 | if not os.path.isdir(config.get("path")): 85 | os.mkdir(config.get("path")) 86 | servers = config.get("guilds") 87 | for server in servers: 88 | stickers = get_list_of_stickers(server, config.get("token")) 89 | guild_name = get_guild_name(server, config.get("token")) 90 | if guild_name == None or stickers == None: 91 | print(f"Guild {server} is incorrect/doesn't exist/fail to fetch guild name or stickers.") 92 | continue 93 | cooldownsec = config.get("cooldownsec") 94 | make_server_dir(guild_name, config) #TODO technically if the server name had special characters (not-ASCII chars) it's gonna break so add support for that "somehow" 95 | count = 0 96 | print(f"Starting sticker download for server {guild_name}") 97 | for sticker in stickers: 98 | if (not config.get("cooldownpersticker") <= 0) & (count >= config.get("cooldownpersticker")): 99 | print(f"\nCooldown reached! Cooling down for {cooldownsec} seconds.") 100 | time.sleep(cooldownsec) 101 | count = 0 102 | print("Continuing from cooldown\n") 103 | sticker_bytes = try_scraping(guild_name, sticker) 104 | if sticker.get("format_type") == 1: 105 | save(sticker_bytes, os.path.join(config.get("path"), guild_name, (sticker.get("name") + ".png"))) 106 | else: 107 | if config.get("convertapngtogif"): 108 | pathorigin = os.path.join(config.get("path"), guild_name, (sticker.get("name") + "_original.apng")) 109 | pathconv = os.path.join(config.get("path"), guild_name, (sticker.get("name") + ".gif")) 110 | stickername = sticker.get("name") 111 | save(sticker_bytes, pathorigin) 112 | apnggif(pathorigin, pathconv) 113 | print(f"Converted animated sticker '{stickername} from {guild_name}'") 114 | else: 115 | save(sticker_bytes, os.path.join(config.get("path"), guild_name, (sticker.get("name") + ".apng"))) 116 | count += 1 117 | print(f"Finished downloading stickers from {guild_name}") 118 | 119 | def main(): 120 | print(info) 121 | while True: 122 | config = {"token":"", "guilds":[], "path":os.getcwd()+"/Stickers", "cooldownsec":0, "cooldownpersticker":0, "convertapngtogif":False} 123 | config["token"] = input("Please enter your discord token (https://youtu.be/YEgFvgg7ZPI for help): ") 124 | 125 | while True: 126 | id = input("Enter the guild/server ID you want to scrape for stickers (when you are done, just press enter, https://youtu.be/6dqYctHmazc for help): ") 127 | if is_none_empty_whitespace(id): break 128 | elif not is_int(id): print("Incorrect guild id.") 129 | else: config["guilds"].append(id) 130 | 131 | file_path = input("Where would you like to save your stickers? (Press enter to use the current directory): ") 132 | if not is_none_empty_whitespace(file_path): 133 | config["path"] = file_path 134 | 135 | cooldownperemoji = input("Enter cooldown trigger per sticker (type nothing or 0 to continue without cooldown): ") 136 | if (is_int(cooldownperemoji)): config["cooldownpersticker"] = int(cooldownperemoji) 137 | 138 | if config["cooldownpersticker"] != 0: 139 | cooldownsec = input("Enter the cooldown time in seconds: ") 140 | if (not is_none_empty_whitespace(cooldownsec)) & (not is_int(cooldownsec)): break 141 | else: config["cooldownsec"] = int(cooldownsec) 142 | 143 | convertapngtogif = input("Would you like to convert apng files (animated stickers) to gif? Leave blank to disable (if this is enabled then original animated sticker files will have '_original' appended between their name and the file extension): ") 144 | if not is_none_empty_whitespace(convertapngtogif): 145 | config["convertapngtogif"] = True 146 | 147 | print("\n") 148 | print(config) 149 | if is_none_empty_whitespace(input("Are these settings correct? (press enter to continue or anything else to cancel): ")): 150 | if input(warning_info) == "I UNDERSTAND": 151 | scrape(config) 152 | print("Thanks for using the discord emoji scraper! You can find your files at-: " + config["path"]) 153 | break 154 | print("Verification failed.") 155 | 156 | main() 157 | --------------------------------------------------------------------------------