# Ensō~Chan - A Multi Purpose Discord Bot That Has Everything Your Server Needs!
# Copyright (C) 2020 Hamothy
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import datetime
import io
import random
import string
import textwrap
import urllib . parse
from typing import Optional
import aiohttp
import discord
from PIL import Image , ImageDraw , ImageFont
from PIL . ImageOps import invert
from aiohttp import request
from discord import Member , Embed
from discord . ext import commands
from discord . ext . commands import BucketType , cooldown , command , Cog
from discord . ext . commands import is_owner , bot_has_permissions
from owotext import OwO
def generate_meme ( image_path , top_text , bottom_text = ' ' , font_path = ' images/homies/impact/Impacted.ttf ' , font_size = 9 ) :
get_image = Image . open ( image_path )
draw = ImageDraw . Draw ( get_image )
image_width , image_height = get_image . size
# Load font
font = ImageFont . truetype ( font = font_path , size = int ( image_height * font_size ) / / 100 )
# Convert text to uppercase
top_text = top_text . upper ( )
bottom_text = bottom_text . upper ( )
# Text wrapping
char_width , char_height = font . getsize ( ' A ' )
chars_per_line = image_width / / char_width
top_lines = textwrap . wrap ( top_text , width = chars_per_line )
bottom_lines = textwrap . wrap ( bottom_text , width = chars_per_line )
# Draw top lines
y = 10
for line in top_lines :
line_width , line_height = font . getsize ( line )
x = ( image_width - line_width ) / 2
draw . text ( ( x , y ) , line , fill = ' white ' , font = font )
y + = line_height
# Draw bottom lines
y = image_height - char_height * len ( bottom_lines ) - 15
for line in bottom_lines :
line_width , line_height = font . getsize ( line )
x = ( image_width - line_width ) / 2
draw . text ( ( x , y ) , line , fill = ' white ' , font = font )
y + = line_height
# Save meme as bytes
file = io . BytesIO ( )
get_image . save ( file , format = ' PNG ' )
file . seek ( 0 )
return file
# Set up the cog
class Fun ( Cog ) :
""" Fun Commands! (8ball, Doggo etc!) """
def __init__ ( self , bot ) :
self . bot = bot
@Cog.listener ( )
async def on_ready ( self ) :
""" Printing out that Cog is ready on startup """
print ( f " { self . __class__ . __name__ } Cog has been loaded \n ----- " )
@command ( name = " attack " , hidden = True )
@is_owner ( )
async def attack ( self , ctx , member : Member ) :
""" Throw Insults at Members """
# Set up array of insults to throw at people
responses = [
f " { member . mention } is stinky " ,
f " { member . mention } is ugly " ,
f " { member . mention } has a gigantic nose " ,
f " { member . mention } gets no views on their tiktok " ,
f " { member . mention } is obviously compensating for something :eyes: " ,
f " { member . mention } DIE DIE DIE :knife: :skull: " ,
f " { member . mention } is so annoying smh :rolling_eyes: " ,
f " I ' d say { member . mention } was dropped as a child but they would have to be held to be dropped in the first place " ,
f " I hate { member . mention } " ,
f " { member . mention } close your legs, it smells like clam chowder :face_vomiting: :face_vomiting: :nauseated_face: :nauseated_face: " ,
f " I bet { member . mention } can ' t reach the wall cabinets without a booster chair " ,
f " { member . mention } Browses 4Chan and Reddit all day looking for love " ,
f " { member . mention } Your forehead could be used as a landing pad " ,
f " I bet { member . mention } likes eating watermelon with the rind. " ,
f " { member . mention } You were the first creation to make god say oops " ,
f " { member . mention } You have delusions of adequacy " ,
f " { member . mention } I treasure the time I don ' t spend with you " ,
f " Don ' t be ashamed of yourself { member . mention } , that ' s your parent ' s job " ,
f " I don ' t have the energy to pretend I like { member . mention } today " ,
f " I know this was made for me to insult but it’ s kinda hard to be a hateful cunt like { member . mention } :star_struck::star_struck: " ,
f " # { member . mention } IsOverParty " ,
f " I hope { member . mention } drops dead with a curable disease that doctors simply didn’ t feel like curing :) " ,
f " { member . mention } You know there ' s no vaccine for stupidity right? " ,
f " { member . mention } You are not very epic at all " ,
f " You make Kpop Fancams 24/7 for validation on the internet { member . mention } " ,
f " Your mother wanted to drop you on the head when you were little { member . mention } " ,
f " { member . mention } You ' re the CEO of Racism " ,
f " { member . mention } has no common sense "
]
# Sending out a random insult from the array "responses"
await ctx . send ( random . choice ( responses ) )
@command ( name = " comp " )
async def compliment ( self , ctx , member : Member ) :
""" Give Compliments to Members """
# Set up array of compliments to throw at people
responses = [
f " { member . mention } is the most adorable uwu <:awie:676201100793085952> <:awie:676201100793085952> <:awie:676201100793085952> " ,
f " { member . mention } You have my ENTIRE HEART <:blushlook1:677310734123663363> <:blushlook2:679524467248201769> " ,
f " { member . mention } Hun you ' re CUTE uwu :pleading_face: :flushed: :pleading_face: :flushed: :pleading_face: " ,
f " I love { member . mention } so so much :heartbeat: :heartbeat: :heartbeat: " ,
f " My heart is full of love for you { member . mention } <:Kawaii:676203363922214953> <:Kawaii:676203363922214953> " ,
f " { member . mention } I admire your greatness so much that I consider making a fan club to become your #1 fan (´꒳`) " ,
f " { member . mention } has no flaws, only special effects :)) " ,
f " { member . mention } ' s smile is brighter than sunlight, so smile more often ( ◠‿◠ ) " ,
f " { member . mention } Your smile is so beautiful it blinds me :heart_eyes: :heart_eyes: " ,
f " Being on a journey all my life, I will never meet a person as amazing as you are { member . mention } " ,
f " Such a pleasure to be on the same server with { member . mention } <:boneappleteeth:676202300573876252> <:boneappleteeth:676202300573876252> " ,
f " With { member . mention } , even the worst day will be filled with joy <:hug:718248629034549299> <:hug:718248629034549299> " ,
f " There ' s no better antidepressant than { member . mention } " ,
f " { member . mention } You ' re great, keep going Σd(˘ꇴ˘๑) " ,
f " I ' d simp for { member . mention } anyday :flushed: :heart_eyes: :flushed: " ,
f " { member . mention } Even the ugliest clothes won ' t ruin your look (。•̀ᴗ -)☆ " ,
f " { member . mention } You’ re that “nothing” when people ask me what I’ m thinking about <:Kawaii:676203363922214953> <:Kawaii:676203363922214953> " ,
f " { member . mention } Somehow you make time stop and fly at the same time <:awie:676201100793085952> <:blushlook1:677310734123663363> " ,
f " { member . mention } is a whole ass SWAGMEAL <:Kawaii:676203363922214953> <:Kawaii:676203363922214953> " ,
f " After meeting { member . mention } , I couldn ' t imagine living my life without them " ,
f " Take me into your arms and tell me you love me <:blushlook1:677310734123663363> <:blushlook2:679524467248201769> { member . mention } " ,
f " { member . mention } I would spend eternity cuddling with you :flushed: :flushed: " ,
f " Would you want to go on an e-date together? :pleading_face: :point_right: :point_left: { member . mention } " ,
f " Let me shoot my shot to you :see_no_evil: :see_no_evil: { member . mention } " ,
f " Your existence makes me feel so much better { member . mention } " ,
f " You ' re so hot, even hotter than hell :heart_eyes: { member . mention } " ,
f " { member . mention } You’ re so cute that Taz will simp for you anytime :flushed: :heart_eyes: :flushed: " ,
f " { member . mention } The thought of you leaving me is too much to bear. Stay with me forever :pleading_face: :pleading_face: " ,
f " You ' re... You ' re SHREKTACULAR :heart_eyes: :flushed: :heart_eyes: { member . mention } " ,
f " { member . mention } Your beauty renders me speechless... :heart_eyes: :heart_eyes: " ,
f " Your taste in music is impeccable { member . mention } " ,
f " { member . mention } I can ' t stop thinking about you :see_no_evil: :see_no_evil: " ,
f " { member . mention } Your wedding will be wonderful, but the y is silent <a:huh:676195228872474643> <a:huh:676195228872474643> " ,
f " { member . mention } I would give up my lifelong goals just to have a chance with you <a:huh:676195228872474643> <a:huh:676195228872474643> " ,
f " { member . mention } Will you be the **yee** to my **haw**? :pleading_face: :pleading_face: " ,
f " { member . mention } is the definition of perfection :heart_eyes: :heart_eyes: " ,
f " { member . mention } My love for you is bigger than the amount of code Hammy has written <:Kawaii:676203363922214953> <:Kawaii:676203363922214953> <:Kawaii:676203363922214953> " ,
f " { member . mention } Why jump off a cliff when you can jump into my arms :flushed: :flushed: "
]
# Sending out a random compliment from the array "responses"
await ctx . send ( random . choice ( responses ) )
@command ( name = " flip " )
async def flip ( self , ctx ) :
""" Flip a Coin (Huge pp/Smol pp) """
# Define array with only 2 entries to create 50/50 chance
pp_array = [ " Smol pp " , " Huge pp " ]
# Send out one of the responses stored in the array
await ctx . send ( f " { ctx . author . mention } { random . choice ( pp_array ) } " )
@command ( name = " digby " , hidden = True )
@bot_has_permissions ( embed_links = True )
async def digby ( self , ctx ) :
""" Pictures of Digby! """
# Surround with try/except to catch any exceptions that may occur
try :
# Open the file containing the digby images
with open ( ' images/FunCommands/digby.txt ' ) as file :
# Store content of the file in digby_array
digby_array = file . readlines ( )
# Set member as the author
member = ctx . message . author
# Get the member avatar
userAvatar = member . avatar_url
# Set up the embed to display a random image of digby
embed = Embed (
title = f " **A cute picture of Digby!** " ,
colour = self . bot . random_colour ( ) ,
timestamp = datetime . datetime . utcnow ( ) )
embed . set_image ( url = random . choice ( digby_array ) )
embed . set_footer ( text = f " Requested by { member } " , icon_url = userAvatar )
# Send the embedded message to the user
await ctx . send ( embed = embed )
except FileNotFoundError as e :
print ( e )
@command ( name = " doggo " )
@bot_has_permissions ( embed_links = True )
@cooldown ( 1 , 1 , BucketType . user )
async def doggo ( self , ctx , breed : Optional [ str ] = None ) :
""" Pictures of Doggos! """
# Set member as the author
member = ctx . message . author
# Get the member avatar
userAvatar = member . avatar_url
# Initialise array to store doggo pics
b_list = [ ]
# If a breed if specified
if breed :
# Get the lowercase string input
lowercase_breed = breed . lower ( )
# If the user wants to know what breeds there are
if lowercase_breed == " breeds " :
# Get the list of breeds
breed_url = " https://dog.ceo/api/breeds/list/all "
# Using API, retrieve the full list of breeds available
async with request ( " GET " , breed_url , headers = { } ) as response :
if response . status == 200 :
data = await response . json ( )
breed_link = data [ " message " ]
# Store every Doggo in an array
for doggo in breed_link :
b_list . append ( doggo )
# Join together all the breeds into a string
doggo_string = string . capwords ( " , " . join ( b_list ) )
# Tell the user to try the breeds listed below
desc = f " Try the Breeds listed below! \n { doggo_string } "
await self . bot . generate_embed ( ctx , desc = desc )
# If no breed has been specified
else :
# Grab a random image of a doggo with the breed specified
image_url = f " https://dog.ceo/api/breed/ { lowercase_breed } /images/random "
# Using API, retrieve the image of a doggo of the breed specified
async with request ( " GET " , image_url , headers = { } ) as response :
if response . status == 200 :
data = await response . json ( )
image_link = data [ " message " ]
# Set up the embed for a doggo image
doggo_embed = Embed (
title = f " **It ' s a { lowercase_breed . capitalize ( ) } Doggo!!** " ,
colour = self . bot . random_colour ( ) ,
timestamp = datetime . datetime . utcnow ( ) )
doggo_embed . set_image ( url = image_link )
doggo_embed . set_footer ( text = f " Requested by { member } " , icon_url = userAvatar )
# Send the doggo image
await ctx . send ( embed = doggo_embed )
else :
# Send error message that Doggo was not found!
desc = f " Doggo Not Found! \n Please do ** { ctx . prefix } doggo breeds** to see the full list of Doggos! "
await self . bot . generate_embed ( ctx , desc = desc )
else :
# Grab a random image of a doggo of any breed
image_url = " https://dog.ceo/api/breeds/image/random "
# Using API, retrieve the image of a doggo of any breed
async with request ( " GET " , image_url , headers = { } ) as response :
if response . status == 200 :
data = await response . json ( )
image_link = data [ " message " ]
# Set up the embed for a random doggo image
doggo_embed = Embed (
title = f " **Doggo!** " ,
colour = self . bot . random_colour ( ) ,
timestamp = datetime . datetime . utcnow ( ) )
doggo_embed . set_image ( url = image_link )
doggo_embed . set_footer ( text = f " Requested by { member } " , icon_url = userAvatar )
# Send random doggo image to the channel
await ctx . send ( embed = doggo_embed )
else :
# Send error message that Doggo was not found!
desc = f " Doggo Not Found! \n Please do ** { ctx . prefix } doggo breeds** to see the full list of Doggos! "
await self . bot . generate_embed ( ctx , desc = desc )
@command ( name = " 8ball " )
async def _8ball ( self , ctx , * , question ) :
""" 8ball Responses! """
try :
# Make the text readable to the api
eightball_question = urllib . parse . quote ( question )
# Using API, make a connection to 8ball API
async with request ( " GET " , f " https://8ball.delegator.com/magic/JSON/ { eightball_question } " ,
headers = { } ) as response :
# With a successful connection
# Get the answer
if response . status == 200 :
data = await response . json ( )
api_question = data [ " magic " ]
api_answer = api_question [ " answer " ]
await ctx . send ( api_answer )
except commands . BadArgument as e :
raise e
@command ( name = " kpop " )
async def get_kpop_member ( self , ctx ) :
""" Retrieve a random kpop member """
# Request profile information from API
url = " https://apis.duncte123.me/kpop "
async with aiohttp . ClientSession ( ) as session :
async with await session . get ( url = url ) as response :
# When successful, read data from json
if response . status == 200 :
kpop = await response . json ( )
name = kpop [ " data " ] [ " name " ]
band = kpop [ " data " ] [ " band " ]
image_url = kpop [ " data " ] [ " img " ]
id = kpop [ " data " ] [ " id " ]
elif response . status == 422 or response . status == 404 :
await self . bot . generate_embed ( ctx , desc = " **Kpop Member Not Found!** " )
return
elif response . status == 429 :
await self . bot . generate_embed ( ctx ,
desc = " **You are being rate limited! You have spammed it too much :(** " )
return
embed = Embed ( title = name ,
description = band ,
colour = self . bot . random_colour ( ) ,
timestamp = datetime . datetime . utcnow ( ) )
embed . set_image ( url = image_url )
embed . set_footer ( text = f " Internal ID: { id } " )
await ctx . send ( embed = embed )
@command ( name = " insta " , aliases = [ " instagram " ] )
async def insta_info ( self , ctx , * , user_name ) :
""" Retrieve a persons Instagram profile information """
with ctx . typing ( ) :
# Request profile information from API
url = f " https://apis.duncte123.me/insta/ { user_name } "
async with aiohttp . ClientSession ( ) as session :
async with await session . get ( url = url ) as response :
# When successful, read data from json
if response . status == 200 :
insta = await response . json ( )
data = insta [ " user " ]
images = insta [ " images " ]
private = data [ " is_private " ]
verified = data [ " is_verified " ]
full_name = data [ " full_name " ]
username = data [ " username " ]
pfp = data [ " profile_pic_url " ]
followers = data [ " followers " ] [ " count " ]
following = data [ " following " ] [ " count " ]
uploads = data [ " uploads " ] [ " count " ]
biography = data [ " biography " ]
# When profile isn't private, grab the information of their last post
if not private :
image_url = images [ 0 ] [ " url " ]
image_caption = images [ 0 ] [ " caption " ]
# Send error if no instagram profile was found with given username
elif response . status == 422 :
await self . bot . generate_embed ( ctx , desc = " **Instagram Username Not Found!** " )
return
elif response . status == 429 :
await self . bot . generate_embed ( ctx ,
desc = " **You are being rate limited! You have spammed it too much :(** " )
return
# Setting bools to ticks/cross emojis
verif = self . bot . tick if verified else self . bot . cross
priv = self . bot . tick if private else self . bot . cross
# Set the page url to the last post or the profile based on privacy settings
page_url = images [ 0 ] [ " page_url " ] if not private else f " https://www.instagram.com/ { username } / "
desc = f " **Full Name:** { full_name } " \
f " \n **Bio:** { biography } " \
f " \n \n **Verified?:** { verif } | **Private?:** { priv } " \
f " \n **Following:** { following } | **Followers:** { followers } " \
f " \n **Upload Count:** { uploads } "
embed = discord . Embed ( title = f " { username } ' s Instagram " ,
description = desc ,
url = page_url ,
colour = self . bot . random_colour ( ) ,
timestamp = datetime . datetime . utcnow ( ) )
embed . set_thumbnail ( url = pfp )
embed . set_footer ( text = f " Requested By { ctx . author } " , icon_url = ctx . author . avatar_url )
# When profile is not private, display the last post with the caption
if not private :
embed . add_field ( name = " My Latest Post " ,
value = f " **Caption:** { image_caption } " ,
inline = False )
embed . set_image ( url = image_url )
await ctx . send ( embed = embed )
@command ( name = " homies " )
@cooldown ( 1 , 5 , BucketType . user )
@bot_has_permissions ( attach_files = True )
async def homies ( self , ctx , * , text ) :
""" Summoning the Homies """
# Make sure the text entered is less than 20 characters
if len ( text ) > = 20 :
await self . bot . generate_embed ( ctx , desc = " Please make sure the prompt is below **20** characters! " )
else :
# Define the text to be drawn on the top and the bottom
top_text = f " Ayo fuck { text } "
bottom_text = f " All my homies hate { text } "
# Call the method to generate the image
file = generate_meme ( ' images/homies/AllMyHomies.jpg ' , top_text = top_text , bottom_text = bottom_text )
# Send the bytes object as an image file
await ctx . send ( file = discord . File ( file , " homies.png " ) )
@command ( name = " grayscale " , aliases = [ " gs " ] )
@cooldown ( 1 , 2 , BucketType . user )
@bot_has_permissions ( attach_files = True )
async def grayscale ( self , ctx ) :
""" Display grayscale version of image uploaded """
if ctx . message . attachments :
for attachments in ctx . message . attachments :
attach = await attachments . read ( )
image = Image . open ( io . BytesIO ( attach ) ) . convert ( ' LA ' )
# Save new grayscale image as bytes
file = io . BytesIO ( )
image . save ( file , format = ' PNG ' )
file . seek ( 0 )
await ctx . message . delete ( )
# Send Grayscale Image
await ctx . send ( file = discord . File ( file , " gs.png " ) )
else :
await self . bot . generate_embed ( ctx , desc = " **Image Not Detected!** " )
@command ( name = " invert " , aliases = [ " negative " ] )
@cooldown ( 1 , 2 , BucketType . user )
@bot_has_permissions ( attach_files = True )
async def invert ( self , ctx ) :
""" Display inverted version of image uploaded """
if ctx . message . attachments :
for attachments in ctx . message . attachments :
attach = await attachments . read ( )
image = Image . open ( io . BytesIO ( attach ) ) . convert ( ' RGB ' )
inverted = invert ( image )
# Save new grayscale image as bytes
file = io . BytesIO ( )
inverted . save ( file , format = ' PNG ' )
file . seek ( 0 )
await ctx . message . delete ( )
# Send Grayscale Image
await ctx . send ( file = discord . File ( file , " inverted.png " ) )
else :
await self . bot . generate_embed ( ctx , desc = " **Image Not Detected!** " )
@command ( name = " owo " , aliases = [ " uwu " ] )
@bot_has_permissions ( manage_messages = True )
async def owo ( self , ctx , * , text ) :
""" Converts given text to ' OwO ' format """
# Delete the message sent by the user
await ctx . message . delete ( )
# Convert to "OwO" text
uwu = OwO ( )
owo = uwu . whatsthis ( text )
# Send the text back
await ctx . message . channel . send ( owo )
def setup ( bot ) :
bot . add_cog ( Fun ( bot ) )