Aevann 2023-09-06 21:02:47 +03:00
parent ab22b4b385
commit 6662eb0606
9 changed files with 108 additions and 7 deletions

View File

@ -11,6 +11,7 @@ from .reports import *
from .user import * from .user import *
from .badges import * from .badges import *
from .userblock import * from .userblock import *
from .usermute import *
from .post import * from .post import *
from .votes import * from .votes import *
from .domains import * from .domains import *

View File

@ -36,6 +36,7 @@ from .sub_relationship import *
from .sub_logs import * from .sub_logs import *
from .subscriptions import * from .subscriptions import *
from .userblock import * from .userblock import *
from .usermute import *
if SITE == 'devrama.net': if SITE == 'devrama.net':
DEFAULT_ADMIN_LEVEL = 3 DEFAULT_ADMIN_LEVEL = 3
@ -561,6 +562,10 @@ class User(Base):
def has_blocked(self, target): def has_blocked(self, target):
return g.db.query(UserBlock).filter_by(user_id=self.id, target_id=target.id).one_or_none() return g.db.query(UserBlock).filter_by(user_id=self.id, target_id=target.id).one_or_none()
@lazy
def has_muted(self, target):
return g.db.query(UserMute).filter_by(user_id=self.id, target_id=target.id).one_or_none()
@property @property
@lazy @lazy
def all_twoway_blocks(self): def all_twoway_blocks(self):
@ -1023,6 +1028,12 @@ class User(Base):
def userblocks(self): def userblocks(self):
return [x[0] for x in g.db.query(UserBlock.target_id).filter_by(user_id=self.id)] return [x[0] for x in g.db.query(UserBlock.target_id).filter_by(user_id=self.id)]
@property
@lazy
def muters(self):
return set([x[0] for x in g.db.query(UserMute.user_id).filter_by(target_id=self.id)])
def get_relationship_count(self, relationship_cls): def get_relationship_count(self, relationship_cls):
# TODO: deduplicate (see routes/users.py) # TODO: deduplicate (see routes/users.py)
if relationship_cls in {SaveRelationship, Subscription}: if relationship_cls in {SaveRelationship, Subscription}:

View File

@ -0,0 +1,19 @@
import time
from sqlalchemy import Column, ForeignKey
from sqlalchemy.sql.sqltypes import *
from files.classes import Base
class UserMute(Base):
__tablename__ = "usermutes"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
target_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<{self.__class__.__name__}(user={self.user_id}, target={self.target_id})>"

View File

@ -187,7 +187,7 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, log_cost=None, followers_pi
if not group: continue if not group: continue
member_ids = group.member_ids member_ids = group.member_ids
members = member_ids - notify_users - BOT_IDs - v.all_twoway_blocks members = member_ids - notify_users - BOT_IDs - v.all_twoway_blocks - v.muters
notify_users.update(members) notify_users.update(members)
@ -210,13 +210,10 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, log_cost=None, followers_pi
if coin_receivers: if coin_receivers:
g.db.query(User).options(load_only(User.id)).filter(User.id.in_(coin_receivers)).update({ User.coins: User.coins + 10 }) g.db.query(User).options(load_only(User.id)).filter(User.id.in_(coin_receivers)).update({ User.coins: User.coins + 10 })
if SITE == 'rdrama.net' and v.id in {256, 9287, 10489, 18701}:
notify_users.discard(AEVANN_ID)
if len(notify_users) > 400 and v.admin_level < PERMS['POST_COMMENT_INFINITE_PINGS']: if len(notify_users) > 400 and v.admin_level < PERMS['POST_COMMENT_INFINITE_PINGS']:
abort(403, "You can only notify a maximum of 400 users.") abort(403, "You can only notify a maximum of 400 users.")
return notify_users - BOT_IDs - {v.id, 0} - v.all_twoway_blocks return notify_users - BOT_IDs - {v.id, 0} - v.all_twoway_blocks - v.muters
def push_notif(uids, title, body, url_or_comment): def push_notif(uids, title, body, url_or_comment):

View File

@ -178,7 +178,7 @@ def comment(v):
if not body and not request.files.get('file'): if not body and not request.files.get('file'):
abort(400, "You need to actually write something!") abort(400, "You need to actually write something!")
if parent_user.has_blocked(v): if parent_user.has_blocked(v) or parent_user.has_muted(v):
notify_op = False notify_op = False
if request.files.get("file") and not g.is_tor: if request.files.get("file") and not g.is_tor:

View File

@ -346,6 +346,7 @@ def notifications(v):
if n.created_utc > 1620391248: c.notif_utc = n.created_utc if n.created_utc > 1620391248: c.notif_utc = n.created_utc
if not n.read: c.unread = True if not n.read: c.unread = True
c.is_notif = True
if c.parent_post or c.wall_user_id: if c.parent_post or c.wall_user_id:
all_items.append(c) all_items.append(c)

View File

