API: post reports (creation)

This commit is contained in:
freamon 2025-01-18 17:56:09 +00:00
parent dc5c0fd7b4
commit eb7af155ee
7 changed files with 143 additions and 8 deletions

View file

@ -1,7 +1,7 @@
from app.api.alpha import bp from app.api.alpha import bp
from app.api.alpha.utils import get_site, post_site_block, \ from app.api.alpha.utils import get_site, post_site_block, \
get_search, \ get_search, \
get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, post_post, put_post, post_post_delete, \ get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, post_post, put_post, post_post_delete, post_post_report, \
get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply, post_reply_delete, post_reply_report, \ get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply, post_reply_delete, post_reply_report, \
get_community_list, get_community, post_community_follow, post_community_block, \ get_community_list, get_community, post_community_follow, post_community_block, \
get_user, post_user_block get_user, post_user_block
@ -195,6 +195,18 @@ def post_alpha_post_delete():
return jsonify({"error": str(ex)}), 400 return jsonify({"error": str(ex)}), 400
@bp.route('/api/alpha/post/report', methods=['POST'])
def post_alpha_post_report():
if not enable_api():
return jsonify({'error': 'alpha api is not enabled'})
try:
auth = request.headers.get('Authorization')
data = request.get_json(force=True) or {}
return jsonify(post_post_report(auth, data))
except Exception as ex:
return jsonify({"error": str(ex)}), 400
# Reply # Reply
@bp.route('/api/alpha/comment/list', methods=['GET']) @bp.route('/api/alpha/comment/list', methods=['GET'])
def get_alpha_comment_list(): def get_alpha_comment_list():

View file

@ -1,6 +1,6 @@
from app.api.alpha.utils.site import get_site, post_site_block from app.api.alpha.utils.site import get_site, post_site_block
from app.api.alpha.utils.misc import get_search from app.api.alpha.utils.misc import get_search
from app.api.alpha.utils.post import get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, post_post, put_post, post_post_delete from app.api.alpha.utils.post import get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, post_post, put_post, post_post_delete, post_post_report
from app.api.alpha.utils.reply import get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply, post_reply_delete, post_reply_report from app.api.alpha.utils.reply import get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply, post_reply_delete, post_reply_report
from app.api.alpha.utils.community import get_community, get_community_list, post_community_follow, post_community_block from app.api.alpha.utils.community import get_community, get_community_list, post_community_follow, post_community_block
from app.api.alpha.utils.user import get_user, post_user_block from app.api.alpha.utils.user import get_user, post_user_block

View file

@ -1,9 +1,9 @@
from app import cache from app import cache
from app.api.alpha.views import post_view from app.api.alpha.views import post_view, post_report_view
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected, string_expected from app.api.alpha.utils.validators import required, integer_expected, boolean_expected, string_expected
from app.constants import POST_TYPE_ARTICLE, POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_VIDEO from app.constants import POST_TYPE_ARTICLE, POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_VIDEO
from app.models import Post, Community, CommunityMember, utcnow from app.models import Post, Community, CommunityMember, utcnow
from app.shared.post import vote_for_post, bookmark_the_post, remove_the_bookmark_from_post, toggle_post_notification, make_post, edit_post, delete_post, restore_post from app.shared.post import vote_for_post, bookmark_the_post, remove_the_bookmark_from_post, toggle_post_notification, make_post, edit_post, delete_post, restore_post, report_post
from app.utils import authorise_api_user, blocked_users, blocked_communities, blocked_instances, community_ids_from_instances, is_image_url, is_video_url from app.utils import authorise_api_user, blocked_users, blocked_communities, blocked_instances, community_ids_from_instances, is_image_url, is_video_url
from datetime import timedelta from datetime import timedelta
@ -222,3 +222,20 @@ def post_post_delete(auth, data):
post_json = post_view(post=post, variant=4, user_id=user_id) post_json = post_view(post=post, variant=4, user_id=user_id)
return post_json return post_json
def post_post_report(auth, data):
required(['post_id', 'reason'], data)
integer_expected(['post_id'], data)
string_expected(['reason'], data)
post_id = data['post_id']
reason = data['reason']
input = {'reason': reason, 'description': '', 'report_remote': True}
user_id, report = report_post(post_id, input, SRC_API, auth)
post_json = post_report_view(report=report, post_id=post_id, user_id=user_id)
return post_json

View file

