Last active
April 24, 2023 00:47
-
-
Save wisheth/eaf9516164240563b7d917113766a712 to your computer and use it in GitHub Desktop.
Grimoire of Crimson, a simple Discord channel archiving bot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import discord | |
from discord.ext import commands | |
import typing | |
import logging | |
import os | |
import time | |
from datetime import datetime | |
import string | |
import random | |
import dotenv | |
dotenv.load_dotenv() | |
logger = logging.getLogger('discord') | |
logger.setLevel(logging.DEBUG) | |
handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w') | |
handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s')) | |
logger.addHandler(handler) | |
bot = commands.Bot(command_prefix='g!') | |
@bot.event | |
async def on_ready(): | |
print(f"Bot live | {bot.user} | {bot.user.id}") | |
datetimeFormatter = lambda dt: dt.strftime("%Y-%m-%d %H:%M") | |
@bot.slash_command(description="Adds the channel to the Grimoire's internal archives.", guild_only=True) | |
async def archive(ctx, thread: typing.Optional[bool] = False, channels: typing.Optional[str] = ""): | |
"""Adds channel(s) to the Grimoire's internal archives. | |
Parameters | |
----------- | |
thread: typing.Optional[bool] | |
Set to True if the channels to be archived are threads. | |
channels: typing.Optional[str] | |
A list of IDs of channels to archive separated by spaces (current channel by default). | |
""" | |
if not await ctx.bot.is_owner(ctx.author): | |
await ctx.respond("`Please ask the bot owner to use this command if a channel needs archiving.`") | |
return | |
if len(channels) > 0: | |
channelList = map(int, channels.strip().split(" ")) | |
await ctx.respond("`Bot has received your request and is archiving all of the channels listed by ID. Please be patient; you will be pinged when the process completes.`") | |
else: | |
channelList = [ctx.channel_id] | |
await ctx.respond("`Bot has received your request and is archiving this channel. Please be patient; you will be pinged when the process completes.`") | |
print(f"Started job in guild with ID {ctx.guild.id}") | |
async with ctx.channel.typing(): | |
jobStart = time.time() | |
embed = discord.Embed(title="Archive complete",description="All archives are located in the archive.txt file") | |
for channel in channelList: | |
print(f"Archiving channel with ID {channel}") | |
channelStart = time.time() | |
if thread: | |
ch = bot.get_thread(channel) | |
else: | |
ch = bot.get_channel(channel) | |
if ch.guild != ctx.guild: | |
embed.add_field(name=f"Channel {channel}", value="No channel with such ID is present in this guild", inline=True) | |
continue | |
path = os.path.join("archives",f"{ctx.guild.id}",f"{ch.category.id}") | |
if thread: | |
path = os.path.join(path,f"{ch.parent.name}@{ch.parent.id}",f"{ch.name}@{ch.id}") | |
else: | |
path = os.path.join(path,f"{ch.name}@{ch.id}") | |
if not os.path.isdir(os.path.join(os.path.dirname(__file__),path)): | |
os.makedirs(os.path.join(os.path.dirname(__file__),path)) | |
with open(os.path.join(os.path.dirname(__file__),path,"archive.txt"), "w", encoding="utf-8") as f: | |
f.write(f"[Channel archived by bot owner request at {datetimeFormatter(datetime.now())}]\n") | |
async for msg in ch.history(limit=None, oldest_first=True): | |
formatted = f"[{msg.id}] <{datetimeFormatter(msg.created_at)}> | {msg.author.name}#{msg.author.discriminator} ({msg.author.id}): {msg.content}".strip() | |
for st in msg.stickers: | |
formatted += f""" (Sticker: "{st.name}")""" | |
for at in msg.attachments: | |
filenameparts = at.filename.split('.') | |
filenameparts = ['.'.join(filenameparts[:-1]), filenameparts[-1]] | |
if len(filenameparts[0]) > 128: | |
filenameparts[0] = filenameparts[0][:128] | |
filename= f"{filenameparts[0]}.{filenameparts[1]}" | |
while os.path.isfile(os.path.join(os.path.dirname(__file__),path,filename)): | |
filename = f"{filenameparts[0]}-{''.join([random.choice(string.ascii_letters) for _ in range(4)])}.{filenameparts[1]}" | |
try: | |
await at.save(os.path.join(os.path.dirname(__file__),path,filename)) | |
except FileNotFoundError as e: | |
print(f"Caught FileNotFoundError for path {os.path.join(os.path.dirname(__file__),path,filename)}") | |
formatted += f" (File: {filename})" | |
formatted += "\n" | |
f.write(formatted) | |
embed.add_field(name=f"{ch.mention}", value=f"Successfully archived in {'%.3f'%(time.time()-channelStart)} seconds", inline=True) | |
embed.set_footer(text=f"Process completed in {'%.3f'%(time.time()-jobStart)} seconds") | |
print(f"Finished job in guild with ID {ctx.guild.id}") | |
await ctx.send(ctx.user.mention, embed=embed) | |
bot.run(os.getenv("BOT_TOKEN")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment