Skip to content

Instantly share code, notes, and snippets.

@wisheth
Last active April 24, 2023 00:47
Show Gist options
  • Save wisheth/eaf9516164240563b7d917113766a712 to your computer and use it in GitHub Desktop.
Save wisheth/eaf9516164240563b7d917113766a712 to your computer and use it in GitHub Desktop.
Grimoire of Crimson, a simple Discord channel archiving bot
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