2023-01-12 07:41:01 +01:00
import openai
2023-01-12 14:33:30 +01:00
# from openai import api_key
2023-01-12 07:41:01 +01:00
import discord
2023-02-06 00:43:22 +01:00
from discord import Intents
2023-01-12 07:41:01 +01:00
from discord . commands import slash_command , option
2023-02-06 00:36:01 +01:00
from discord . ext import commands
2023-01-12 07:41:01 +01:00
import re
import os
import asyncio
import logging
2023-01-12 14:33:30 +01:00
import datetime
import base64
2023-01-20 13:30:45 +01:00
import requests
2023-02-24 13:04:24 +01:00
import zipfile
from zipfile import ZipFile
2023-01-20 13:30:45 +01:00
from dotenv import load_dotenv
2023-01-22 23:17:56 +01:00
load_dotenv ( )
2023-01-20 13:38:37 +01:00
use_images = os . getenv ( " USE_IMAGES " )
2023-02-06 00:36:01 +01:00
cooldown = os . getenv ( " COOLDOWN " )
2023-01-20 13:30:45 +01:00
if use_images != " No " : import imagesGeneration
2023-01-12 07:41:01 +01:00
logging . basicConfig ( level = logging . INFO )
2023-01-13 16:21:56 +01:00
imageint = " "
2023-02-08 10:19:02 +01:00
if use_images != " No " : imageint = " To add an image illustration , use  at the beginning of the slide, just after \" --- \" . Use only .png. It ' s not possible to add technical images but only illustrations. The images are generated by an ai, the name of the file should be a detailed quite long description of the image wanted. For example \"  \" but don ' t need to show a person necessairly. "
2023-01-13 16:21:56 +01:00
intstructions = f ''' Here is a presentation with marp. It ' s not possible to make slides longer than 200 characters. to separate slides,
2023-01-12 07:41:01 +01:00
"
- - -
"
2023-01-13 16:21:56 +01:00
then go at the line . The presentatio should be for everybody , all technical words and concepts , explained . { imageint } The presentation is minimum 20 slides long . You can use bulletpoints . Use markdown formatting ( titles , etc . . . ) . The presentation has also a conclusion . '''
2023-01-12 07:41:01 +01:00
bot = discord . Bot ( )
2023-02-24 13:04:24 +01:00
styles = [ " default " , " gaia " , " uncover " , " default-dark " , " gaia-dark " , " uncover-dark " , " olive " ]
2023-01-12 14:33:30 +01:00
languages = [ " english " , " french " , " spanish " , " german " , " italian " , " portuguese " , " russian " , " chinese " , " japanese " , " korean " , " arabic " ]
2023-01-12 07:41:01 +01:00
darkstyles = [ " default-dark " , " gaia-dark " , " uncover-dark " ]
2023-02-24 13:04:24 +01:00
customstyles = [ " olive " ]
2023-01-12 07:41:01 +01:00
async def get_style ( ctx : discord . AutocompleteContext ) :
""" Returns a list of colors that begin with the characters entered so far. """
2023-02-24 13:04:24 +01:00
return [ style for style in styles if style . startswith ( ctx . value . lower ( ) ) ]
2023-01-12 14:33:30 +01:00
async def get_ln ( ctx : discord . AutocompleteContext ) :
2023-02-24 13:04:24 +01:00
return [ language for language in languages if language . startswith ( ctx . value . lower ( ) ) ]
2023-01-12 07:41:01 +01:00
2023-02-06 10:40:33 +01:00
@bot.slash_command ( name = " private_present " , description = " Generate a presentation with marp, private command for user 707196665668436019 " )
@option ( name = " subject " , description = " The subject of the presentation " , required = True )
@option ( name = " style " , description = " The style of the presentation " , required = False , autocomplete = get_style )
2023-02-24 13:04:24 +01:00
@option ( name = " center " , description = " Center the text " , required = False )
2023-02-06 10:40:33 +01:00
@option ( name = " language " , description = " The language of the presentation " , required = False , autocomplete = get_ln )
@option ( name = " indications " , description = " The indications for the presentation " , required = False )
#command wprks only in dm and only for user 707196665668436019
@commands.is_owner ( )
2023-02-24 13:04:24 +01:00
async def private_present ( ctx : discord . ApplicationContext , subject : str , style : str = " default " , center : bool = True , language : str = " english " , indications : str = " " ) :
2023-02-06 10:40:33 +01:00
await present ( ctx , subject , style , language , indications )
2023-01-12 07:41:01 +01:00
@bot.slash_command ( name = " present " , description = " Generate a presentation with marp " )
#we create a function that takes the subject of the presentation and the style of the presentation as arguments, and that
@option ( name = " subject " , description = " The subject of the presentation " , required = True )
@option ( name = " style " , description = " The style of the presentation " , required = False , autocomplete = get_style )
2023-02-24 13:04:24 +01:00
@option ( name = " center " , description = " Center the text " , required = False )
2023-01-12 14:33:30 +01:00
@option ( name = " language " , description = " The language of the presentation " , required = False , autocomplete = get_ln )
@option ( name = " indications " , description = " The indications for the presentation " , required = False )
2023-02-06 10:40:33 +01:00
# a cooldown of duration cooldown seconds, except if the user is 707196665668436019
#@commands.cooldown(1, int(cooldown), commands.BucketType.user)
2023-02-06 10:49:21 +01:00
@commands.cooldown ( 1 , int ( cooldown ) , commands . BucketType . guild )
2023-02-24 13:04:24 +01:00
async def normal_present ( ctx : discord . ApplicationContext , subject : str , style : str = " default " , center : bool = True , language : str = " english " , indications : str = " " ) :
2023-02-06 11:16:49 +01:00
await present ( ctx , subject , style , language , indications )
2023-02-24 13:04:24 +01:00
async def present ( ctx : discord . ApplicationContext , subject : str , style : str = " default " , center : bool = True , language : str = " english " , indications : str = " " ) :
2023-01-12 07:41:01 +01:00
await ctx . defer ( )
2023-01-12 14:33:30 +01:00
date = datetime . datetime . now ( )
date = date . strftime ( " % Y- % m- %d - % H- % M- % S " )
2023-02-06 15:49:55 +01:00
#if the style is dark
dark = False
if style in darkstyles :
2023-02-24 13:04:24 +01:00
style = style . replace ( " -dark " , " " )
2023-02-06 15:49:55 +01:00
dark = True
2023-01-20 13:30:45 +01:00
marp = f ''' ---
marp : true
theme : { styles [ styles . index ( style ) ] }
class :
'''
2023-02-06 15:49:55 +01:00
if dark : marp = marp + f " - invert \n --- "
2023-02-24 13:04:24 +01:00
if center : marp = marp + " - lead \n --- "
2023-01-20 13:30:45 +01:00
else : marp = marp + " \n --- "
2023-02-24 13:04:24 +01:00
# if style in customstyles:
# marp = f"/* @theme {style} */\n" + marp
# print(marp)
2023-01-20 13:30:45 +01:00
prompt = f " { intstructions } { indications } The subject of the presentation is: { subject } The Language is: { language } <|endofprompt|> \n { marp } "
2023-01-12 07:41:01 +01:00
subject2 = subject
2023-02-21 11:05:26 +01:00
forbidden = [ " \\ " , " / " , " ? " , " ! " , " : " , " ; " , " ( " , " ) " , " [ " , " ] " , " { " , " } " , " ' " , ' " ' , " = " , " + " , " * " , " & " , " ^ " , " % " , " $ " , " # " , " @ " , " ` " , " ~ " , " | " , " < " , " > " , " , " , " . " , " ? " , " " ]
for i in forbidden :
if i in subject : subject = subject . replace ( i , " - " )
2023-01-12 14:33:30 +01:00
#we save teh subject in base64 in a variable
#if dosen't exist, create a directory called "userid" where the userid is the id of the user who called the command
uid = str ( ctx . author . id )
2023-01-20 13:30:45 +01:00
if not os . path . exists ( " ./data/ " + uid ) :
os . mkdir ( " ./data/ " + uid )
2023-01-12 14:33:30 +01:00
datenow = datetime . datetime . now ( )
datenow = datenow . strftime ( " % Y- % m- %d - % H- % M- % S " )
response = await openai . Completion . acreate (
2023-01-12 07:41:01 +01:00
engine = " text-davinci-003 " ,
prompt = prompt ,
2023-01-20 13:30:45 +01:00
temperature = 0.6 ,
2023-01-12 07:41:01 +01:00
max_tokens = 1024 ,
top_p = 1 ,
frequency_penalty = 0 ,
presence_penalty = 0 ,
stop = [ " <|endofprompt|> " ]
)
#we save the output in a variable
output = response [ " choices " ] [ 0 ] [ " text " ]
present = marp + output
##we save the output in a file called "subject.md"
matches = re . finditer ( r ' ! \ [.*? \ ] \ ((.*?) \ ) ' , present )
image_filenames = [ ]
for match in matches :
image_filenames . append ( match . group ( 1 ) )
#we create a text file with the image names and a md file for the presentation with utf8 encoding
2023-02-24 13:04:24 +01:00
if len ( subject ) > 15 : subject = subject [ : 15 ]
b64 = base64 . urlsafe_b64encode ( subject . encode ( " utf-8 " ) )
os . mkdir ( f " ./data/ { uid } / { b64 } { datenow } " )
path = f " ./data/ { uid } / { b64 } { datenow } "
with open ( f " { path } / { subject } -images.txt " , " w " , encoding = " utf8 " ) as f :
2023-01-12 07:41:01 +01:00
for image in image_filenames :
f . write ( image + " \n " )
2023-02-24 13:04:24 +01:00
with open ( f " { path } / { subject } .md " , " w " , encoding = " utf8 " ) as f : f . write ( present )
2023-01-20 13:30:45 +01:00
if len ( image_filenames ) > 0 and use_images != " no " :
2023-01-12 21:44:14 +01:00
#now we first remove the extension from the image filenames by removing the last 4 characters
image_filenames = [ image [ : - 4 ] for image in image_filenames ]
print ( image_filenames )
for images in image_filenames :
2023-01-22 23:17:56 +01:00
print ( " generating image " + images + " with " + str ( use_images ) )
r = await imagesGeneration . generate ( images , f " { os . getcwd ( ) } \\ data \\ { uid } \\ { b64 } { datenow } \\ " , str ( use_images ) , apikey )
if str ( use_images ) == " sd " : os . rename ( f " { os . getcwd ( ) } \\ . \\ data \\ { uid } \\ { b64 } { datenow } \\ { images } _0.png " , f " { os . getcwd ( ) } \\ data \\ { uid } \\ { b64 } { datenow } \\ { images } .png " )
2023-02-21 11:05:26 +01:00
if str ( use_images ) == " dalle " :
2023-01-20 13:30:45 +01:00
image_url = r [ ' data ' ] [ 0 ] [ ' url ' ]
img_data = requests . get ( image_url ) . content
2023-02-24 13:04:24 +01:00
with open ( f ' { path } / { images } .png ' , ' wb ' ) as handler :
2023-01-20 13:30:45 +01:00
handler . write ( img_data )
2023-02-24 13:04:24 +01:00
await asyncio . sleep ( 15 ) #wait 15 seconds to avoid rate limiting
cmd = f " --pdf --allow-local-files { path } / { subject } .md "
if style in customstyles : cmd = cmd + f " --theme ./themes/ { style } .css "
2023-02-07 18:18:00 +01:00
if os . path . exists ( " ./marp.exe " ) :
2023-02-07 15:44:13 +01:00
os . system ( f " marp.exe { cmd } " )
2023-02-07 18:18:00 +01:00
else :
2023-02-07 15:44:13 +01:00
cmd = cmd . replace ( " ' " , " \\ ' " )
os . system ( f " ./marp { cmd } " )
2023-02-24 13:04:24 +01:00
cmd = f " --image png -o { path } / { subject } .png --allow-local-files { path } / { subject } .md "
if style in customstyles : cmd = cmd + f " --theme ./themes/ { style } .css "
2023-02-07 18:18:00 +01:00
if os . path . exists ( " ./marp.exe " ) :
os . system ( f " marp.exe { cmd } " )
else :
2023-02-07 15:44:13 +01:00
cmd = cmd . replace ( " ' " , " \\ ' " )
os . system ( f " ./marp { cmd } " )
2023-02-24 13:04:24 +01:00
cmd = f " --html --allow-local-files { path } / { subject } .md "
if style in customstyles : cmd = cmd + f " --theme ./themes/ { style } .css "
2023-02-07 18:18:00 +01:00
if os . path . exists ( " ./marp.exe " ) :
os . system ( f " marp.exe { cmd } " )
else :
2023-02-07 15:44:13 +01:00
cmd = cmd . replace ( " ' " , " \\ ' " )
os . system ( f " ./marp { cmd } " )
2023-02-24 13:04:24 +01:00
cmd = f " --pptx --allow-local-files { path } / { subject } .md "
if style in customstyles : cmd = cmd + f " --theme ./themes/ { style } .css "
if os . path . exists ( " ./marp.exe " ) :
os . system ( f " marp.exe { cmd } " )
else :
cmd = cmd . replace ( " ' " , " \\ ' " )
os . system ( f " ./marp { cmd } " )
#now, we create a zip file with all the files
zipObj = ZipFile ( f " { path } / { subject } .zip " , ' w ' )
zipObj . write ( f " { path } / { subject } .md " , f " { subject } .md " )
zipObj . write ( f " { path } / { subject } .html " , f " { subject } .html " )
zipObj . write ( f " { path } / { subject } .pdf " , f " { subject } .pdf " )
zipObj . write ( f " { path } / { subject } .png " , f " { subject } .png " )
zipObj . write ( f " { path } / { subject } .pptx " , f " { subject } .pptx " )
with open ( f " { path } / { subject } -images.txt " , " r " , encoding = " utf8 " ) as f :
for image in f . readlines ( ) :
zipObj . write ( f " { path } / { image . strip ( ) } " , f " { image . strip ( ) } " )
zipObj . close ( )
embed = discord . Embed ( title = subject2 , description = " Thanks for using presentator bot. You will find your presentation in the attached zip file in the following formats: markdown, html, pdf, pptx, and the presentation ' images. If you want to modify your presentation you can use the markdown file. More information about how to modify the file [HERE](https://marp.app). " , color = discord . Color . brand_red ( ) )
files = [ discord . File ( f " { path } / { subject } .zip " ) , discord . File ( f " { path } / { subject } .png " ) ]
2023-01-12 07:41:01 +01:00
embed . set_image ( url = f " attachment:// { subject } .png " )
await ctx . respond ( embed = embed , files = files )
2023-01-12 14:33:30 +01:00
@bot.slash_command ( name = " list " , description = " List all the presentations you have created " )
async def list ( ctx : discord . ApplicationContext ) :
embed = discord . Embed ( title = " Presentations " , description = " Here is the list of all the presentations you have created. You can download the presentation in different formats (pdf, markdown, html) by doing `/get` \" *presentation id* \" . The images are generated by an ai. If you want to modify your presentation you can use the markdown file. More information about how to modify the file [HERE](https://marp.app). " , color = 0x00ff00 )
liste = await get_presentations ( str ( ctx . author . id ) )
for key in liste :
embed . add_field ( name = f " { liste [ key ] } " , value = f " </get:1063051827010084925> ` { key } ` " , inline = False )
await ctx . respond ( embed = embed , ephemeral = True )
async def get_presentations ( uid ) :
2023-01-12 23:01:10 +01:00
folders = os . listdir ( f " ./data/ { uid } " )
2023-01-12 14:33:30 +01:00
names = { }
for folder in folders :
name = base64 . urlsafe_b64decode ( folder [ 2 : - 20 ] ) . decode ( " utf-8 " )
names [ folder ] = name
return names
@bot.slash_command ( name = " get " , description = " Get a presentation " )
@option ( name = " pid " , description = " The id of the presentation " , required = True )
async def get ( ctx : discord . ApplicationContext , pid : str ) :
uid = str ( ctx . author . id )
liste = await get_presentations ( uid )
if pid in liste :
2023-01-12 21:44:14 +01:00
files = [ discord . File ( f " ./data/ { uid } / { pid } / { liste [ pid ] } .pdf " ) , discord . File ( f " ./data/ { uid } / { pid } / { liste [ pid ] } .md " ) , discord . File ( f " ./data/ { uid } / { pid } / { liste [ pid ] } .html " ) ]
2023-01-12 14:33:30 +01:00
await ctx . respond ( files = files , ephemeral = True )
2023-01-12 07:41:01 +01:00
#when the bot is ready we print a message
@bot.event
async def on_ready ( ) :
print ( " Bot is ready " )
2023-01-12 21:44:14 +01:00
if not os . path . exists ( " data " ) :
os . mkdir ( " data " )
2023-02-06 00:40:06 +01:00
@bot.event
async def on_application_command_error ( ctx , error ) :
#if there is an error we send a message to the user
await ctx . respond ( f " An error occured: { error } " , ephemeral = True )
2023-02-24 13:04:24 +01:00
2023-01-12 07:41:01 +01:00
#get the openai key drom he key.env file
2023-01-20 13:30:45 +01:00
token = os . getenv ( " TOKEN " )
apikey = os . getenv ( " OPENAI " )
2023-01-12 07:41:01 +01:00
openai . api_key = apikey
2023-01-12 14:33:30 +01:00
bot . run ( token )