219 lines
6.2 KiB
Python

import logging
import os
import random
import re
from typing import Union
import openai
from dotenv import load_dotenv
from flask import Flask, redirect, request
from html5lib import HTMLParser
from htmls import index_html, prompt_html_template
load_dotenv()
app = Flask(__name__)
openai.api_key = os.getenv("OPENAI_KEY")
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
def format_message(message: str) -> list:
"""
Formats the message to send to chatgpt
Args:
message: The message to send to chatgpt
Returns:
The formatted message in a list
Raises:
None
"""
return [
{
"role": "system",
"content": (
"You are a very knowledgeable person with random knowledge for everything the humanity has ever done"
),
},
{"role": "user", "content": message},
]
def extract_html_from_chatgpt_response(returned_string: str) -> Union[str, None]:
"""
Extracts the HTML from the chatgpt response
Args:
returned_string: The string returned by chatgpt
Returns:
The HTML if it is valid, None otherwise
Raises:
None
"""
validator = HTMLParser(strict=True)
try:
validator.parse(returned_string)
logging.info(f"Valid HTML Found! {returned_string}")
return returned_string
except Exception:
match = returned_string.split("<html>")
if len(match) > 1:
logging.info("No valid HTML found, trying to parse...")
html = "<html>" + match[1]
html_match = html.split("</html>")
possible_html = html_match[0] + "</html>"
if "(topic)" not in possible_html and "(slugified_related_topic_name)" not in possible_html:
return possible_html
def get_random_topic() -> str:
"""
Gets a random topic from chatgpt
Args:
None
Returns:
A random topic
Raises:
None
"""
try:
completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=format_message(
"Give me a random list of 10 topics to talk about in a comma separated list. Each topic should be less "
"than 5 words long and should not contain commas. Be as creative as possible and choose topics from "
"multiple genres and industries."
),
)
topics = completion.choices[0].message.content.split(",")
if len(topics) < 5:
logging.info(f"The topics is too short: {topics}. Retrying...")
return get_random_topic()
logging.info(f"The topics list is: {topics}")
topic = random.choice(topics)
return topic.strip()
except openai.error.RateLimitError:
return get_random_topic()
def generate_topic_image(topic: str) -> str:
"""
Generates an image for the topic
Args:
topic: The topic to generate an image for
Returns:
The URL of the image
Raises:
None
"""
try:
completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=format_message(f"Generate a prompt for dall-e about {topic}"),
)
dalle_prompt = completion.choices[0].message.content
logging.info(f"The Dall-E prompt is: {dalle_prompt}")
image = openai.Image.create(prompt=dalle_prompt, n=1, size="256x256")
return image["data"][0]["url"]
except Exception:
generate_topic_image(topic)
def get_chatgpt_html_response(message: str, count: int = 0) -> str:
"""
Gets the response from chatgpt
Args:
message: The message to send to chatgpt
count: The number of times this function has been called
Returns:
The HTML response from chatgpt
Raises:
None
"""
count += 1
if count >= 5:
return f"Error! ChatGPT is having problems with this topic: {message}. Try a new one."
logging.info(f"The topic to generate is {message}")
try:
completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=format_message(
(
f"Give me information about {message}. Now, given this HTML: \n {prompt_html_template} \n replace "
"(topic) with the name of the selected topic and (topic_info) with 5 parragraphs talking about the "
"topic. Replace (related_links) with 5 links related to the topic. For these links, the href "
"should be '/infinite?topic=(slugified_related_topic_name)' and the text should be the name of the "
"related topic. The name of these topics should not be more than 5 words. Finally, replace "
"(new_topics) with a 5 links with completely new and random topics to talk about. These links "
"should follow the same rules as before. Do not replace (image_source). Respond only the code"
)
),
)
except openai.error.RateLimitError:
return get_chatgpt_html_response(message, count)
response = completion.choices[0].message.content
html = extract_html_from_chatgpt_response(response)
if not html:
logging.info(f"No HTML was found. The response was {response}. Retrying...")
return get_chatgpt_html_response(message, count)
pattern = r'src="([^"]*)"'
image = generate_topic_image(message)
logging.info(f"The image is {image}")
html_with_image = re.sub(pattern, f'src="{image}"', html)
return html_with_image
@app.route("/")
def index():
return index_html
@app.route("/start")
def start():
random_topic = get_random_topic()
return get_chatgpt_html_response(random_topic)
@app.route("/infinite")
def topic():
topic = request.args.get("topic", None)
if not topic:
return redirect("/start")
unslugified_topic = topic.replace("-", " ").replace("_", " ").replace("%20", " ").replace("\n", " ")
if len(unslugified_topic.split()) > 5:
logging.info(f"The topic is too long: {unslugified_topic}")
return "Error! Your topic should be < 5 words"
return get_chatgpt_html_response(unslugified_topic)