@ -373,6 +373,47 @@ def reply_report_view(report, reply_id, user_id):
return v1 return v1
def post_report_view(report, post_id, user_id):
# views/post_report_view.dart - /post/report api endpoint
post_json = post_view(post=post_id, variant=2, user_id=user_id)
community_json = community_view(community=post_json['post']['community_id'], variant=1, stub=True)
banned = db.session.execute(text('SELECT user_id FROM "community_ban" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': report.reporter_id, 'community_id': community_json['id']}).scalar()
moderator = db.session.execute(text('SELECT is_moderator FROM "community_member" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': report.reporter_id, 'community_id': community_json['id']}).scalar()
admin = db.session.execute(text('SELECT user_id FROM "user_role" WHERE user_id = :user_id and role_id = 4'), {'user_id': report.reporter_id}).scalar()
creator_banned_from_community = True if banned else False
creator_is_moderator = True if moderator else False
creator_is_admin = True if admin else False
v1 = {
'post_report_view': {
'post_report': {
'id': report.id,
'creator_id': report.reporter_id,
'post_id': report.suspect_post_id,
'original_post_name': post_json['post']['title'],
'original_post_body': '',
'reason': report.reasons,
'resolved': report.status == 3,
'published': report.created_at.isoformat() + 'Z'
},
'post': post_json['post'],
'community': community_json,
'creator': user_view(user=user_id, variant=1, stub=True),
'post_creator': user_view(user=report.suspect_user_id, variant=1, stub=True),
'counts': post_json['counts'],
'creator_banned_from_community': creator_banned_from_community,
'creator_is_moderator': creator_is_moderator,
'creator_is_admin': creator_is_admin,
'creator_blocked': False,
'subscribed': post_json['subscribed'],
'saved': post_json['saved']
}
}
return v1
def search_view(type): def search_view(type):
v1 = { v1 = {
'type_': type, 'type_': type,

View file

@ -2,7 +2,7 @@ from app import db, cache
from app.activitypub.util import make_image_sizes from app.activitypub.util import make_image_sizes
from app.constants import * from app.constants import *
from app.community.util import tags_from_string_old, end_poll_date from app.community.util import tags_from_string_old, end_poll_date
from app.models import File, Language, NotificationSubscription, Poll, PollChoice, Post, PostBookmark, PostVote, utcnow from app.models import File, Language, Notification, NotificationSubscription, Poll, PollChoice, Post, PostBookmark, PostVote, Report, Site, User, utcnow
from app.shared.tasks import task_selector from app.shared.tasks import task_selector
from app.utils import render_template, authorise_api_user, shorten_string, gibberish, ensure_directory_exists, \ from app.utils import render_template, authorise_api_user, shorten_string, gibberish, ensure_directory_exists, \
piefed_markdown_to_lemmy_markdown, markdown_to_html, remove_tracking_from_link, domain_from_url, \ piefed_markdown_to_lemmy_markdown, markdown_to_html, remove_tracking_from_link, domain_from_url, \
@ -473,3 +473,61 @@ def restore_post(post_id, src, auth):
return user_id, post return user_id, post
else: else:
return return
def report_post(post_id, input, src, auth=None):
if src == SRC_API:
post = Post.query.filter_by(id=post_id).one()
user_id = authorise_api_user(auth)
reason = input['reason']
description = input['description']
report_remote = input['report_remote']
else:
post = Post.query.get_or_404(post_id)
user_id = current_user.id
reason = input.reasons_to_string(input.reasons.data)
description = input.description.data
report_remote = input.report_remote.data
if post.reports == -1: # When a mod decides to ignore future reports, post.reports is set to -1
if src == SRC_API:
raise Exception('already_reported')
else:
flash(_('Post has already been reported, thank you!'))
return
report = Report(reasons=reason, description=description, type=1, reporter_id=user_id, suspect_post_id=post.id, suspect_community_id=post.community_id,
suspect_user_id=post.user_id, in_community_id=post.community_id, source_instance_id=1)
db.session.add(report)
# Notify moderators
already_notified = set()
for mod in post.community.moderators():
moderator = User.query.get(mod.user_id)
if moderator and moderator.is_local():
notification = Notification(user_id=mod.user_id, title=_('A comment has been reported'),
url=f"https://{current_app.config['SERVER_NAME']}/comment/{post.id}",
author_id=user_id)
db.session.add(notification)
already_notified.add(mod.user_id)
post.reports += 1
# todo: only notify admins for certain types of report
for admin in Site.admins():
if admin.id not in already_notified:
notify = Notification(title='Suspicious content', url='/admin/reports', user_id=admin.id, author_id=user_id)
db.session.add(notify)
admin.unread_notifications += 1
db.session.commit()
# federate report to originating instance
if not post.community.is_local() and report_remote:
summary = reason
if description:
summary += ' - ' + description
task_selector('report_post', user_id=user_id, post_id=post_id, summary=summary)
if src == SRC_API:
return user_id, report
else:
return

View file

@ -2,7 +2,7 @@ from app.shared.tasks.follows import join_community, leave_community
from app.shared.tasks.likes import vote_for_post, vote_for_reply from app.shared.tasks.likes import vote_for_post, vote_for_reply
from app.shared.tasks.notes import make_reply, edit_reply from app.shared.tasks.notes import make_reply, edit_reply
from app.shared.tasks.deletes import delete_reply, restore_reply, delete_post, restore_post from app.shared.tasks.deletes import delete_reply, restore_reply, delete_post, restore_post
from app.shared.tasks.flags import report_reply from app.shared.tasks.flags import report_reply, report_post
from app.shared.tasks.pages import make_post, edit_post from app.shared.tasks.pages import make_post, edit_post
from flask import current_app from flask import current_app
@ -22,7 +22,8 @@ def task_selector(task_key, send_async=True, **kwargs):
'make_post': make_post, 'make_post': make_post,
'edit_post': edit_post, 'edit_post': edit_post,
'delete_post': delete_post, 'delete_post': delete_post,
'restore_post': restore_post 'restore_post': restore_post,
'report_post': report_post
} }
if current_app.debug: if current_app.debug:

View file

@ -1,6 +1,6 @@
from app import celery from app import celery
from app.activitypub.signature import default_context, post_request from app.activitypub.signature import default_context, post_request
from app.models import CommunityBan, PostReply, User from app.models import CommunityBan, Post, PostReply, User
from app.utils import gibberish, instance_banned from app.utils import gibberish, instance_banned
from flask import current_app from flask import current_app
@ -27,6 +27,12 @@ def report_reply(send_async, user_id, reply_id, summary):
report_object(user_id, reply, summary) report_object(user_id, reply, summary)
@celery.task
def report_post(send_async, user_id, post_id, summary):
post = Post.query.filter_by(id=post_id).one()
report_object(user_id, post, summary)
def report_object(user_id, object, summary): def report_object(user_id, object, summary):
user = User.query.filter_by(id=user_id).one() user = User.query.filter_by(id=user_id).one()
community = object.community community = object.community