Browse Source

拉黑与危险用户

pull/7/head
hole-thu 4 years ago
parent
commit
ea3bf7a3b1
  1. 46
      hole.py
  2. 27
      utils.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

@ -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)

Loading…
Cancel
Save