@ -623,6 +623,9 @@ def message2(v, username=None, id=None):
if v.admin_level <= PERMS['MESSAGE_BLOCKED_USERS'] and hasattr(user, 'is_blocked') and user.is_blocked: if v.admin_level <= PERMS['MESSAGE_BLOCKED_USERS'] and hasattr(user, 'is_blocked') and user.is_blocked:
abort(403, f"@{user.username} is blocking you!") abort(403, f"@{user.username} is blocking you!")
if user.has_muted(v):
abort(403, f"@{user.username} is muting notifications from you, so messaging them is pointless!")
body = request.values.get("message", "") body = request.values.get("message", "")
body = body[:COMMENT_BODY_LENGTH_LIMIT].strip() body = body[:COMMENT_BODY_LENGTH_LIMIT].strip()
@ -708,6 +711,9 @@ def messagereply(v):
and hasattr(user, 'is_blocked') and user.is_blocked): and hasattr(user, 'is_blocked') and user.is_blocked):
abort(403, f"You're blocked by @{user.username}") abort(403, f"You're blocked by @{user.username}")
if user.has_muted(v):
abort(403, f"@{user.username} is muting notifications from you, so messaging them is pointless!")
if not g.is_tor and get_setting("dm_media"): if not g.is_tor and get_setting("dm_media"):
body = process_files(request.files, v, body, is_dm=True, dm_user=user) body = process_files(request.files, v, body, is_dm=True, dm_user=user)
body = body[:COMMENT_BODY_LENGTH_LIMIT].strip() #process_files potentially adds characters to the post body = body[:COMMENT_BODY_LENGTH_LIMIT].strip() #process_files potentially adds characters to the post
@ -1511,3 +1517,47 @@ def users_list(v):
page=page, page=page,
user_cards_title="Users Feed", user_cards_title="Users Feed",
) )
@app.post("/mute_notifs/<int:uid>")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@limiter.limit("20/day", deduct_when=lambda response: response.status_code < 400)
@limiter.limit("20/day", deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def mute_notifs(v, uid):
user = get_account(uid)
if user.id == v.id:
abort(400, "You can't mute notifications from yourself!")
if user.id == AUTOJANNY_ID:
abort(403, f"You can't mute notifications from @{user.username}")
if v.has_muted(user):
abort(409, f"You have already muted notifications from @{user.username}")
new_mute = UserMute(user_id=v.id, target_id=user.id)
g.db.add(new_mute)
send_notification(user.id, f"@{v.username} has muted notifications from you!")
return {"message": f"You have muted notifications from @{user.username} successfully!"}
@app.post("/unmute_notifs/<int:uid>")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@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)
@auth_required
def unmute_notifs(v, uid):
user = get_account(uid)
x = v.has_muted(user)
if not x:
abort(409, "You can't unmute notifications from someone you haven't muted notifications from!")
g.db.delete(x)
send_notification(user.id, f"@{v.username} has unmuted notifications from you!")
return {"message": f"You have unmuted notifications from @{user.username} successfully!"}

View File

@ -116,7 +116,7 @@
<div id="{% if comment_info and comment_info.id == c.id %}context{% else %}comment-{{c.id}}-only{% endif %}" class="{% if c.unread %}unread{% endif %} {% if c.award_count('glowie', v) %}glow{% endif %} comment-{{c.id}}-only comment-anchor {% if comment_info and comment_info.id == c.id %}context{% endif %}{% if c.is_banned %} banned{% endif %}{% if c.deleted_utc %} deleted{% endif %}"> <div id="{% if comment_info and comment_info.id == c.id %}context{% else %}comment-{{c.id}}-only{% endif %}" class="{% if c.unread %}unread{% endif %} {% if c.award_count('glowie', v) %}glow{% endif %} comment-{{c.id}}-only comment-anchor {% if comment_info and comment_info.id == c.id %}context{% endif %}{% if c.is_banned %} banned{% endif %}{% if c.deleted_utc %} deleted{% endif %}">
<div class="user-info"> <div class="user-info {% if request.path.startswith('/notifications') %}x-scroll{% endif %}">
<span class="comment-collapse-icon" data-nonce="{{g.nonce}}" data-onclick="collapse_comment('{{c.id}}')"></span> <span class="comment-collapse-icon" data-nonce="{{g.nonce}}" data-onclick="collapse_comment('{{c.id}}')"></span>
{% for a in c.awards %} {% for a in c.awards %}
@ -238,6 +238,11 @@
{% if c.blackjack_result %} {% if c.blackjack_result %}
{{c.blackjack_html | safe}} {{c.blackjack_html | safe}}
{% endif %} {% endif %}
{% if c.is_notif or (request.path == '/notifications/messages' and c.author_id != v.id) %}
<button type="button" class="text-danger font-weight-bold ml-1 mt-2 mr-3 p-1 {% if v.has_muted(c.author) %}d-none{% endif %}" id="mute-notifs-{{c.id}}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/mute_notifs/{{c.author.id}}','mute-notifs-{{c.id}}','unmute-notifs-{{c.id}}','d-none')">Mute notifications from @{{c.author.username}}</button>
<button type="button" class="text-success font-weight-bold ml-1 mt-2 mr-3 p-1 {% if not v.has_muted(c.author) %}d-none{% endif %}" id="unmute-notifs-{{c.id}}" data-nonce="{{g.nonce}}" data-onclick="postToastSwitch(this,'/unmute_notifs/{{c.author.id}}','mute-notifs-{{c.id}}','unmute-notifs-{{c.id}}','d-none')">Unmute notifications from @{{c.author.username}}</button>
{% endif %}
</div> </div>
{{macros.reports(c, 'comment')}} {{macros.reports(c, 'comment')}}

View File

@ -0,0 +1,17 @@
CREATE TABLE public.usermutes (
user_id integer NOT NULL,
target_id integer NOT NULL,
created_utc integer
);
ALTER TABLE ONLY public.usermutes
ADD CONSTRAINT usermutes_pkey PRIMARY KEY (user_id, target_id);
CREATE INDEX mute_target_idx ON public.usermutes USING btree (target_id);
ALTER TABLE ONLY public.usermutes
ADD CONSTRAINT mute_target_fkey FOREIGN KEY (target_id) REFERENCES public.users(id);
ALTER TABLE ONLY public.usermutes
ADD CONSTRAINT mute_user_fkey FOREIGN KEY (user_id) REFERENCES public.users(id);