forked from rDrama/rDrama
1
0
Fork 0

what a mess

master
Aevann1 2022-03-24 21:44:12 +02:00
parent cbbeaf209c
commit eec1738d01
14 changed files with 280 additions and 244 deletions

View File

@ -13,7 +13,7 @@ from sqlalchemy import *
import gevent import gevent
import redis import redis
import time import time
from sys import stdout from sys import stdout, argv
import faulthandler import faulthandler
from json import loads from json import loads
@ -116,4 +116,8 @@ def after_request(response):
response.headers.add("X-Frame-Options", "deny") response.headers.add("X-Frame-Options", "deny")
return response return response
from files.routes import * if "load_chat" in argv or app.config["SERVER_NAME"] == 'localhost':
from files.routes.chat import *
if "load_chat" not in argv:
from files.routes import *

View File

@ -724,7 +724,7 @@ def sub_matcher_upper(match):
return SLURS[match.group(0).lower()].upper() return SLURS[match.group(0).lower()].upper()
def censor_slurs(body, logged_user): def censor_slurs(body, logged_user):
if not logged_user or logged_user.slurreplacer: if not logged_user or logged_user == 'chat' or logged_user.slurreplacer:
body = slur_regex_upper.sub(sub_matcher_upper, body) body = slur_regex_upper.sub(sub_matcher_upper, body)
body = slur_regex.sub(sub_matcher, body) body = slur_regex.sub(sub_matcher, body)
return body return body

View File

@ -326,6 +326,10 @@ def sanitize(sanitized, noimages=False, alert=False, comment=False, edit=False):
marsey.count += 1 marsey.count += 1
g.db.add(marsey) g.db.add(marsey)
if '#fortune' in sanitized:
sanitized = sanitized.replace('#fortune', '')
sanitized += '\n\n<p>' + random.choice(FORTUNE_REPLIES) + '</p>'
signal.alarm(0) signal.alarm(0)
return sanitized return sanitized

View File

@ -16,4 +16,3 @@ from .feeds import *
from .awards import * from .awards import *
from .giphy import * from .giphy import *
from .subs import * from .subs import *
from .chat import *

View File

@ -1,61 +1,65 @@
import time
from files.helpers.wrappers import auth_required
from files.helpers.sanitize import sanitize
from files.helpers.const import *
from datetime import datetime
from flask_socketio import SocketIO, emit
from files.__main__ import app, limiter, cache
from flask import render_template, make_response, send_from_directory
import sys import sys
from files.helpers.const import SITE, SITE_FULL import atexit
if "load_chat" in sys.argv or SITE == 'localhost':
import time
from files.helpers.wrappers import auth_required
from files.helpers.sanitize import sanitize
from datetime import datetime
from flask_socketio import SocketIO, emit
from files.__main__ import app, limiter, cache
from flask import render_template, make_response, send_from_directory, abort
import sys
import atexit
if SITE == 'localhost':
socketio = SocketIO(app, async_mode='gevent', cors_allowed_origins=[SITE_FULL], logger=True, engineio_logger=True, debug=True)
else:
socketio = SocketIO(app, async_mode='gevent', cors_allowed_origins=[SITE_FULL]) socketio = SocketIO(app, async_mode='gevent', cors_allowed_origins=[SITE_FULL])
typing = []
online = []
messages = cache.get('chat') or []
typing = []
online = []
messages = cache.get('chat') or []
total = cache.get('total') or 0
@app.get("/chat") @app.get("/chat")
@auth_required @auth_required
def chat( v): def chat( v):
return render_template("chat.html", v=v, messages=messages) return render_template("chat.html", v=v, messages=messages)
@app.get('/chat.js') @app.get('/chat.js')
@limiter.exempt @limiter.exempt
def chatjs(): def chatjs():
resp = make_response(send_from_directory('assets', 'js/chat.js')) resp = make_response(send_from_directory('assets', 'js/chat.js'))
return resp return resp
@socketio.on('speak') @socketio.on('speak')
@limiter.limit("3/second;10/minute") @limiter.limit("3/second;10/minute")
@auth_required @auth_required
def speak(data, v): def speak(data, v):
if v.is_banned: abort(403) if v.is_banned: return '', 403
global messages global messages, total
text = data[:1000].strip() text = data[:1000].strip()
if not text: abort(403) if not text: return '', 403
text_html = sanitize(text)
data={ data={
"avatar": v.profile_url, "avatar": v.profile_url,
"username":v.username, "username":v.username,
"namecolor":v.namecolor, "namecolor":v.namecolor,
"text":text, "text":text,
"text_html":sanitize(text), "text_html":text_html,
"text_censored":censor_slurs(text_html, 'chat')
} }
messages.append(data) messages.append(data)
messages = messages[-500:] messages = messages[-20:]
total += 1
emit('speak', data, broadcast=True) emit('speak', data, broadcast=True)
return '', 204 return '', 204
@socketio.on('connect') @socketio.on('connect')
@auth_required @auth_required
def connect(v): def connect(v):
if v.username not in online: if v.username not in online:
online.append(v.username) online.append(v.username)
emit("online", online, broadcast=True) emit("online", online, broadcast=True)
@ -63,9 +67,9 @@ if "load_chat" in sys.argv or SITE == 'localhost':
emit('typing', typing) emit('typing', typing)
return '', 204 return '', 204
@socketio.on('disconnect') @socketio.on('disconnect')
@auth_required @auth_required
def disconnect(v): def disconnect(v):
if v.username in online: if v.username in online:
online.remove(v.username) online.remove(v.username)
emit("online", online, broadcast=True) emit("online", online, broadcast=True)
@ -74,9 +78,9 @@ if "load_chat" in sys.argv or SITE == 'localhost':
emit('typing', typing, broadcast=True) emit('typing', typing, broadcast=True)
return '', 204 return '', 204
@socketio.on('typing') @socketio.on('typing')
@auth_required @auth_required
def typing_indicator(data, v): def typing_indicator(data, v):
if data and v.username not in typing: typing.append(v.username) if data and v.username not in typing: typing.append(v.username)
elif not data and v.username in typing: typing.remove(v.username) elif not data and v.username in typing: typing.remove(v.username)
@ -85,6 +89,7 @@ if "load_chat" in sys.argv or SITE == 'localhost':
return '', 204 return '', 204
def close_running_threads(): def close_running_threads():
cache.set('chat', messages) cache.set('chat', messages)
atexit.register(close_running_threads) cache.set('total', total)
atexit.register(close_running_threads)

