201 lines
5.8 KiB
Python
201 lines
5.8 KiB
Python
import logging
|
|
import random
|
|
import re
|
|
from typing import Union
|
|
|
|
import openai
|
|
from html5lib import HTMLParser
|
|
|
|
from htmls import prompt_html_template
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
|
|
|
DEFAULT_MODEL = "gpt-4"
|
|
|
|
|
|
def format_message(message: str, role: 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": role},
|
|
{"role": "user", "content": message},
|
|
]
|
|
|
|
|
|
async 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 "(slugified_related_topic_name)" not in possible_html:
|
|
return possible_html
|
|
|
|
|
|
async 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=DEFAULT_MODEL,
|
|
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.",
|
|
"You are a very knowledgeable person with random knowledge about everything the humanity has done",
|
|
),
|
|
)
|
|
|
|
topics = completion.choices[0].message.content.split(",")
|
|
|
|
if len(topics) < 5:
|
|
logging.info(f"The topics is too short: {topics}. Retrying")
|
|
return await get_random_topic()
|
|
|
|
logging.info(f"The topics list is: {topics}")
|
|
topic = random.choice(topics)
|
|
|
|
return topic.strip().title()
|
|
|
|
except openai.error.RateLimitError:
|
|
return await get_random_topic()
|
|
|
|
|
|
async 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=DEFAULT_MODEL,
|
|
messages=format_message(
|
|
f"Generate a prompt for dall-e about {topic}. Use less than 10 words and only return the prompt",
|
|
"You are a dall-e expert",
|
|
),
|
|
)
|
|
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:
|
|
return generate_topic_image(topic)
|
|
|
|
|
|
async def get_chatgpt_html_response(topic: str = None, 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: {topic}. Try a new one."
|
|
|
|
if topic is None:
|
|
topic = await get_random_topic()
|
|
|
|
html = prompt_html_template.replace("(topic)", topic)
|
|
|
|
logging.info(f"The topic to generate is {topic}")
|
|
|
|
try:
|
|
completion = openai.ChatCompletion.create(
|
|
model=DEFAULT_MODEL,
|
|
messages=format_message(
|
|
f"""
|
|
You are going to add information to this: {html}.
|
|
- Add 5 parragraphs talking about {topic} in the "information-container" div Add only the information.
|
|
- Add 5 links for related topics in the "related-topics-container" div. The href should be
|
|
"/infinite?topic=(slugified_related_topic_name)" and the text should be "(related_topic_name).
|
|
- Add 5 links for new random topics not related to {topic} in the "new-topics-container" div. The href
|
|
should be the same as before.
|
|
- Only respond with the HTML code
|
|
""",
|
|
"You are a very skilled web developer with expertise in HTML.",
|
|
),
|
|
)
|
|
except openai.error.RateLimitError:
|
|
return await get_chatgpt_html_response(topic, count)
|
|
|
|
response = completion.choices[0].message.content
|
|
|
|
logging.info(f"The response is {response}")
|
|
|
|
html = await extract_html_from_chatgpt_response(response)
|
|
|
|
if not html:
|
|
logging.info(f"No HTML was found. The response was {response}. Retrying")
|
|
return await get_chatgpt_html_response(topic, count)
|
|
|
|
pattern = r'src="([^"]*)"'
|
|
image = await generate_topic_image(topic)
|
|
|
|
logging.info(f"The image is {image}")
|
|
|
|
complete_html = re.sub(pattern, f'src="{image}"', html)
|
|
return_html = complete_html.split("</html>")[0]
|
|
return_html += (
|
|
"<footer><i><b>Remember this is autogenerated by ChatGPT so it is VERY slow. Be patient!</b></i></footer></html>"
|
|
)
|
|
|
|
return return_html
|