fdMerge branch 'frost' of https://github.com/Aevann1/Drama into frost
commit
18f00bfb4a
|
@ -1,4 +1,5 @@
|
|||
|
||||
import gevent.monkey
|
||||
gevent.monkey.patch_all()
|
||||
from os import environ, path
|
||||
import secrets
|
||||
from flask import *
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import math
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
pat_frames = [
|
||||
Image.open("files/assets/images/pat/0.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/1.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/2.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/3.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/4.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/5.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/6.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/7.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/8.gif").convert("RGBA"),
|
||||
Image.open("files/assets/images/pat/9.gif").convert("RGBA")
|
||||
]
|
||||
|
||||
def getPat(avatar_file, format="webp"):
|
||||
avatar_x = 5
|
||||
avatar_y = 5
|
||||
avatar_width = 150
|
||||
avatar_height = 150
|
||||
image_width = 160
|
||||
image_height = 160
|
||||
hand_x = 0
|
||||
hand_y = 0
|
||||
delay = 30
|
||||
|
||||
y_scale = [
|
||||
1,
|
||||
0.95,
|
||||
0.9,
|
||||
0.85,
|
||||
0.8,
|
||||
0.8,
|
||||
0.85,
|
||||
0.9,
|
||||
0.95,
|
||||
1
|
||||
]
|
||||
|
||||
x_scale = [
|
||||
0.80,
|
||||
0.85,
|
||||
0.90,
|
||||
0.95,
|
||||
1,
|
||||
1,
|
||||
0.95,
|
||||
0.90,
|
||||
0.85,
|
||||
0.80
|
||||
]
|
||||
|
||||
frames = []
|
||||
avatar_img = Image.open(avatar_file)
|
||||
for i in range(0, 10):
|
||||
avatar_actual_x = math.ceil((1 - x_scale[i]) * avatar_width / 2 + avatar_x)
|
||||
avatar_actual_y = math.ceil((1 - y_scale[i]) * avatar_height + avatar_y)
|
||||
avatar_actual_width = math.ceil(avatar_width * x_scale[i])
|
||||
avatar_actual_height = math.ceil(avatar_height * y_scale[i])
|
||||
|
||||
scaled_avatar_img = avatar_img.resize((avatar_actual_width, avatar_actual_height))
|
||||
frame = Image.new(mode="RGBA", size=(image_width, image_height))
|
||||
frame.paste(scaled_avatar_img, (avatar_actual_x, avatar_actual_y))
|
||||
frame.paste(pat_frames[i], (hand_x, hand_y), pat_frames[i])
|
||||
frames.append(frame)
|
||||
|
||||
output = io.BytesIO()
|
||||
frames[0].save(output, format,
|
||||
save_all = True,
|
||||
append_images = frames[1:],
|
||||
duration = delay,
|
||||
loop = 0
|
||||
)
|
||||
return output
|
||||
|
||||
|
||||
def pat(emoji):
|
||||
stream = getPat(open(f'files/assets/images/emojis/{emoji}.webp', "rb"), "webp")
|
||||
stream.seek(0)
|
||||
open(f'files/assets/images/emojis/{emoji}pat.webp', "wb").write(stream.read())
|
|
@ -3,6 +3,7 @@ from bs4 import BeautifulSoup
|
|||
from bleach.linkifier import LinkifyFilter
|
||||
from functools import partial
|
||||
from .get import *
|
||||
from .patter import pat
|
||||
from os import path, environ
|
||||
import re
|
||||
from mistletoe import markdown
|
||||
|
@ -243,6 +244,10 @@ def sanitize(sanitized, noimages=False, alert=False, comment=False, edit=False):
|
|||
if path.isfile(f'files/assets/images/emojis/{remoji}.webp'):
|
||||
new = re.sub(f'(?<!"):{emoji}:', f'<img loading="lazy" data-bs-toggle="tooltip" alt=":{emoji}:" title=":{emoji}:" class="{classes}" src="/e/{remoji}.webp">', new, flags=re.I|re.A)
|
||||
if comment: marseys_used.add(emoji)
|
||||
elif remoji.endswith('pat') and path.isfile(f"files/assets/images/emojis/{remoji.replace('pat','')}.webp"):
|
||||
pat(remoji.replace('pat',''))
|
||||
new = re.sub(f'(?<!"):{emoji}:', f'<img loading="lazy" data-bs-toggle="tooltip" alt=":{emoji}:" title=":{emoji}:" class="{classes}" src="/e/{remoji}.webp">', new, flags=re.I|re.A)
|
||||
|
||||
|
||||
sanitized = sanitized.replace(old, new)
|
||||
|
||||
|
@ -263,10 +268,6 @@ def sanitize(sanitized, noimages=False, alert=False, comment=False, edit=False):
|
|||
old = emoji
|
||||
if emoji == 'marseyrandom': emoji = choice(marseys_const2)
|
||||
else: emoji = old
|
||||
|
||||
if path.isfile(f'files/assets/images/emojis/{emoji}.webp'):
|
||||
sanitized = re.sub(f'(?<!"):{i.group(1).lower()}:', f'<img loading="lazy" data-bs-toggle="tooltip" alt=":!{old}:" title=":!{old}:" class="{classes}" src="/e/{emoji}.webp">', sanitized, flags=re.I|re.A)
|
||||
if comment: marseys_used.add(emoji)
|
||||
else:
|
||||
classes = 'emoji'
|
||||
if not edit and random() < 0.0025 and ('marsey' in emoji or emoji in marseys_const2): classes += ' golden'
|
||||
|
@ -275,9 +276,13 @@ def sanitize(sanitized, noimages=False, alert=False, comment=False, edit=False):
|
|||
if emoji == 'marseyrandom': emoji = choice(marseys_const2)
|
||||
else: emoji = old
|
||||
|
||||
|
||||
if path.isfile(f'files/assets/images/emojis/{emoji}.webp'):
|
||||
sanitized = re.sub(f'(?<!"):{i.group(1).lower()}:', f'<img loading="lazy" data-bs-toggle="tooltip" alt=":{old}:" title=":{old}:" class="{classes}" src="/e/{emoji}.webp">', sanitized, flags=re.I|re.A)
|
||||
if comment: marseys_used.add(emoji)
|
||||
elif emoji.endswith('pat') and path.isfile(f"files/assets/images/emojis/{emoji.replace('pat','')}.webp"):
|
||||
pat(emoji.replace('pat',''))
|
||||
sanitized = re.sub(f'(?<!"):{i.group(1).lower()}:', f'<img loading="lazy" data-bs-toggle="tooltip" alt=":!{old}:" title=":!{old}:" class="{classes}" src="/e/{emoji}.webp">', sanitized, flags=re.I|re.A)
|
||||
|
||||
|
||||
for rd in ["://reddit.com", "://new.reddit.com", "://www.reddit.com", "://redd.it", "://libredd.it"]:
|
||||
|
@ -353,10 +358,7 @@ def filter_emojis_only(title, edit=False, graceful=False):
|
|||
old = emoji
|
||||
if emoji == 'marseyrandom': emoji = choice(marseys_const2)
|
||||
else: emoji = old
|
||||
|
||||
if path.isfile(f'files/assets/images/emojis/{emoji}.webp'):
|
||||
title = re.sub(f'(?<!"):!{old}:', f'<img loading="lazy" data-bs-toggle="tooltip" alt=":!{old}:" title=":!{old}:" src="/e/{emoji}.webp" class="{classes}">', title, flags=re.I|re.A)
|
||||
|
||||
old = '!' + emoji
|
||||
else:
|
||||
classes = 'emoji'
|
||||
if not edit and random() < 0.0025 and ('marsey' in emoji or emoji in marseys_const2): classes += ' golden'
|
||||
|
@ -365,8 +367,13 @@ def filter_emojis_only(title, edit=False, graceful=False):
|
|||
if emoji == 'marseyrandom': emoji = choice(marseys_const2)
|
||||
else: emoji = old
|
||||
|
||||
|
||||
if path.isfile(f'files/assets/images/emojis/{emoji}.webp'):
|
||||
title = re.sub(f'(?<!"):{old}:', f'<img loading="lazy" data-bs-toggle="tooltip" alt=":{old}:" title=":{old}:" class="{classes}" src="/e/{emoji}.webp">', title, flags=re.I|re.A)
|
||||
elif emoji.endswith('pat') and path.isfile(f"files/assets/images/emojis/{emoji.replace('pat','')}.webp"):
|
||||
pat(emoji.replace('pat',''))
|
||||
title = re.sub(f'(?<!"):{old}:', f'<img loading="lazy" data-bs-toggle="tooltip" alt=":{old}:" title=":{old}:" class="{classes}" src="/e/{emoji}.webp">', title, flags=re.I|re.A)
|
||||
|
||||
|
||||
title = strikethrough_regex.sub(r'<del>\1</del>', title)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from files.helpers.const import SITE
|
||||
|
||||
if SITE in ('pcmemes.net', 'localhost'):
|
||||
import sys
|
||||
if "load_chat" in sys.argv:
|
||||
from files.helpers.const import SITE_FULL
|
||||
import time
|
||||
from files.helpers.wrappers import auth_required
|
||||
from files.helpers.sanitize import sanitize
|
||||
|
@ -10,29 +10,34 @@ if SITE in ('pcmemes.net', 'localhost'):
|
|||
from flask import render_template
|
||||
import sys
|
||||
|
||||
socketio = SocketIO(app, async_mode='gevent')
|
||||
socketio = SocketIO(app, async_mode='gevent', cors_allowed_origins=[SITE_FULL])
|
||||
typing = []
|
||||
online = []
|
||||
messages = []
|
||||
|
||||
@app.get("/chat")
|
||||
@auth_required
|
||||
def chat( v):
|
||||
return render_template("chat.html", v=v)
|
||||
return render_template("chat.html", v=v, messages=messages)
|
||||
|
||||
@socketio.on('speak')
|
||||
@limiter.limit("5/second;30/minute")
|
||||
@auth_required
|
||||
def speak(data, v):
|
||||
if not data: abort(403)
|
||||
global messages
|
||||
text = data[:1000].strip()
|
||||
if not text: abort(403)
|
||||
|
||||
data={
|
||||
"avatar": v.profile_url,
|
||||
"username":v.username,
|
||||
"namecolor":v.namecolor,
|
||||
"text":sanitize(data[:1000].strip()),
|
||||
"time": time.strftime("%d %b %Y at %H:%M:%S", time.gmtime(int(time.time())))
|
||||
"text":text,
|
||||
"text_html":sanitize(text),
|
||||
}
|
||||
|
||||
messages.append(data)
|
||||
messages = messages[:20]
|
||||
emit('speak', data, broadcast=True)
|
||||
return '', 204
|
||||
|
||||
|
|
|
@ -718,7 +718,7 @@ def thumbnail_thread(pid):
|
|||
|
||||
for i in data:
|
||||
|
||||
if i["author"] == 'GoMarsey': continue
|
||||
if i["subreddit"] == 'PokemonGoRaids': continue
|
||||
|
||||
body_html = sanitize(f'New {word} mention: https://old.reddit.com{i["permalink"]}?context=89', noimages=True)
|
||||
|
||||
|
|
|
@ -22,10 +22,12 @@
|
|||
</span>
|
||||
<div class="pl-md-3 text-muted">
|
||||
<div>
|
||||
<img class="mobile-avatar profile-pic-30 mr-2 d-inline-block d-md-none" data-toggle="tooltip" data-placement="right">
|
||||
<img class="mobile-avatar profile-pic-30 mr-1 d-inline-block d-md-none" data-toggle="tooltip" data-placement="right">
|
||||
<a href="" class="font-weight-bold text-black userlink" target="_blank"></a>
|
||||
<div style="overflow:hidden">
|
||||
<span class="chat-message text-black text-break"></span>
|
||||
<span class="text d-none"></span>
|
||||
<button class="quote-btn btn d-inline-block pt-0" onclick="quote2()"><i class="fas fa-reply" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,9 +35,29 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container p-md-0 chat-container">
|
||||
<div class="container py-0 chat-container">
|
||||
<div id="chat-window">
|
||||
<div id="chat-text" class="fullchat">
|
||||
{% for m in messages %}
|
||||
<div class="chat-line my-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="rounded mb-auto d-none d-md-block chat-profile">
|
||||
<img class="desktop-avatar rounded-circle w-100" src="{{m['avatar']}}">
|
||||
</span>
|
||||
<div class="pl-md-3 text-muted">
|
||||
<div>
|
||||
<img src="{{m['avatar']}}" class="mobile-avatar profile-pic-30 mr-1 d-inline-block d-md-none" data-toggle="tooltip" data-placement="right">
|
||||
<a class="font-weight-bold text-black userlink" style="color:#{{m['namecolor']}}" target="_blank" href="/@{{m['username']}}">{{m['username']}}</a>
|
||||
<div style="overflow:hidden">
|
||||
<span class="chat-message text-black text-break">{{m['text_html'] | safe}}</span>
|
||||
{% set text=m['text'] %}
|
||||
<button class="btn d-inline-block pt-0"><i onclick="quote('{{text}}')" class="fas fa-reply" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div id="system-template">
|
||||
<div class="system-line">
|
||||
|
@ -49,40 +71,14 @@
|
|||
<span id="loading-indicator" class="d-none"></span>
|
||||
</div>
|
||||
<i class="btn btn-secondary mr-2 fas fa-smile-beam" style="padding-top:0.65rem" onclick="loadEmojis('input-text')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-placement="bottom" title="Add Emoji"></i>
|
||||
<input id="input-text" minlength="1" maxlength="1000" type="text" class="form-control" placeholder="Message" autocomplete="off" autofocus>
|
||||
<textarea id="input-text" minlength="1" maxlength="1000" type="text" class="form-control" placeholder="Message" autocomplete="off" autofocus rows="1"></textarea>
|
||||
<button id="chatsend" onclick="send()" class="btn btn-primary ml-3" type="submit">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/assets/js/socketio.js"></script>
|
||||
<script data-cfasync="false" src="/assets/js/socketio.js"></script>
|
||||
|
||||
<style>
|
||||
#chat-window {
|
||||
max-height:calc(100vh - 300px);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.fullchat .chat-profile {
|
||||
min-width: 42px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
#chat-window::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#chat-window {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.chat-mention {
|
||||
background-color: var(--primary);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
<script data-cfasync="false">
|
||||
let socket=io()
|
||||
let chatline = document.getElementsByClassName('chat-line')[0]
|
||||
let box = document.getElementById('chat-window')
|
||||
|
@ -94,6 +90,8 @@
|
|||
let is_typing = false;
|
||||
let alert=true;
|
||||
|
||||
box.scrollTo(0, box.scrollHeight)
|
||||
|
||||
function flash(){
|
||||
let title = document.getElementsByTagName('title')[0]
|
||||
if (notifs >= 1 && !focused){
|
||||
|
@ -118,8 +116,9 @@
|
|||
|
||||
socket.on('speak', function(json) {
|
||||
let text = json['text']
|
||||
let text_html = json['text_html']
|
||||
|
||||
if (text.includes('<a href="/id/{{v.id}}">')){
|
||||
if (text_html.includes('<a href="/id/{{v.id}}">')){
|
||||
chatline.classList.add('chat-mention');
|
||||
}
|
||||
else {
|
||||
|
@ -133,26 +132,45 @@
|
|||
|
||||
scrolled_down = (box.scrollHeight - box.scrollTop <= window.innerHeight-109)
|
||||
document.getElementsByClassName('desktop-avatar')[0].src = json['avatar']
|
||||
document.getElementsByClassName('mobile-avatar')[0].src = json['avatar']
|
||||
document.getElementsByClassName('userlink')[0].href = '/@' + json['username']
|
||||
document.getElementsByClassName('userlink')[0].innerHTML = json['username']
|
||||
document.getElementsByClassName('userlink')[0].style.color = '#' + json['namecolor']
|
||||
document.getElementsByClassName('chat-message')[0].innerHTML = text
|
||||
document.getElementsByClassName('text')[0].innerHTML = text
|
||||
document.getElementsByClassName('chat-message')[0].innerHTML = text_html
|
||||
document.getElementById('chat-text').append(document.getElementsByClassName('chat-line')[0].cloneNode(true))
|
||||
if (scrolled_down) box.scrollTo(0, box.scrollHeight)
|
||||
})
|
||||
|
||||
|
||||
function send() {
|
||||
socket.emit('speak', textbox.value);
|
||||
text = textbox.value.trim()
|
||||
if (text)
|
||||
{
|
||||
socket.emit('speak', text);
|
||||
textbox.value = ''
|
||||
textbox.style.height = '40px'
|
||||
is_typing = false
|
||||
socket.emit('typing', false);
|
||||
}
|
||||
}
|
||||
|
||||
function quote(text) {
|
||||
textbox.style.height = '80px'
|
||||
textbox.value = '> ' + text + '\n\n'
|
||||
textbox.focus()
|
||||
}
|
||||
|
||||
textbox.addEventListener("keyup", function(event) {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
function quote2() {
|
||||
textbox.style.height = '80px'
|
||||
text = document.getElementsByClassName('text')[0].innerHTML
|
||||
textbox.value = '> ' + text + '\n\n'
|
||||
textbox.focus()
|
||||
}
|
||||
|
||||
textbox.addEventListener("keyup", function(e) {
|
||||
if (!e.shiftKey && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
send()
|
||||
}
|
||||
})
|
||||
|
@ -211,6 +229,46 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
#chat-window {
|
||||
max-height:calc(100vh - 300px);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.fullchat .chat-profile {
|
||||
min-width: 42px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
#chat-window::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#chat-window {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.chat-mention {
|
||||
background-color: var(--primary55);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.profile-pic-30 {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.chat-message p {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% include "emoji_modal.html" %}
|
||||
|
||||
{% endblock %}
|
|
@ -6,7 +6,12 @@
|
|||
|
||||
<script src="/static/assets/js/bootstrap.js?v=245"></script>
|
||||
{% if v %}
|
||||
<style>:root{--primary:#{{v.themecolor}}}</style>
|
||||
<style>
|
||||
:root{
|
||||
--primary:#{{v.themecolor}};
|
||||
--primary55:#{{v.themecolor}}55;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/static/assets/css/main.css?v=186">
|
||||
<link rel="stylesheet" href="/static/assets/css/{{v.theme}}.css?v=30">
|
||||
{% if v.agendaposter %}
|
||||
|
@ -229,6 +234,7 @@
|
|||
{% if '@' not in request.path %}
|
||||
{% if v %}
|
||||
|
||||
{% if request.path != '/chat' %}
|
||||
{% if sub %}
|
||||
<img alt="/h/{{sub.name}} banner" role="button" data-bs-toggle="modal" data-bs-target="#expandImageModal" onclick="expandDesktopImage('{{sub.banner_url}}')" loading="lazy" src="{{sub.banner_url}}" width=100% style="object-fit:cover;max-height:25vw">
|
||||
{% elif SITE_NAME == 'Drama' %}
|
||||
|
@ -243,6 +249,7 @@
|
|||
<img alt="site banner" src="/static/assets/images/{{SITE_NAME}}/banner.webp?v=1042" width="100%">
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<a href="/login">
|
||||
|
@ -263,7 +270,7 @@
|
|||
{% block postNav %}
|
||||
{% endblock %}
|
||||
|
||||
<div class="container{% if request.path=='/' or '/post/' in request.path or '/comment/' in request.path %} transparent{% endif %}">
|
||||
<div class="container {% if request.path=='/' or '/post/' in request.path or '/comment/' in request.path %}transparent{% elif request.path == '/chat' %}pb-0{% endif %}">
|
||||
<div class="row justify-content-around" id="main-content-row">
|
||||
|
||||
<div class="col h-100 {% block customPadding %}{% if request.path.startswith('/@') %}user-gutters{% else %}custom-gutters{% endif %}{% endblock %}" id="main-content-col">
|
||||
|
|
Loading…
Reference in New Issue