Browse Source

Initial commit

master
noirscape 2 years ago
commit
82c5e32bc1
Signed by: noirscape GPG Key ID: 38216818B8894B9F
5 changed files with 156 additions and 0 deletions
  1. +2
    -0
      .gitignore
  2. +55
    -0
      README.md
  3. +8
    -0
      config.example.ini
  4. +90
    -0
      main.py
  5. +1
    -0
      requirements.txt

+ 2
- 0
.gitignore View File

@ -0,0 +1,2 @@
.vscode
config.ini

+ 55
- 0
README.md View File

@ -0,0 +1,55 @@
## avatar-cloner
Discord bot to track someones avatar changes by downloading and reuploading them to a dedicated channel whenever it changes.
## Usage
- Fill out `config.example.ini` and rename it to `config.ini` (see below for config keys). Everything has to be filled out.
- Install `requirements.txt`
- Run the code with `python main.py`.
**Caveat: The bot has to be able to _see_ the users you're monitoring, otherwise their avatars can't be updated.**
**Caveat: The bot cannot track other bots. This is because they don't emit on_user_update events.**
## Config keys
### Bot
- token: Discord token for the bot
- prefix: Prefix used for the commands
- owner: User ID that "owns" the bot. The "owner" is the only user that can run commands on the bot.
### Tracking
- user: User ID(s) to watch. Can watch multiple IDs, separate IDs by spaces.
- channel: Channel ID to send profile images to.
## Commands
Note: `[p]` in the below commands is replaced with the bots prefix.
### save_user
Example:
```
[p]save_user [ID]
```
### Arguments
- **ID**: User ID to save
### Explanation
Saves the avatar ID that you pass in. Can be used to save current avatars or avatars from users the bot isn't tracking.
This command can be used to save bot avatars.
## License
Copyright (c) 2019 Valentijn "noirscape" V.
All rights reserved. No warranty, explicit or implicit, provided.
Unauthorized copying of this file, via any medium is strictly prohibited.

+ 8
- 0
config.example.ini View File

@ -0,0 +1,8 @@
[bot]
token=
prefix=
owner=
[tracking]
user=
channel=

+ 90
- 0
main.py View File

@ -0,0 +1,90 @@
# Copyright (c) 2019 Valentijn "noirscape" V.
# All rights reserved. No warranty, explicit or implicit, provided.
# Unauthorized copying of this file, via any medium is strictly prohibited.
# Standard library
import configparser
import datetime
import io
import logging.config
# External libraries
import discord
# Set up logging
logging.basicConfig(level=logging.DEBUG)
logging.config.dictConfig({'version': 1, 'disable_existing_loggers': True})
# Load config
config = configparser.ConfigParser()
config.read("config.ini")
# Assign some important constants for the config
TOKEN=config["bot"]["token"]
PREFIX=config["bot"]["prefix"]
USER_IDS=[int(x) for x in config["tracking"]["user"].split()]
CHANNEL_ID=int(config["tracking"]["channel"])
client = discord.Client(max_messages=1, activity=discord.Game("Starting up...")) # We don't need the message cache for this bot.
# Define the saving the avatar method
# user_id argument: the user id that will be changed/used
async def save_avatar(user_id):
user = client.get_user(user_id)
channel = client.get_channel(CHANNEL_ID)
avatar_asset = user.avatar_url_as(static_format="png", size=4096) # Explanation: webp is an annoying format. 4096 is the biggest possible avatar size.
avatar_fp = io.BytesIO()
logging.info("Saving avatar to bytesIO")
await avatar_asset.save(avatar_fp)
logging.info("Done saving avatar")
if user.is_avatar_animated(): # If avatar is animated, we need a different filename.
filename ="ava_{}.gif".format(user.avatar)
logging.debug("Created new bytesIO with filename %s", filename)
up_file = discord.File(avatar_fp, filename=filename)
else:
filename ="ava_{}.png".format(user.avatar)
logging.debug("Created new bytesIO with filename %s", filename)
up_file = discord.File(avatar_fp, filename=filename)
message = "{} - Stored avatar on: {}".format(user, datetime.datetime.now().strftime("%H:%M:%S on %B %d, %Y"))
logging.info("Sending message to channel.")
await channel.send(message, file=up_file)
logging.info("Message send to channel.")
# Define events to listen to
@client.event
async def on_message(message: discord.Message):
if message.author == client.user:
return # bot-ception prevention
if message.content:
logging.debug("Message received. Contents: %s", message.content)
logging.debug("Valid command contents: %ssave_user [ID]", PREFIX)
if message.content.startswith(PREFIX + "save_user"): # Save current avatar
split_message = message.content.split()
if len(split_message) != 2:
return await message.channel.send("You need to specify an ID!")
logging.info("Saving avatar for %s", split_message[1])
await save_avatar(int(split_message[1]))
@client.event
async def on_user_update(before: discord.User, after: discord.User):
logging.debug("User update detected: %s", after)
logging.debug("User ID: %s (Tracking IDs: %s)", after.id, USER_IDS)
if after.id in USER_IDS:
logging.debug("User ID matched.")
if before.avatar != after.avatar:
logging.info("Detected avatar for ID %s changed. Proceeding with saving.", after.id)
await save_avatar(after.id)
@client.event
async def on_ready():
if len(USER_IDS) > 1:
user = f"{len(USER_IDS)} users"
else:
user = client.get_user(USER_IDS[0])
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name=f"over {user}"))
client.run(TOKEN)

+ 1
- 0
requirements.txt View File

@ -0,0 +1 @@
discord.py

Loading…
Cancel
Save