| ''' |
| |
| This is a library for formatting GPT-4chan and chat outputs as nice HTML. |
| |
| ''' |
|
|
| import os |
| import re |
| from pathlib import Path |
|
|
| from PIL import Image |
|
|
| |
| image_cache = {} |
|
|
| def generate_basic_html(s): |
| css = """ |
| .container { |
| max-width: 600px; |
| margin-left: auto; |
| margin-right: auto; |
| background-color: rgb(31, 41, 55); |
| padding:3em; |
| } |
| .container p { |
| font-size: 16px !important; |
| color: white !important; |
| margin-bottom: 22px; |
| line-height: 1.4 !important; |
| } |
| """ |
| s = '\n'.join([f'<p>{line}</p>' for line in s.split('\n')]) |
| s = f'<style>{css}</style><div class="container">{s}</div>' |
| return s |
|
|
| def process_post(post, c): |
| t = post.split('\n') |
| number = t[0].split(' ')[1] |
| if len(t) > 1: |
| src = '\n'.join(t[1:]) |
| else: |
| src = '' |
| src = re.sub('>', '>', src) |
| src = re.sub('(>>[0-9]*)', '<span class="quote">\\1</span>', src) |
| src = re.sub('\n', '<br>\n', src) |
| src = f'<blockquote class="message">{src}\n' |
| src = f'<span class="name">Anonymous </span> <span class="number">No.{number}</span>\n{src}' |
| return src |
|
|
| def generate_4chan_html(f): |
| css = """ |
| |
| #parent #container { |
| background-color: #eef2ff; |
| padding: 17px; |
| } |
| #parent #container .reply { |
| background-color: rgb(214, 218, 240); |
| border-bottom-color: rgb(183, 197, 217); |
| border-bottom-style: solid; |
| border-bottom-width: 1px; |
| border-image-outset: 0; |
| border-image-repeat: stretch; |
| border-image-slice: 100%; |
| border-image-source: none; |
| border-image-width: 1; |
| border-left-color: rgb(0, 0, 0); |
| border-left-style: none; |
| border-left-width: 0px; |
| border-right-color: rgb(183, 197, 217); |
| border-right-style: solid; |
| border-right-width: 1px; |
| border-top-color: rgb(0, 0, 0); |
| border-top-style: none; |
| border-top-width: 0px; |
| color: rgb(0, 0, 0); |
| display: table; |
| font-family: arial, helvetica, sans-serif; |
| font-size: 13.3333px; |
| margin-bottom: 4px; |
| margin-left: 0px; |
| margin-right: 0px; |
| margin-top: 4px; |
| overflow-x: hidden; |
| overflow-y: hidden; |
| padding-bottom: 4px; |
| padding-left: 2px; |
| padding-right: 2px; |
| padding-top: 4px; |
| } |
| |
| #parent #container .number { |
| color: rgb(0, 0, 0); |
| font-family: arial, helvetica, sans-serif; |
| font-size: 13.3333px; |
| width: 342.65px; |
| margin-right: 7px; |
| } |
| |
| #parent #container .op { |
| color: rgb(0, 0, 0); |
| font-family: arial, helvetica, sans-serif; |
| font-size: 13.3333px; |
| margin-bottom: 8px; |
| margin-left: 0px; |
| margin-right: 0px; |
| margin-top: 4px; |
| overflow-x: hidden; |
| overflow-y: hidden; |
| } |
| |
| #parent #container .op blockquote { |
| margin-left: 0px !important; |
| } |
| |
| #parent #container .name { |
| color: rgb(17, 119, 67); |
| font-family: arial, helvetica, sans-serif; |
| font-size: 13.3333px; |
| font-weight: 700; |
| margin-left: 7px; |
| } |
| |
| #parent #container .quote { |
| color: rgb(221, 0, 0); |
| font-family: arial, helvetica, sans-serif; |
| font-size: 13.3333px; |
| text-decoration-color: rgb(221, 0, 0); |
| text-decoration-line: underline; |
| text-decoration-style: solid; |
| text-decoration-thickness: auto; |
| } |
| |
| #parent #container .greentext { |
| color: rgb(120, 153, 34); |
| font-family: arial, helvetica, sans-serif; |
| font-size: 13.3333px; |
| } |
| |
| #parent #container blockquote { |
| margin: 0px !important; |
| margin-block-start: 1em; |
| margin-block-end: 1em; |
| margin-inline-start: 40px; |
| margin-inline-end: 40px; |
| margin-top: 13.33px !important; |
| margin-bottom: 13.33px !important; |
| margin-left: 40px !important; |
| margin-right: 40px !important; |
| } |
| |
| #parent #container .message { |
| color: black; |
| border: none; |
| } |
| """ |
|
|
| posts = [] |
| post = '' |
| c = -2 |
| for line in f.splitlines(): |
| line += "\n" |
| if line == '-----\n': |
| continue |
| elif line.startswith('--- '): |
| c += 1 |
| if post != '': |
| src = process_post(post, c) |
| posts.append(src) |
| post = line |
| else: |
| post += line |
| if post != '': |
| src = process_post(post, c) |
| posts.append(src) |
|
|
| for i in range(len(posts)): |
| if i == 0: |
| posts[i] = f'<div class="op">{posts[i]}</div>\n' |
| else: |
| posts[i] = f'<div class="reply">{posts[i]}</div>\n' |
| |
| output = '' |
| output += f'<style>{css}</style><div id="parent"><div id="container">' |
| for post in posts: |
| output += post |
| output += '</div></div>' |
| output = output.split('\n') |
| for i in range(len(output)): |
| output[i] = re.sub(r'^(>(.*?)(<br>|</div>))', r'<span class="greentext">\1</span>', output[i]) |
| output[i] = re.sub(r'^<blockquote class="message">(>(.*?)(<br>|</div>))', r'<blockquote class="message"><span class="greentext">\1</span>', output[i]) |
| output = '\n'.join(output) |
|
|
| return output |
|
|
| def get_image_cache(path): |
| cache_folder = Path("cache") |
| if not cache_folder.exists(): |
| cache_folder.mkdir() |
|
|
| mtime = os.stat(path).st_mtime |
| if (path in image_cache and mtime != image_cache[path][0]) or (path not in image_cache): |
| img = Image.open(path) |
| img.thumbnail((200, 200)) |
| output_file = Path(f'cache/{path.name}_cache.png') |
| img.convert('RGB').save(output_file, format='PNG') |
| image_cache[path] = [mtime, output_file.as_posix()] |
|
|
| return image_cache[path][1] |
|
|
| def generate_chat_html(history, name1, name2, character): |
| css = """ |
| .chat { |
| margin-left: auto; |
| margin-right: auto; |
| max-width: 800px; |
| height: 66.67vh; |
| overflow-y: auto; |
| padding-right: 20px; |
| display: flex; |
| flex-direction: column-reverse; |
| } |
| |
| .message { |
| display: grid; |
| grid-template-columns: 60px 1fr; |
| padding-bottom: 25px; |
| font-size: 15px; |
| font-family: Helvetica, Arial, sans-serif; |
| line-height: 1.428571429; |
| } |
| |
| .circle-you { |
| width: 50px; |
| height: 50px; |
| background-color: rgb(238, 78, 59); |
| border-radius: 50%; |
| } |
| |
| .circle-bot { |
| width: 50px; |
| height: 50px; |
| background-color: rgb(59, 78, 244); |
| border-radius: 50%; |
| } |
| |
| .circle-bot img, .circle-you img { |
| border-radius: 50%; |
| width: 100%; |
| height: 100%; |
| object-fit: cover; |
| } |
| |
| .text { |
| } |
| |
| .text p { |
| margin-top: 5px; |
| } |
| |
| .username { |
| font-weight: bold; |
| } |
| |
| .message-body { |
| } |
| |
| .message-body img { |
| max-width: 300px; |
| max-height: 300px; |
| border-radius: 20px; |
| } |
| |
| .message-body p { |
| margin-bottom: 0 !important; |
| font-size: 15px !important; |
| line-height: 1.428571429 !important; |
| } |
| |
| .dark .message-body p em { |
| color: rgb(138, 138, 138) !important; |
| } |
| |
| .message-body p em { |
| color: rgb(110, 110, 110) !important; |
| } |
| |
| """ |
|
|
| output = '' |
| output += f'<style>{css}</style><div class="chat" id="chat">' |
| img = '' |
|
|
| for i in [ |
| f"characters/{character}.png", |
| f"characters/{character}.jpg", |
| f"characters/{character}.jpeg", |
| "img_bot.png", |
| "img_bot.jpg", |
| "img_bot.jpeg" |
| ]: |
|
|
| path = Path(i) |
| if path.exists(): |
| img = f'<img src="file/{get_image_cache(path)}">' |
| break |
|
|
| img_me = '' |
| for i in ["img_me.png", "img_me.jpg", "img_me.jpeg"]: |
| path = Path(i) |
| if path.exists(): |
| img_me = f'<img src="file/{get_image_cache(path)}">' |
| break |
|
|
| for i,_row in enumerate(history[::-1]): |
| row = _row.copy() |
| row[0] = re.sub(r"(\*\*)([^\*\n]*)(\*\*)", r"<b>\2</b>", row[0]) |
| row[1] = re.sub(r"(\*\*)([^\*\n]*)(\*\*)", r"<b>\2</b>", row[1]) |
| row[0] = re.sub(r"(\*)([^\*\n]*)(\*)", r"<em>\2</em>", row[0]) |
| row[1] = re.sub(r"(\*)([^\*\n]*)(\*)", r"<em>\2</em>", row[1]) |
| p = '\n'.join([f"<p>{x}</p>" for x in row[1].split('\n')]) |
| output += f""" |
| <div class="message"> |
| <div class="circle-bot"> |
| {img} |
| </div> |
| <div class="text"> |
| <div class="username"> |
| {name2} |
| </div> |
| <div class="message-body"> |
| {p} |
| </div> |
| </div> |
| </div> |
| """ |
|
|
| if not (i == len(history)-1 and len(row[0]) == 0): |
| p = '\n'.join([f"<p>{x}</p>" for x in row[0].split('\n')]) |
| output += f""" |
| <div class="message"> |
| <div class="circle-you"> |
| {img_me} |
| </div> |
| <div class="text"> |
| <div class="username"> |
| {name1} |
| </div> |
| <div class="message-body"> |
| {p} |
| </div> |
| </div> |
| </div> |
| """ |
|
|
| output += "</div>" |
| return output |
|
|