128 lines
3.8 KiB
Python
128 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
import enum
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING
|
|
|
|
from files.helpers.config.const import PERMS
|
|
|
|
if TYPE_CHECKING:
|
|
from files.classes.user import User
|
|
from files.helpers.content import Submittable
|
|
|
|
|
|
class StateMod(enum.Enum):
|
|
VISIBLE = 0
|
|
FILTERED = 1
|
|
REMOVED = 2
|
|
|
|
class StateReport(enum.Enum):
|
|
UNREPORTED = 0
|
|
RESOLVED = 1
|
|
REPORTED = 2
|
|
IGNORED = 3
|
|
|
|
|
|
@dataclass(frozen=True, kw_only=True, slots=True)
|
|
class VisibilityState:
|
|
'''
|
|
The full moderation state machine. It holds the moderation state, report
|
|
state, deleted information, and shadowban information. A decision to show
|
|
or hide a post or comment should be able to be done with information from
|
|
this alone.
|
|
'''
|
|
state_mod: StateMod
|
|
state_mod_set_by: str | None
|
|
state_report: StateReport
|
|
state_mod_set_by: str | None
|
|
deleted: bool
|
|
op_shadowbanned: bool
|
|
op_id: int
|
|
op_name_safe: str
|
|
|
|
@property
|
|
def removed(self) -> bool:
|
|
return self.state_mod == StateMod.REMOVED
|
|
|
|
@property
|
|
def filtered(self) -> bool:
|
|
return self.state_mod == StateMod.FILTERED
|
|
|
|
@property
|
|
def reports_ignored(self) -> bool:
|
|
return self.state_report == StateReport.IGNORED
|
|
|
|
@classmethod
|
|
def from_submittable(cls, target: Submittable) -> "VisibilityState":
|
|
return cls(
|
|
state_mod=target.state_mod,
|
|
state_mod_set_by=target.state_mod_set_by, # type: ignore
|
|
state_report=target.state_report,
|
|
deleted=bool(target.state_user_deleted_utc != None),
|
|
op_shadowbanned=bool(target.author.shadowbanned),
|
|
op_id=target.author_id, # type: ignore
|
|
op_name_safe=target.author_name
|
|
)
|
|
|
|
def moderated_body(self, v: User | None, is_blocking: bool=False) -> str | None:
|
|
if v and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] \
|
|
or v.id == self.op_id):
|
|
return None
|
|
if self.deleted: return 'Deleted'
|
|
if self.appear_removed(v): return 'Removed'
|
|
if self.filtered: return 'Filtered'
|
|
if is_blocking: return f'You are blocking @{self.op_name_safe}'
|
|
return None
|
|
|
|
def visibility_and_message(self, v: User | None, is_blocking: bool) -> tuple[bool, str]:
|
|
'''
|
|
Returns a tuple of whether this content is visible and a publicly
|
|
visible message to accompany it. The visibility state machine is
|
|
a slight mess but... this should at least unify the state checks.
|
|
'''
|
|
def can(v: User | None, perm_level: int) -> bool:
|
|
return v and v.admin_level >= perm_level
|
|
|
|
can_moderate: bool = can(v, PERMS['POST_COMMENT_MODERATION'])
|
|
can_shadowban: bool = can(v, PERMS['USER_SHADOWBAN'])
|
|
|
|
if v and v.id == self.op_id:
|
|
return True, "This shouldn't be here, please report it!"
|
|
if (self.removed and not can_moderate) or \
|
|
(self.op_shadowbanned and not can_shadowban):
|
|
msg: str = 'Removed'
|
|
if self.state_mod_set_by:
|
|
msg = f'Removed by @{self.state_mod_set_by}'
|
|
return False, msg
|
|
if self.filtered and not can_moderate:
|
|
return False, 'Filtered'
|
|
if self.deleted and not can_moderate:
|
|
return False, 'Deleted by author'
|
|
if is_blocking:
|
|
return False, f'You are blocking @{self.op_name_safe}'
|
|
return True, "This shouldn't be here, please report it!"
|
|
|
|
def is_visible_to(self, v: User | None, is_blocking: bool) -> bool:
|
|
return self.visibility_and_message(v, is_blocking)[0]
|
|
|
|
def replacement_message(self, v: User | None, is_blocking: bool) -> str:
|
|
return self.visibility_and_message(v, is_blocking)[1]
|
|
|
|
def appear_removed(self, v: User | None) -> bool:
|
|
if self.removed: return True
|
|
if not self.op_shadowbanned: return False
|
|
return (not v) or bool(v.admin_level < PERMS['USER_SHADOWBAN'])
|
|
|
|
@property
|
|
def publicly_visible(self) -> bool:
|
|
return all(
|
|
not state for state in
|
|
[self.deleted, self.removed, self.filtered, self.op_shadowbanned]
|
|
)
|
|
|
|
@property
|
|
def explicitly_moderated(self) -> bool:
|
|
'''
|
|
Whether this was removed or filtered and not as the result of a shadowban
|
|
'''
|
|
return self.removed or self.filtered
|