View File

@ -335,10 +335,6 @@ def api_comment(v):
if v.agendaposter and not v.marseyawarded and parent_post.id not in ADMIGGERS: if v.agendaposter and not v.marseyawarded and parent_post.id not in ADMIGGERS:
body = torture_ap(body, v.username) body = torture_ap(body, v.username)
if '#fortune' in body:
body = body.replace('#fortune', '')
body += '\n\n<p>' + random.choice(FORTUNE_REPLIES) + '</p>'
body_html = sanitize(body, comment=True) body_html = sanitize(body, comment=True)
if v.marseyawarded and parent_post.id not in ADMIGGERS and marseyaward_body_regex.search(body_html): if v.marseyawarded and parent_post.id not in ADMIGGERS and marseyaward_body_regex.search(body_html):

View File

@ -1034,10 +1034,6 @@ def submit_post(v, sub=None):
else: else:
return error("Image/Video files only.") return error("Image/Video files only.")
if '#fortune' in body:
body = body.replace('#fortune', '')
body += '\n\n<p>' + random.choice(FORTUNE_REPLIES) + '</p>'
body_html = sanitize(body) body_html = sanitize(body)
if v.marseyawarded and marseyaward_body_regex.search(body_html): if v.marseyawarded and marseyaward_body_regex.search(body_html):

View File

