拉黑与危险用户
This commit is contained in:
46
hole.py
46
hole.py
@@ -10,7 +10,7 @@ from sqlalchemy.sql.expression import func
|
||||
|
||||
from mastodon import Mastodon
|
||||
from models import db, User, Post, Comment, Attention, TagRecord, Syslog
|
||||
from utils import get_current_username, map_post, map_comment, map_syslog, check_attention, hash_name, look, get_num, tmp_token, is_admin, check_can_del, rds, RDS_KEY_POLL_OPTS, RDS_KEY_POLL_VOTES, gen_poll_dict, name_with_tmp_limit
|
||||
from utils import get_current_username, map_post, map_comment, map_syslog, check_attention, hash_name, look, get_num, tmp_token, is_admin, check_can_del, rds, RDS_KEY_POLL_OPTS, RDS_KEY_POLL_VOTES, gen_poll_dict, name_with_tmp_limit, RDS_KEY_BLOCK_SET, RDS_KEY_BLOCKED_COUNT, RDS_KEY_DANGEROUS_USERS
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///hole.db'
|
||||
@@ -39,6 +39,10 @@ limiter = Limiter(
|
||||
)
|
||||
|
||||
PER_PAGE = 50
|
||||
DANGEROUS_USER_THRESHOLD = 10
|
||||
|
||||
# 重置后旧的被拉黑次数可以丢弃了,但其他仍需要保留
|
||||
rds.delete(RDS_KEY_BLOCKED_COUNT)
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
@@ -558,5 +562,45 @@ def add_vote():
|
||||
}
|
||||
|
||||
|
||||
@app.route('/_api/v1/block', methods=['POST'])
|
||||
@limiter.limit("15 / hour; 1 / 2 second")
|
||||
def block_user_by_target():
|
||||
username = get_current_username()
|
||||
target_type = request.form.get('type')
|
||||
target_id = request.form.get('id', type=int)
|
||||
|
||||
if username.startswith('tmp_'):
|
||||
raise APIError('临时用户无法拉黑')
|
||||
|
||||
if target_type == 'post':
|
||||
target = Post.query.get_or_404(target_id)
|
||||
elif target_type == 'comment':
|
||||
target = Comment.query.get_or_404(target_id)
|
||||
else:
|
||||
raise APIError('无效的type')
|
||||
|
||||
if hash_name(username) == target.name_hash:
|
||||
raise APIError('不可拉黑自己')
|
||||
|
||||
if is_admin(username):
|
||||
rds.sadd(RDS_KEY_DANGEROUS_USERS, target.name_hash)
|
||||
curr_cnt = rds.hget(RDS_KEY_BLOCKED_COUNT, target.name_hash)
|
||||
else:
|
||||
if rds.sismember(RDS_KEY_BLOCK_SET % username, target.name_hash):
|
||||
raise APIError('已经拉黑了')
|
||||
rds.sadd(RDS_KEY_BLOCK_SET % username, target.name_hash)
|
||||
curr_cnt = rds.hincrby(RDS_KEY_BLOCKED_COUNT, target.name_hash, 1)
|
||||
if curr_cnt >= DANGEROUS_USER_THRESHOLD:
|
||||
rds.sadd(RDS_KEY_DANGEROUS_USERS, target.name_hash)
|
||||
|
||||
return {
|
||||
'code': 0,
|
||||
'data': {
|
||||
'curr': curr_cnt,
|
||||
'threshold': DANGEROUS_USER_THRESHOLD
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
|
||||
27
utils.py
27
utils.py
@@ -10,6 +10,10 @@ from config import RDS_CONFIG, ADMINS, ENABLE_TMP
|
||||
RDS_KEY_POLL_OPTS = 'hole_thu:poll_opts:%s'
|
||||
RDS_KEY_POLL_VOTES = 'hole_thu:poll_votes:%s:%s'
|
||||
|
||||
RDS_KEY_BLOCK_SET = 'hole_thu:block_list:%s' # key的参数是name而非namehash,为了方便清理和持续拉黑。拉黑名单不那么敏感,应该可以接受后台实名。value是namehash。
|
||||
RDS_KEY_BLOCKED_COUNT = 'hole_thu:blocked_count' # namehash -> 被拉黑次数
|
||||
RDS_KEY_DANGEROUS_USERS = 'hole_thu:dangerous_users'
|
||||
|
||||
rds = redis.Redis(**RDS_CONFIG)
|
||||
|
||||
|
||||
@@ -52,11 +56,14 @@ def hash_name(name):
|
||||
|
||||
|
||||
def map_post(p, name, mc=50):
|
||||
blocked = is_blocked(p.name_hash, name)
|
||||
# TODO: 如果未来量大还是sql里not in一下
|
||||
r = {
|
||||
'blocked': blocked,
|
||||
'pid': p.id,
|
||||
'likenum': p.likenum,
|
||||
'cw': p.cw,
|
||||
'text': p.content,
|
||||
'text': '' if blocked else p.content,
|
||||
'timestamp': p.timestamp,
|
||||
'type': p.post_type,
|
||||
'url': p.file_url,
|
||||
@@ -65,7 +72,7 @@ def map_post(p, name, mc=50):
|
||||
'attention': check_attention(name, p.id),
|
||||
'can_del': check_can_del(name, p.name_hash),
|
||||
'allow_search': bool(p.search_text),
|
||||
'poll': gen_poll_dict(p.id, name)
|
||||
'poll': None if blocked else gen_poll_dict(p.id, name)
|
||||
}
|
||||
if is_admin(name):
|
||||
r['hot_score'] = p.hot_score
|
||||
@@ -98,6 +105,19 @@ def name_with_tmp_limit(name: str) -> str:
|
||||
'tmp_') else name
|
||||
|
||||
|
||||
def is_blocked(target_name_hash, name):
|
||||
if rds.sismember(RDS_KEY_BLOCK_SET % name, target_name_hash):
|
||||
return True
|
||||
if rds.sismember(
|
||||
RDS_KEY_DANGEROUS_USERS, target_name_hash
|
||||
) and not (
|
||||
is_admin(name) or rds.sismember(
|
||||
RDS_KEY_DANGEROUS_USERS, hash_name(name))
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def map_comment(p, name):
|
||||
|
||||
names = {p.name_hash: 0}
|
||||
@@ -108,10 +128,11 @@ def map_comment(p, name):
|
||||
return names[nh]
|
||||
|
||||
return [{
|
||||
'blocked': (blocked := is_blocked(c.name_hash, name)),
|
||||
'cid': c.id,
|
||||
'name_id': gen_name_id(c.name_hash),
|
||||
'pid': p.id,
|
||||
'text': c.content,
|
||||
'text': '' if blocked else c.content,
|
||||
'timestamp': c.timestamp,
|
||||
'can_del': check_can_del(name, c.name_hash)
|
||||
} for c in p.comments if not (c.deleted and gen_name_id(c.name_hash) >= 0)
|
||||
|
||||
Reference in New Issue
Block a user