add orgies for private chats

master
Aevann 2024-04-08 08:33:47 +02:00
parent 6dd609983c
commit 36f65ede46
16 changed files with 189 additions and 142 deletions

View File

@ -1,8 +0,0 @@
function remove_orgy(t, created_utc) {
postToast(t, `/admin/remove_orgy/${created_utc}`,
{},
() => {
t.parentElement.parentElement.remove()
}
);
}

View File

@ -138,7 +138,7 @@ if (!location.pathname.endsWith('/submit') && !location.pathname.startsWith('/ch
return
}
if (location.pathname == '/admin/orgies') {
if (location.pathname.endsWith('/orgies')) {
document.getElementById('start-orgy').click();
return
}

View File

@ -0,0 +1,8 @@
function remove_orgy(t, chat_id, created_utc) {
postToast(t, `/chat/${chat_id}/remove_orgy/${created_utc}`,
{},
() => {
t.parentElement.parentElement.remove()
}
);
}

View File

@ -2,7 +2,7 @@ import time
from flask import g, abort
import requests
from sqlalchemy import Column, or_
from sqlalchemy import Column, or_, ForeignKey
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
@ -20,6 +20,7 @@ class Orgy(Base):
start_utc = Column(Integer)
end_utc = Column(Integer)
started = Column(Boolean, default=False)
chat_id = Column(Integer, ForeignKey("chats.id"))
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -36,24 +37,24 @@ class Orgy(Base):
t += 303
return t
def get_running_orgy(v):
def get_running_orgy(v, chat_id):
if not (v and v.allowed_in_chat): return None
refresh = False
ended_orgies = g.db.query(Orgy).filter(Orgy.end_utc != None, Orgy.end_utc < time.time()).all()
ended_orgies = g.db.query(Orgy).filter(Orgy.chat_id == chat_id, Orgy.end_utc != None, Orgy.end_utc < time.time()).all()
for orgy in ended_orgies:
if orgy.started:
refresh = True
g.db.delete(orgy)
orgy = g.db.query(Orgy).filter(Orgy.start_utc < time.time()).order_by(Orgy.start_utc).first()
orgy = g.db.query(Orgy).filter(Orgy.chat_id == chat_id, Orgy.start_utc < time.time()).order_by(Orgy.start_utc).first()
if orgy and not orgy.started:
orgy.started = True
g.db.add(orgy)
refresh = True
if refresh:
requests.post('http://localhost:5001/refresh_chat', headers={"Host": SITE})
requests.post(f'http://localhost:5001/chat/{chat_id}/refresh_chat', headers={"User-Agent": "refreshing_chat", "Host": SITE})
return orgy

View File

@ -1926,118 +1926,6 @@ def admin_reset_password(user_id, v):
return {"message": f"@{user.username}'s password has been reset! The new password has been messaged to them and to you!"}
@app.get("/admin/orgies")
@admin_level_required(PERMS['ORGIES'])
def orgy_control(v):
orgies = g.db.query(Orgy).order_by(Orgy.start_utc).all()
return render_template("admin/orgy_control.html", v=v, orgies=orgies)
@app.post("/admin/schedule_orgy")
@admin_level_required(PERMS['ORGIES'])
def schedule_orgy(v):
link = request.values.get("link", "").strip()
title = request.values.get("title", "").strip()
start_utc = request.values.get("start_utc", "").strip()
if not link:
abort(400, "A link is required!")
if not title:
abort(400, "A title is required!")
normalized_link = normalize_url(link)
if start_utc:
start_utc = int(start_utc)
else:
last_orgy = g.db.query(Orgy).order_by(Orgy.start_utc.desc()).first()
if last_orgy and last_orgy.end_utc:
start_utc = last_orgy.end_utc
else:
start_utc = int(time.time())
end_utc = None
if bare_youtube_regex.match(normalized_link):
orgy_type = 'youtube'
data, _ = get_youtube_id_and_t(normalized_link)
if YOUTUBE_KEY != DEFAULT_CONFIG_VALUE:
req = requests.get(f"https://www.googleapis.com/youtube/v3/videos?id={data}&key={YOUTUBE_KEY}&part=contentDetails", headers=HEADERS, timeout=5).json()
duration = req['items'][0]['contentDetails']['duration']
if duration != 'P0D':
duration = isodate.parse_duration(duration).total_seconds()
end_utc = int(start_utc + duration)
orgy_type = 'file'
ydl_opts = {
"quiet": True,
"simulate": True,
"forceurl": True,
'format': 'b',
'proxy': PROXY_URL
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(f"https://www.youtube.com/watch?v={data}")
data = info["url"]
elif rumble_regex.match(normalized_link):
orgy_type = 'rumble'
data = normalized_link
elif twitch_regex.match(normalized_link):
orgy_type = 'twitch'
data = twitch_regex.search(normalized_link).group(3)
elif any((normalized_link.lower().endswith(f'.{x}') for x in VIDEO_FORMATS)):
orgy_type = 'file'
data = normalized_link
video_info = ffmpeg.probe(data, headers=f'referer:{SITE_FULL}/chat')
duration = float(video_info['streams'][0]['duration'])
if duration == 2.0: raise
if duration > 3000:
duration += 300 #account for break
end_utc = int(start_utc + duration)
else:
abort(400)
data = data.strip()
orgy = Orgy(
title=title,
type=orgy_type,
data=data,
start_utc=start_utc,
end_utc=end_utc,
)
g.db.add(orgy)
ma = ModAction(
kind="schedule_orgy",
user_id=v.id,
_note=f'<a href="{data}" rel="nofollow noopener">{title}</a>',
)
g.db.add(ma)
return redirect('/admin/orgies')
@app.post("/admin/remove_orgy/<int:created_utc>")
@admin_level_required(PERMS['ORGIES'])
def remove_orgy(v, created_utc):
orgy = g.db.query(Orgy).filter_by(created_utc=created_utc).one()
ma = ModAction(
kind="remove_orgy",
user_id=v.id,
_note=f'<a href="{orgy.data}" rel="nofollow noopener">{orgy.title}</a>',
)
g.db.add(ma)
started = orgy.started
g.db.delete(orgy)
g.db.commit()
if started:
requests.post('http://localhost:5001/refresh_chat', headers={"Host": SITE})
return {"message": "Orgy stopped successfully!"}
@app.get("/admin/insert_transaction")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)

View File

@ -24,7 +24,7 @@ def before_request():
abort(403, "Cloudflare workers are not allowed to access this website!")
g.agent = request.headers.get("User-Agent", "")
if not g.agent and request.path not in {'/kofi', '/bm', '/refresh_chat'}:
if not g.agent and request.path not in {'/kofi', '/bm'}:
abort(403, 'Please use a "User-Agent" header!')
if not get_setting('bots') and request.headers.get("Authorization"):

View File

@ -58,9 +58,9 @@ def commit_and_close():
g.db.close()
stdout.flush()
@app.post('/refresh_chat')
def refresh_chat():
emit('refresh_chat', namespace='/', to=f'{SITE_FULL}/chat/1')
@app.post('/chat/<int:chat_id>/refresh_chat')
def refresh_chat(chat_id):
emit('refresh_chat', namespace='/', to=f'{SITE_FULL}/chat/{chat_id}')
return ''
@socketio.on('speak')

View File

@ -1,5 +1,6 @@
from files.classes.chats import *
from files.classes.orgy import *
from files.routes.wrappers import *
from files.helpers.config.const import *
from files.helpers.get import *
@ -91,6 +92,11 @@ def chat(v, chat_id):
sorted_memberships = [owner_membership] + sorted_memberships
orgy = get_running_orgy(v, chat_id)
if orgy:
orgies = g.db.query(Orgy).filter_by(chat_id=chat_id).order_by(Orgy.start_utc).all()
return render_template("orgy.html", v=v, messages=displayed_messages, chat=chat, orgy=orgy, orgies=orgies)
return render_template("chat.html", v=v, messages=displayed_messages, chat=chat, sorted_memberships=sorted_memberships)
@ -141,3 +147,149 @@ def leave_chat(v, chat_id):
g.db.add(chat_leave)
return {"message": "Chat left successfully!"}
@app.get("/chat/<int:chat_id>/orgies")
@auth_required
def orgy_control(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
abort(404, "Chat not found!")
if chat.id == 1:
if v.admin_level < PERMS["ORGIES"]:
abort(403, "Your admin-level is not sufficient enough for this action!")
elif v.id != chat.owner_id:
abort(403, "Only the chat owner can manage its orgies!")
orgies = g.db.query(Orgy).filter_by(chat_id=chat_id).order_by(Orgy.start_utc).all()
return render_template("orgy_control.html", v=v, orgies=orgies, chat=chat)
@app.post("/chat/<int:chat_id>/schedule_orgy")
@auth_required
def schedule_orgy(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
abort(404, "Chat not found!")
if chat.id == 1:
if v.admin_level < PERMS["ORGIES"]:
abort(403, "Your admin-level is not sufficient enough for this action!")
elif v.id != chat.owner_id:
abort(403, "Only the chat owner can manage its orgies!")
link = request.values.get("link", "").strip()
title = request.values.get("title", "").strip()
start_utc = request.values.get("start_utc", "").strip()
if not link:
abort(400, "A link is required!")
if not title:
abort(400, "A title is required!")
normalized_link = normalize_url(link)
if start_utc:
start_utc = int(start_utc)
else:
last_orgy = g.db.query(Orgy).order_by(Orgy.start_utc.desc()).first()
if last_orgy and last_orgy.end_utc:
start_utc = last_orgy.end_utc
else:
start_utc = int(time.time())
end_utc = None
if bare_youtube_regex.match(normalized_link):
orgy_type = 'youtube'
data, _ = get_youtube_id_and_t(normalized_link)
if YOUTUBE_KEY != DEFAULT_CONFIG_VALUE:
req = requests.get(f"https://www.googleapis.com/youtube/v3/videos?id={data}&key={YOUTUBE_KEY}&part=contentDetails", headers=HEADERS, timeout=5).json()
duration = req['items'][0]['contentDetails']['duration']
if duration != 'P0D':
duration = isodate.parse_duration(duration).total_seconds()
end_utc = int(start_utc + duration)
orgy_type = 'file'
ydl_opts = {
"quiet": True,
"simulate": True,
"forceurl": True,
'format': 'b',
'proxy': PROXY_URL
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(f"https://www.youtube.com/watch?v={data}")
data = info["url"]
elif rumble_regex.match(normalized_link):
orgy_type = 'rumble'
data = normalized_link
elif twitch_regex.match(normalized_link):
orgy_type = 'twitch'
data = twitch_regex.search(normalized_link).group(3)
elif any((normalized_link.lower().endswith(f'.{x}') for x in VIDEO_FORMATS)):
orgy_type = 'file'
data = normalized_link
video_info = ffmpeg.probe(data, headers=f'referer:{SITE_FULL}/chat')
duration = float(video_info['streams'][0]['duration'])
if duration == 2.0: raise
if duration > 3000:
duration += 300 #account for break
end_utc = int(start_utc + duration)
else:
abort(400)
data = data.strip()
orgy = Orgy(
title=title,
type=orgy_type,
data=data,
start_utc=start_utc,
end_utc=end_utc,
chat_id=chat_id,
)
g.db.add(orgy)
if chat.id == 1:
ma = ModAction(
kind="schedule_orgy",
user_id=v.id,
_note=f'<a href="{data}" rel="nofollow noopener">{title}</a>',
)
g.db.add(ma)
return redirect(f"/chat/{chat_id}/orgies")
@app.post("/chat/<int:chat_id>/remove_orgy/<int:created_utc>")
@auth_required
def remove_orgy(v, created_utc, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
abort(404, "Chat not found!")
if chat.id == 1:
if v.admin_level < PERMS["ORGIES"]:
abort(403, "Your admin-level is not sufficient enough for this action!")
elif v.id != chat.owner_id:
abort(403, "Only the chat owner can manage its orgies!")
orgy = g.db.query(Orgy).filter_by(created_utc=created_utc).one()
if chat.id == 1:
ma = ModAction(
kind="remove_orgy",
user_id=v.id,
_note=f'<a href="{orgy.data}" rel="nofollow noopener">{orgy.title}</a>',
)
g.db.add(ma)
started = orgy.started
g.db.delete(orgy)
g.db.commit()
if started:
requests.post(f'http://localhost:5001/chat/{chat_id}/refresh_chat', headers={"User-Agent": "refreshing_chat", "Host": SITE})
return {"message": "Orgy stopped successfully!"}

View File

@ -14,7 +14,7 @@
{% if v.admin_level >= PERMS['ORGIES'] %}
<h4>Orgies</h4>
<ul>
<li><a href="/admin/orgies">Schedule/Stop Orgy</a></li>
<li><a href="/chat/1/orgies">Schedule/Stop Orgy</a></li>
</ul>
{%- endif %}

View File

@ -26,6 +26,8 @@
<input id="chat-name" autocomplete="off" class="form-control d-inline-block" name="new_name" value="{{chat.name}}" style="max-width:300px">
<button type="submit" class="btn btn-primary" style="margin-top:-5px">Save</button>
</form>
<a href="/chat/{{chat.id}}/orgies" class="fas fa-tv text-muted ml-2"></a>
{% else %}
<button id="leave-private-chat" type="submit" class="btn btn-danger ml-1 ml-md-3 px-2 text-small-sm" data-nonce="{{g.nonce}}" data-onclick="areyousure(this)" data-areyousure="postToastReload(this, '/chat/{{chat.id}}/leave')">Leave</button>
{% endif %}

View File

@ -1,6 +1,7 @@
{%- extends 'root.html' -%}
{% block body_attributes %}id="{% if request.path != '/comments' %}{% block pagetype %}frontpage{% endblock %}{% endif %}" class="has_header"{% endblock %}
{% block body %}
{% set central_orgy = get_running_orgy(v, 1) %}
{% block pulltorefresh %}
<div id="pulltorefresh">
<img id="pulltorefresh-img" src="{{SITE_FULL_IMAGES}}/e/marseyunamused.webp">
@ -22,7 +23,7 @@
{% set src = hole.random_banner %}
{% set alt = ['/h/', hole, ' banner']|join %}
{% set class = 'site-banner-hole' %}
{% elif get_running_orgy(v) and os_path.exists(path ~ "/orgy_banners") %}
{% elif central_orgy and os_path.exists(path ~ "/orgy_banners") %}
{% set src = macros.random_image("assets/images/" ~ SITE_NAME ~ "/orgy_banners") %}
{% set href = "/chat/1" %}
{% set expand = false %}

View File

@ -286,13 +286,13 @@
{%- if FEATURES['CHAT'] -%}
<li class="nav-item d-none d-lg-flex align-items-center justify-content-center text-center mx-1" id="header--chat--item">
<a class="nav-link position-relative" href="/chat/1" style="margin-top: {% if get_running_orgy(v) %}-4px{% else %}-1.6px{% endif %} !important">
{% if get_running_orgy(v) %}
<a class="nav-link position-relative" href="/chat/1" style="margin-top: {% if central_orgy %}-4px{% else %}-1.6px{% endif %} !important">
{% if central_orgy %}
<img src="{{SITE_FULL_IMAGES}}/i/orgy.webp" width="30">
{% else %}
<i class="fas fa-messages" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Chat"></i>
{% endif %}
<b id="chat-count-header-bar" class="text-lg {% if get_running_orgy(v) or g.loggedin_chat > 40 %}text-danger{% elif g.loggedin_chat > 20 %}text-yellow{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Users in chat right now">
<b id="chat-count-header-bar" class="text-lg {% if central_orgy or g.loggedin_chat > 40 %}text-danger{% elif g.loggedin_chat > 20 %}text-yellow{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Users in chat right now">
{{g.loggedin_chat}}
</b>
</a>

View File

@ -47,10 +47,10 @@
</button>
{% if v %}
{%- if FEATURES['CHAT'] -%}
<button type="button" class="nobackground border-0 col px-0 btn btn-dead m-0 {% if not get_running_orgy(v) %}pt-0{% endif %}">
<button type="button" class="nobackground border-0 col px-0 btn btn-dead m-0 {% if not central_orgy %}pt-0{% endif %}">
<a href="/chat/1" class="text-decoration-none">
<div class="text-center {% if request|selected_tab=='chat' %}text-primary{% else %}text-muted{% endif %}">
{%- if get_running_orgy(v) -%}
{%- if central_orgy -%}
<img src="{{SITE_FULL_IMAGES}}/i/orgy.webp" width="30">
<div class="text-small-extra text-danger">Chat ({{g.loggedin_chat}})</div>
{%- else -%}

View File

@ -1,5 +1,5 @@
{%- extends 'root.html' -%}
{% block pagetitle -%}Chat{%- endblock %}
{% block pagetitle -%}{{chat.name}}{%- endblock %}
{% block pagetype %}chat{% endblock %}
{% block body_attributes %}class="has_header"{% endblock %}
{% block body %}
@ -12,7 +12,8 @@
{% set vlink = '<a href="/id/' ~ v.id ~ '"' %}
<div class="orgy-top-container">
<div id="orgy-col" class="col text-left px-md-2">
<h3 id="orgy-title" class="ml-3 mt-3 mb-4">{{orgy.title}}</h1>
<h3 id="orgy-title" class="ml-3 mt-3 mb-4 d-inline-block">{{orgy.title}}</h3>
<a href="/chat/{{chat.id}}/orgies" class="fas fa-tv text-muted ml-2"></a>
<div>
<p id="orgy-file-container" class="resizable">
{% if orgy.type == 'youtube' %}

View File

@ -12,7 +12,7 @@
</div>
<div class="body d-lg-flex">
<div class="w-lg-100">
<form id="orgy" action="/admin/schedule_orgy" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<form id="orgy" action="/chat/{{chat.id}}/schedule_orgy" method="post" data-nonce="{{g.nonce}}" data-onsubmit="sendFormXHRReload(this)">
<div class="d-lg-flex border-bottom mt-5">
<div class="title w-lg-25">
<label for="title">Title</label>
@ -67,7 +67,7 @@
<a href="{{orgy.data}}" rel="nofollow noopener">{{orgy.title}}</a>
</td>
<td>
<button type="button" class="btn btn-danger btn-block" data-nonce="{{g.nonce}}" data-areyousure="remove_orgy(this, {{orgy.created_utc}})" data-onclick="areyousure(this)">Remove</button>
<button type="button" class="btn btn-danger btn-block" data-nonce="{{g.nonce}}" data-areyousure="remove_orgy(this, {{chat.id}}, {{orgy.created_utc}})" data-onclick="areyousure(this)">Remove</button>
</td>
{% if orgy.started %}
<td>Started</td>
@ -80,5 +80,5 @@
</table>
</div>
<script defer src="{{'js/admin/orgies.js' | asset}}"></script>
<script defer src="{{'js/orgies.js' | asset}}"></script>
{% endblock %}

View File

@ -0,0 +1,2 @@
alter table orgies add column chat_id integer not null;
alter table orgies add constraint chat_messages_chat_fkey foreign key (chat_id) references public.chats(id);