Initial commit, already feature complete.
This commit is contained in:
commit
af0d6cd2c0
7 changed files with 256 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Ignore python cache
|
||||||
|
**/*.pyc
|
||||||
|
**/__pycache__/*
|
||||||
|
|
||||||
|
# Ignore python virtual environment
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# Ignore log files
|
||||||
|
**/*.log
|
||||||
|
|
||||||
|
# Ignore config file
|
||||||
|
**/config.json
|
1
.python-version
Normal file
1
.python-version
Normal file
|
@ -0,0 +1 @@
|
||||||
|
3.11
|
57
README.md
Normal file
57
README.md
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# Discord emoji uploader
|
||||||
|
|
||||||
|
I just wanna put my emojis on my Discord server.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### (Optional) Set up a virtual environment
|
||||||
|
|
||||||
|
```
|
||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install the required packages
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Edit the config
|
||||||
|
|
||||||
|
Rename `sample-config.json` to `config.json`.
|
||||||
|
|
||||||
|
```
|
||||||
|
mv sample-config.json config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, edit in your API Key and Guild ID.
|
||||||
|
|
||||||
|
### (Optional) Add emoji files
|
||||||
|
|
||||||
|
Any images in the `emojis/` directory will be uploaded by default.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Assuming your `config.json` is in the default place at the root of the
|
||||||
|
project directory, and the emoji images you want to upload are in `emoji/`,
|
||||||
|
you do not need to add any arguments. You can, however, run
|
||||||
|
`upload-emoji.py -h` for a usage printout.
|
||||||
|
|
||||||
|
You can specify a config file with `-c`.
|
||||||
|
|
||||||
|
```
|
||||||
|
upload-emoji.py -c some/path/to/config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also specify a directory for emojis.
|
||||||
|
|
||||||
|
```
|
||||||
|
upload-emoji.py -e some/path/to/emoji/images/
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want more verbose logs, you can use the `-v` flag.
|
||||||
|
|
||||||
|
```
|
||||||
|
upload-emoji.py -v
|
||||||
|
```
|
0
emojis/.gitkeep
Normal file
0
emojis/.gitkeep
Normal file
11
requirements.txt
Normal file
11
requirements.txt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
aiohappyeyeballs==2.6.1
|
||||||
|
aiohttp==3.11.16
|
||||||
|
aiosignal==1.3.2
|
||||||
|
attrs==25.3.0
|
||||||
|
discord==2.3.2
|
||||||
|
discord.py==2.5.2
|
||||||
|
frozenlist==1.5.0
|
||||||
|
idna==3.10
|
||||||
|
multidict==6.2.0
|
||||||
|
propcache==0.3.1
|
||||||
|
yarl==1.19.0
|
4
sample-config.json
Normal file
4
sample-config.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"api_key": "(Your big api_key from https://discord.com/developers)",
|
||||||
|
"guild_id": "0000000000000000000"
|
||||||
|
}
|
171
upload-emoji.py
Executable file
171
upload-emoji.py
Executable file
|
@ -0,0 +1,171 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from argparse import ArgumentParser, Namespace
|
||||||
|
|
||||||
|
from json import load
|
||||||
|
from pathlib import Path
|
||||||
|
from logging import error, info, debug, INFO, DEBUG, FileHandler
|
||||||
|
from sys import exit
|
||||||
|
from os import listdir
|
||||||
|
from os.path import splitext
|
||||||
|
from discord import Client, Intents
|
||||||
|
|
||||||
|
# Set default paths for config and emoji dir, and supported file formats
|
||||||
|
DEFAULT_CONFIG_PATH = "config.json"
|
||||||
|
DEFAULT_EMOJI_DIR = "emojis/"
|
||||||
|
SUPPORTED_FORMATS = [".jpeg", ".jpg", ".png", ".gif"]
|
||||||
|
|
||||||
|
|
||||||
|
class EmojiUploadClient(Client):
|
||||||
|
emoji_dir: Path = Path("")
|
||||||
|
guild_id: str = ""
|
||||||
|
|
||||||
|
async def on_ready(self):
|
||||||
|
"""Key function to run upon startup."""
|
||||||
|
|
||||||
|
info(f"Logged on as {self.user}")
|
||||||
|
|
||||||
|
# Attempt to upload emoji after startup.
|
||||||
|
await self.upload_emoji()
|
||||||
|
|
||||||
|
async def upload_emoji(self):
|
||||||
|
"""Uses emoji_dir and guild_id to upload all emoji in the directory
|
||||||
|
to the specified guild.
|
||||||
|
"""
|
||||||
|
|
||||||
|
info(f'Attempting to upload emoji at "{self.emoji_dir}"...')
|
||||||
|
|
||||||
|
# Check guild_id is numeric.
|
||||||
|
if not self.guild_id.isnumeric():
|
||||||
|
error(f"Not a proper guild_id: {self.guild_id}")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# Obtain guild object.
|
||||||
|
guild = self.get_guild(int(self.guild_id))
|
||||||
|
|
||||||
|
# Grab list of emoji files.
|
||||||
|
emoji_files = listdir(self.emoji_dir)
|
||||||
|
|
||||||
|
# Iterate through emojis and attempt to upload them.
|
||||||
|
for emoji in emoji_files:
|
||||||
|
current_emoji_filepath = f"{self.emoji_dir}/{emoji}"
|
||||||
|
(file_name, file_extension) = splitext(emoji)
|
||||||
|
|
||||||
|
# Validate that the image is a supported format.
|
||||||
|
if file_extension not in SUPPORTED_FORMATS:
|
||||||
|
debug(f'Skipping: "{current_emoji_filepath}"')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Try to upload the custom emoji.
|
||||||
|
debug(f"Attempting to upload: {current_emoji_filepath}")
|
||||||
|
with open(current_emoji_filepath, "rb") as img:
|
||||||
|
await guild.create_custom_emoji(name=file_name, image=img.read())
|
||||||
|
|
||||||
|
# Disconnect the client.
|
||||||
|
info("All emojis attempted, disconnecting.")
|
||||||
|
await self.close()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_config(config_file: Path) -> tuple:
|
||||||
|
try:
|
||||||
|
with open(config_file) as config:
|
||||||
|
parse_config = load(config)
|
||||||
|
return (parse_config["api_key"], parse_config["guild_id"])
|
||||||
|
except Exception:
|
||||||
|
error(f"There was an error parsing the config file at: {config_file}")
|
||||||
|
exit(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def parse_opts(opts: Namespace) -> tuple:
|
||||||
|
"""Parse out opts for config and emoji paths.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
opts (Namespace): Opts object to be parsed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(config_path, emoji_dir): Tuple of opts paths parsed.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
info(
|
||||||
|
f"""
|
||||||
|
config_file: {opts.config_file}
|
||||||
|
emoji_dir: {opts.emoji_dir}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
config_file = Path(opts.config_file)
|
||||||
|
emoji_dir = Path(opts.emoji_dir)
|
||||||
|
except Exception:
|
||||||
|
error(f"{Exception}\nCould not successfully parse arguments.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if not config_file.exists():
|
||||||
|
error(f'No config file found at "{config_file}".')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if not emoji_dir.exists():
|
||||||
|
error(f'"{emoji_dir}" is not a valid directory.')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if opts.verbose:
|
||||||
|
log_level = DEBUG
|
||||||
|
else:
|
||||||
|
log_level = INFO
|
||||||
|
|
||||||
|
return (config_file, emoji_dir, log_level)
|
||||||
|
|
||||||
|
|
||||||
|
def make_args() -> ArgumentParser:
|
||||||
|
"""Create an ArgumentParser object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ArgumentParser: ArgumentParser object
|
||||||
|
"""
|
||||||
|
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--config-file",
|
||||||
|
default=DEFAULT_CONFIG_PATH,
|
||||||
|
help=f"Path to config json, defaults to: {DEFAULT_CONFIG_PATH}",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-e",
|
||||||
|
"--emoji-dir",
|
||||||
|
default=DEFAULT_EMOJI_DIR,
|
||||||
|
help=f"Path to emoji directory, defaults to: {DEFAULT_EMOJI_DIR}",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-v",
|
||||||
|
"--verbose",
|
||||||
|
action="store_true",
|
||||||
|
help="Sets the logging level to DEBUG.",
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None, print_stdout: bool | None = True):
|
||||||
|
args = make_args()
|
||||||
|
opts = args.parse_args(argv)
|
||||||
|
|
||||||
|
# Parse out the config filepath, emoji directory, and verbose flag.
|
||||||
|
config_file, emoji_dir, log_level = parse_opts(opts)
|
||||||
|
|
||||||
|
# Parse out the api key and the guild id.
|
||||||
|
(api_key, guild_id) = parse_config(config_file)
|
||||||
|
|
||||||
|
# Create Discord API Client object and set properties.
|
||||||
|
client = EmojiUploadClient(intents=Intents.default())
|
||||||
|
client.emoji_dir = emoji_dir
|
||||||
|
client.guild_id = guild_id
|
||||||
|
|
||||||
|
# Set up logging for Discord API Client
|
||||||
|
handler = FileHandler(filename="emoji_uploader.log", encoding="utf-8", mode="w")
|
||||||
|
|
||||||
|
# Run the Discord API Client.
|
||||||
|
client.run(api_key, log_handler=handler, root_logger=True, log_level=log_level)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in a new issue