Since a LOT of people like the way it looks, and wanted to know how to make it, here is the code :3
Preview
import disnake | |
from disnake.ext import commands | |
from PIL import Image, ImageFont, ImageDraw, ImageStat | |
import textwrap | |
from io import BytesIO | |
import numpy as np | |
# Credit for the ascii art (it's slightly modified though) -> https://www.geeksforgeeks.org/converting-image-ascii-image-python/ | |
gscale1 = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. " | |
gscale2 = ' .:-=+*#%@' | |
def getAverageL(image: Image): | |
im = np.array(image) | |
w,h = im.shape | |
return np.average(im.reshape(w*h)) | |
def covertImageToAscii(IMAGE: Image, cols: int, scale: float, moreLevels: bool): | |
global gscale1, gscale2 | |
image = IMAGE.convert('L') | |
W, H = image.size | |
w = W/cols | |
h = w/scale | |
rows = int(H/h) | |
if cols > W or rows > H: | |
return None | |
aimg = [] | |
for j in range(rows): | |
y1 = int(j*h) | |
y2 = int((j+1)*h) | |
if j == rows-1: | |
y2 = H | |
aimg.append("") | |
for i in range(cols): | |
x1 = int(i*w) | |
x2 = int((i+1)*w) | |
if i == cols-1: | |
x2 = W | |
img = image.crop((x1, y1, x2, y2)) | |
avg = int(getAverageL(img)) | |
if moreLevels: | |
gsval = gscale1[int((avg*69)/255)] | |
else: | |
gsval = gscale2[int((avg*9)/255)] | |
aimg[j] += gsval | |
return aimg | |
def ascii_art(image: BytesIO, more_levels = False): | |
scale = .65 | |
cols = 100 | |
image = Image.open(image) | |
aimg = covertImageToAscii( image, cols, scale, more_levels ) | |
return aimg | |
def brightness_level(image: Image.Image): | |
image = image.convert('L') | |
stat = ImageStat.Stat(image) | |
return stat.mean[0] | |
def create_welcome(user:disnake.Member, user_avatar, join_pos): | |
canvas = Image.new('RGB', (600, 250), (0,0,0,1)) | |
image_array = [canvas] | |
text_color = 108, 247, 80 | |
info_font = ImageFont.truetype("assets/fonts/terminal.ttf", size=15) | |
welcome_font = ImageFont.truetype("assets/fonts/terminal.ttf", size=20) | |
name_font = ImageFont.truetype("assets/fonts/terminal.ttf", size=30) | |
ascii_font = ImageFont.truetype("assets/fonts/terminal.ttf", size=3) | |
WELCOME_TEXT = "Welcome to BytesToBits," | |
NAME_TEXT = user.name | |
INFO_TEXT = [ | |
f"Subject ID: #{str(join_pos).zfill(4)}", | |
user.created_at.strftime("Since %B %d, %Y"), | |
f"Status: {user.status.name.upper()}", | |
f"Activity:", | |
] + textwrap.wrap(user.activity.name if user.activity else "UNDETECTED", 25) | |
for letter_range in range(len(WELCOME_TEXT)): | |
im = image_array[-1].copy() | |
draw = ImageDraw.Draw(im) | |
draw.text((5+(welcome_font.size-5)*letter_range, 5), WELCOME_TEXT[letter_range], fill=text_color, font=welcome_font) | |
image_array.append(im) | |
# DELAY BEFORE WRITING THE NAME | |
image_array += [image_array[-1] for _ in range(2)] | |
for letter_range in range(len(NAME_TEXT)): | |
im = image_array[-1].copy() | |
draw = ImageDraw.Draw(im) | |
draw.text((5+(name_font.size-10)*letter_range, 30), NAME_TEXT[letter_range], fill=text_color, font=name_font) | |
image_array.append(im) | |
# DELAY BEFORE WRITING THE INFO | |
image_array += [image_array[-1] for _ in range(2)] | |
for (index, line) in enumerate(INFO_TEXT): | |
for letter_range in range(len(line)): | |
im = image_array[-1].copy() | |
draw = ImageDraw.Draw(im) | |
draw.text((5+(info_font.size-5)*letter_range, 80+20*index), line[letter_range], fill=text_color, font=info_font) | |
image_array.append(im) | |
# DELAY BEFORE DRAWING THE IMAGE | |
image_array += [image_array[-1] for _ in range(2)] | |
brightness = brightness_level(Image.open(BytesIO(user_avatar))) | |
ascii = ascii_art(BytesIO(user_avatar), more_levels=brightness>170) | |
if not ascii: return | |
ascii_len = len(ascii)*ascii_font.size | |
ascii_image = Image.new('RGB', (ascii_len, ascii_len), (0,0,0)) | |
draw = ImageDraw.Draw(ascii_image) | |
for (index, line) in enumerate(ascii): | |
im = ascii_image.copy() | |
draw = ImageDraw.Draw(im) | |
# draw.text((L_MARGIN, T_MARGIN+ascii_font.size*index), line, fill=text_color, font=ascii_font) | |
draw.text((0, ascii_font.size*index), line, fill=text_color, font=ascii_font) | |
ascii_image = im | |
im = image_array[-1].copy() | |
im.paste(ascii_image.resize((250,250)), (350, 0)) | |
image_array.append(im) | |
img = BytesIO() | |
canvas.save(img, "GIF", save_all=True, append_images=image_array[1:], duration=50) | |
img.seek(0) | |
return img | |
class WelcomeBanner(commands.Cog): | |
def __init__(self, bot: commands.Bot): | |
self.bot = bot | |
@commands.Cog.listener() | |
async def on_member_join(self, member: disnake.Member): | |
avatar = await member.display_avatar.read() | |
# Do it like this because the welcome image might take some time to make, and the bot would freeze otherwize. | |
welcome_image = await self.bot.loop.run_in_executor(None, lambda: create_welcome(member, avatar, member.guild.member_count)) | |
await member.guild.text_channels[0].send(file=disnake.File(welcome_image, "welcome.gif")) |