This repository has been archived by the owner on Sep 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
/
main.py
221 lines (211 loc) · 11.9 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import openai
import discord
from discord import Intents
from discord.commands import slash_command, option
from discord.ext import commands
import re
import os
import asyncio
import logging
import datetime
import base64
import requests
import zipfile
from zipfile import ZipFile
from dotenv import load_dotenv
load_dotenv()
use_images = os.getenv("USE_IMAGES")
cooldown = os.getenv("COOLDOWN")
if use_images != "No": import imagesGeneration
logging.basicConfig(level=logging.INFO)
imageint = ""
if use_images != "No": imageint = "To add an image illustration , use ![bg left:50% 70%](a-long-detailed-description-of-the-image.png) 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. You always need to specify the description of the image."
intstructions = f'''Here is a presentation with marp. It's not possible to make slides longer than 200 characters. to separate slides,
"
---
"
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.'''
bot = discord.Bot()
styles = ["default", "gaia", "uncover", "default-dark", "gaia-dark", "uncover-dark", "olive"]
languages = ["english", "french", "spanish", "german", "italian", "portuguese", "russian", "chinese", "japanese", "korean", "arabic"]
darkstyles = ["default-dark", "gaia-dark", "uncover-dark"]
customstyles = ["olive"]
async def get_style(ctx: discord.AutocompleteContext):
"""Returns a list of colors that begin with the characters entered so far."""
return [style for style in styles if style.startswith(ctx.value.lower())]
async def get_ln(ctx: discord.AutocompleteContext):
return [language for language in languages if language.startswith(ctx.value.lower())]
@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)
@option(name="center", description="Center the text", required=False)
@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()
async def private_present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
await present(ctx, subject, style, language, indications)
@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)
@option(name="center", description="Center the text", required=False)
@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)
# a cooldown of duration cooldown seconds, except if the user is 707196665668436019
#@commands.cooldown(1, int(cooldown), commands.BucketType.user)
@commands.cooldown(1, int(cooldown), commands.BucketType.guild)
async def normal_present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
await present(ctx, subject, style, language, indications)
async def present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
await ctx.defer()
date = datetime.datetime.now()
date = date.strftime("%Y-%m-%d-%H-%M-%S")
#if the style is dark
dark = False
if style in darkstyles:
style = style.replace("-dark", "")
dark = True
marp = f'''---
marp: true
theme: {styles[styles.index(style)]}
class:
'''
if dark: marp = marp + f" - invert\n---"
if center: marp = marp + " - lead\n---"
else: marp = marp + "\n---"
# if style in customstyles:
# marp = f"/* @theme {style} */\n" + marp
# print(marp)
prompt = f"{intstructions} {indications} The subject of the presentation is: {subject} The Language is: {language} <|endofprompt|> \n {marp}"
subject2 = subject
forbidden = ["\\", "/", "?", "!", ":", ";", "(", ")", "[", "]", "{", "}", "'", '"', "=", "+", "*", "&", "^", "%", "$", "#", "@", "`", "~", "|", "<", ">", ",", ".", "?", " "]
for i in forbidden:
if i in subject: subject = subject.replace(i, "-")
#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)
if not os.path.exists("./data/"+uid):
os.mkdir("./data/"+uid)
datenow = datetime.datetime.now()
datenow = datenow.strftime("%Y-%m-%d-%H-%M-%S")
response = await openai.Completion.acreate(
engine="text-davinci-003",
prompt=prompt,
temperature=0.6,
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
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:
for image in image_filenames:
f.write(image + "\n")
with open(f"{path}/{subject}.md", "w", encoding="utf8") as f: f.write(present)
if len(image_filenames) > 0 and use_images!="no":
#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:
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")
if str(use_images) == "dalle":
image_url = r['data'][0]['url']
img_data = requests.get(image_url).content
with open(f'{path}/{images}.png', 'wb') as handler:
handler.write(img_data)
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"
if os.path.exists("./marp.exe"):
os.system(f"marp.exe {cmd}")
else:
cmd = cmd.replace("'", "\\'")
os.system(f"./marp {cmd}")
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"
if os.path.exists("./marp.exe"):
os.system(f"marp.exe {cmd}")
else:
cmd = cmd.replace("'", "\\'")
os.system(f"./marp {cmd}")
cmd = f" --html --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}")
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")]
embed.set_image(url=f"attachment://{subject}.png")
await ctx.respond(embed=embed, files=files)
@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):
folders = os.listdir(f"./data/{uid}")
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:
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")]
await ctx.respond(files=files, ephemeral=True)
@bot.event
async def on_ready():
print("Bot is ready")
if not os.path.exists("data"):
os.mkdir("data")
@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)
#get the openai key drom he key.env file
token = os.getenv("TOKEN")
apikey = os.getenv("OPENAI")
openai.api_key = apikey
bot.run(token)