@ -1,95 +1,32 @@
{% extends "default.html" %} <!DOCTYPE html>
<html lang="en">
<head>
<meta name="description" content="{{config('DESCRIPTION')}}">
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; connect-src 'self'; object-src 'none';">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="author" content="">
<link id="favicon" rel="icon" type="image/png" href="/static/assets/images/{{SITE_NAME}}/icon.webp?v=1012">
{% block title %}
<title>Chat</title> <title>Chat</title>
<meta name="description" content="Chat">
{% endblock %}
{% block content %} <style>:root{--primary:#{{v.themecolor}}}</style>
<link rel="stylesheet" href="/static/assets/css/main.css?v=188">
<link rel="stylesheet" href="/static/assets/css/{{v.theme}}.css?v=30">
{% if v.css %}
<link rel="stylesheet" href="/@{{v.username}}/css">
{% endif %}
<div class="border-right py-3 px-3"> <style>
<span data-toggle="tooltip" data-placement="bottom" data-bs-title="Users online right now" title="Users online right now" class="text-muted">
<i class="far fa-user fa-sm mr-1"></i>
<span class="board-chat-count">0</span>
</span>
</div>
<div id="chat-line-template" class="d-none">
<div class="chat-line">
<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">
</span>
<div class="pl-md-3 text-muted">
<div>
<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="quote(this)"><i class="fas fa-reply" aria-hidden="true"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container py-0 chat-container">
<div id="chat-window">
<div id="chat-text" class="fullchat">
{% for m in messages %}
{% set text_html = m['text_html'] %}
{% set link = '<a href="/id/' + v.id|string + '">' %}
<div class="chat-line {% if link in text_html %}chat-mention{% endif %}">
<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">{{text_html | safe}}</span>
<span class="d-none">{{m['text']}}</span>
<button class="btn d-inline-block pt-0" onclick="quote(this)"><i class="fas fa-reply" aria-hidden="true"></i></button>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div id="system-template">
<div class="system-line">
<p class="message text-muted"></p>
</div>
</div>
</div>
<div id='message' class="d-none position-relative form-group d-flex pb-3">
<div class="position-absolute text-muted text-small" style="bottom: -1.5rem; line-height: 1;">
<span id="typing-indicator"></span>
<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>
<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>
<input id="vid" type="hidden" value="{{v.id}}">
<input id="site_name" type="hidden" value="{{SITE_NAME}}">
<script data-cfasync="false" src="/chat.js?v=2"></script>
<style>
#chat-window { #chat-window {
max-height:calc(100vh - 300px); max-height:90vh;
overflow-y: scroll; overflow-y: scroll;
} }
.fullchat .chat-profile { #chat-window .chat-profile {
min-width: 42px; min-width: 42px;
width: 42px; width: 42px;
height: 42px; height: 42px;
@ -109,24 +46,120 @@
border-radius: 5px; border-radius: 5px;
} }
.profile-pic-30 {
width: 30px;
height: 30px;
border-radius: 50%;
text-align: center;
object-fit: cover;
}
.chat-message p { .chat-message p {
display: inline-block; display: inline-block;
} }
.diff {
margin-top: 1rem;
}
@media (max-width: 768px) { @media (max-width: 768px) {
* { #shrink * {
font-size: 10px !important; font-size: 10px !important;
} }
.fa-reply:before {
font-size: 9px;
} }
</style> .diff {
margin-top: 0.5rem;
}
}
#input-text {
max-height: 50px!important;
}
p {
margin: 0!important;
margin-left: 27px !important;
}
</style>
</head>
<body>
<script src="/static/assets/js/bootstrap.js?v=245"></script>
{% include "header.html" %}
<div class="container {% if request.path=='/' or '/post/' in request.path or '/comment/' in request.path %}transparent{% 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">
<div class="border-right py-3 px-3">
<span data-toggle="tooltip" data-placement="bottom" data-bs-title="Users online right now" title="Users online right now" class="text-muted">
<i class="far fa-user fa-sm mr-1"></i>
<span class="board-chat-count">0</span>
</span>
</div>
<div id="chat-line-template" class="d-none">
<div class="chat-line">
<div class="d-flex align-items-center">
<div class="pl-md-3 text-muted">
<div>
<img class="avatar pp20 mr-1" 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 py-0" onclick="quote(this)"><i class="fas fa-reply" aria-hidden="true"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="shrink">
<div id="chat-window" class="container py-0">
{% for m in messages %}
{% set text_html = m['text_censored'] if v.slurreplacer else m['text_html'] %}
{% set link = '<a href="/id/' + v.id|string + '">' %}
{% set same = m['username'] == messages[loop.index-2]['username'] %}
<div class="chat-line {% if link in text_html %}chat-mention{% endif %} {% if not same %}diff{% endif %}">
<div class="d-flex align-items-center">
<div class="pl-md-3 text-muted">
<div>
{% if not same %}<img src="{{m['avatar']}}" class="avatar pp20 mr-1" data-toggle="tooltip" data-placement="right">{% endif %}
<a class="{% if same %}d-none{% endif %} 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">{{text_html | safe}}</span>
<span class="d-none">{{m['text']}}</span>
<button class="btn d-inline-block py-0" onclick="quote(this)"><i class="fas fa-reply" aria-hidden="true"></i></button>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div id='message' class="d-none position-relative form-group d-flex mt-4">
<div class="position-absolute text-muted text-small" style="bottom: -1.5rem; line-height: 1;">
<span id="typing-indicator"></span>
<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>
<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>
</div>
<div id="online" class="col sidebar text-left d-none d-lg-block pt-3 bg-white" style="max-width:300px">
</div>
</div>
</div>
<input id="vid" type="hidden" value="{{v.id}}">
<input id="site_name" type="hidden" value="{{SITE_NAME}}">
<input id="slurreplacer" type="hidden" value="{{v.slurreplacer}}">
<script data-cfasync="false" src="/chat.js?v=4"></script>
<script> <script>
box.scrollTo(0, box.scrollHeight) box.scrollTo(0, box.scrollHeight)
@ -135,4 +168,6 @@
{% include "emoji_modal.html" %} {% include "emoji_modal.html" %}
{% include "expanded_image_modal.html" %} {% include "expanded_image_modal.html" %}
{% endblock %} <script src="/static/assets/js/lozad.js?v=240"></script>
</body>

View File

@ -843,7 +843,7 @@
{% if v %} {% if v %}
<script src="/static/assets/js/marked.js?v=249"></script> <script src="/static/assets/js/marked.js?v=249"></script>
<script src="/static/assets/js/comments_v.js?v=262"></script> <script src="/static/assets/js/comments_v.js?v=263"></script>
{% endif %} {% endif %}
<script src="/static/assets/js/clipboard.js?v=250"></script> <script src="/static/assets/js/clipboard.js?v=250"></script>
@ -854,7 +854,7 @@
{% include "expanded_image_modal.html" %} {% include "expanded_image_modal.html" %}
<script src="/static/assets/js/comments+submission_listing.js?v=253"></script> <script src="/static/assets/js/comments+submission_listing.js?v=254"></script>
<script src="/static/assets/js/comments.js?v=253"></script> <script src="/static/assets/js/comments.js?v=253"></script>
<script> <script>
@ -885,8 +885,9 @@
xhr.setRequestHeader('xhr', 'xhr'); xhr.setRequestHeader('xhr', 'xhr');
xhr.onload=function(){ xhr.onload=function(){
if (xhr.status==200) { if (xhr.status==200) {
document.getElementById(`morecomments-${cid}`).innerHTML = xhr.response.replace(/data-src/g, 'src').replace(/data-cfsrc/g, 'src').replace(/style="display:none;visibility:hidden;"/g, ''); let e = document.getElementById(`morecomments-${cid}`)
bs_trigger() e.innerHTML = xhr.response.replace(/data-src/g, 'src').replace(/data-cfsrc/g, 'src').replace(/style="display:none;visibility:hidden;"/g, '');
bs_trigger(e)
{% if p %} {% if p %}
comments = JSON.parse(localStorage.getItem("old-comment-counts")) || {} comments = JSON.parse(localStorage.getItem("old-comment-counts")) || {}

View File

@ -88,7 +88,7 @@
<link rel="apple-touch-icon" sizes="180x180" href="/static/assets/images/{{SITE_NAME}}/icon.webp?v=1012"> <link rel="apple-touch-icon" sizes="180x180" href="/static/assets/images/{{SITE_NAME}}/icon.webp?v=1012">
<link rel="manifest" href="/static/assets/manifest.json?v=1"> <link rel="manifest" href="/static/assets/manifest.json?v=1">
<link rel="mask-icon" href="/static/assets/images/{{SITE_NAME}}/icon.webp?v=1012"> <link rel="mask-icon" href="/static/assets/images/{{SITE_NAME}}/icon.webp?v=1012">
<link id="favicon" rel="shortcut icon" href="/static/assets/images/{{SITE_NAME}}/icon.webp?v=1012"> <link rel="shortcut icon" href="/static/assets/images/{{SITE_NAME}}/icon.webp?v=1012">
<meta name="apple-mobile-web-app-title" content="{{SITE_NAME}}"> <meta name="apple-mobile-web-app-title" content="{{SITE_NAME}}">
<meta name="application-name" content="{{SITE_NAME}}"> <meta name="application-name" content="{{SITE_NAME}}">
<meta name="msapplication-TileColor" content="#{{config('DEFAULT_COLOR')}}"> <meta name="msapplication-TileColor" content="#{{config('DEFAULT_COLOR')}}">
@ -233,8 +233,6 @@
{% block Banner %} {% block Banner %}
{% if '@' not in request.path %} {% if '@' not in request.path %}
{% if v %} {% if v %}
{% if request.path != '/chat' %}
{% if sub %} {% 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"> <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' %} {% elif SITE_NAME == 'Drama' %}
@ -249,8 +247,6 @@
<img alt="site banner" src="/static/assets/images/{{SITE_NAME}}/banner.webp?v=1042" width="100%"> <img alt="site banner" src="/static/assets/images/{{SITE_NAME}}/banner.webp?v=1042" width="100%">
</a> </a>
{% endif %} {% endif %}
{% endif %}
{% else %} {% else %}
<a href="/login"> <a href="/login">
<img class="banner" alt="site banner" src="/static/assets/images/{{SITE_NAME}}/cached.webp?v=1012" width="100%"> <img class="banner" alt="site banner" src="/static/assets/images/{{SITE_NAME}}/cached.webp?v=1012" width="100%">
@ -270,7 +266,7 @@
{% block postNav %} {% block postNav %}
{% endblock %} {% endblock %}
<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="container {% if request.path=='/' or '/post/' in request.path or '/comment/' in request.path %}transparent{% endif %}">
<div class="row justify-content-around" id="main-content-row"> <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"> <div class="col h-100 {% block customPadding %}{% if request.path.startswith('/@') %}user-gutters{% else %}custom-gutters{% endif %}{% endblock %}" id="main-content-col">
@ -297,9 +293,6 @@
{% block sidebar %} {% block sidebar %}
{% if home or sub and p %} {% if home or sub and p %}
{% include "sidebar_" + SITE_NAME + ".html" %} {% include "sidebar_" + SITE_NAME + ".html" %}
{% elif request.path == '/chat' %}
<div id="online" class="col sidebar text-left d-none d-lg-block pt-3 bg-white" style="max-width:300px">
</div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
</div> </div>

View File

@ -256,7 +256,7 @@
</div> </div>
</nav> </nav>
<script src="/static/assets/js/header.js?v=253"></script> <script src="/static/assets/js/header.js?v=254"></script>
{% if v and not err %} {% if v and not err %}
<div id="formkey" class="d-none">{{v.formkey}}</div> <div id="formkey" class="d-none">{{v.formkey}}</div>

View File

@ -999,8 +999,9 @@
xhr.setRequestHeader('xhr', 'xhr'); xhr.setRequestHeader('xhr', 'xhr');
xhr.onload=function(){ xhr.onload=function(){
if (xhr.status==200) { if (xhr.status==200) {
document.getElementById(`viewmore-${offset}`).innerHTML = xhr.response.replace(/data-src/g, 'src').replace(/data-cfsrc/g, 'src').replace(/style="display:none;visibility:hidden;"/g, ''); let e = document.getElementById(`viewmore-${offset}`);
bs_trigger() e.innerHTML = xhr.response.replace(/data-src/g, 'src').replace(/data-cfsrc/g, 'src').replace(/style="display:none;visibility:hidden;"/g, '');
bs_trigger(e)
comments = JSON.parse(localStorage.getItem("old-comment-counts")) || {} comments = JSON.parse(localStorage.getItem("old-comment-counts")) || {}
lastCount = comments['{{p.id}}'] lastCount = comments['{{p.id}}']

View File

@ -445,5 +445,5 @@
{% include "expanded_image_modal.html" %} {% include "expanded_image_modal.html" %}
<script src="/static/assets/js/clipboard.js?v=240"></script> <script src="/static/assets/js/clipboard.js?v=240"></script>
<script src="/static/assets/js/comments+submission_listing.js?v=253"></script> <script src="/static/assets/js/comments+submission_listing.js?v=254"></script>
<script src="/static/assets/js/submission_listing.js?v=240"></script> <script src="/static/assets/js/submission_listing.js?v=240"></script>

View File

@ -3648,3 +3648,5 @@ down. I dont deserve to be shamed, im just a sad pathetic woman who cares about
would. would.
{[para]} {[para]}
My husband use to snore like a chainsaw then he got nose surgery and it was like white noise and then a cpap so nice and quiet. He farts so much and I HATE IT. He is so freaking proud of them, throws his leg in the air and pretends to kick start a bike while farting. Then he tries to crop dust me. Ive asked him to stop but Nada. Im am disgusted by it. We've both put on a lot of weight in the last few years and that doesn't repulse me but his hygiene and man funk do. My husband use to snore like a chainsaw then he got nose surgery and it was like white noise and then a cpap so nice and quiet. He farts so much and I HATE IT. He is so freaking proud of them, throws his leg in the air and pretends to kick start a bike while farting. Then he tries to crop dust me. Ive asked him to stop but Nada. Im am disgusted by it. We've both put on a lot of weight in the last few years and that doesn't repulse me but his hygiene and man funk do.
{[para]}
The only thing that is advanced from this thread is my stage 4 cancer from reading it.