API: post add/remove sticky

This commit is contained in:
freamon 2025-01-19 22:53:32 +00:00
parent e8d9176931
commit 2112bf6829
7 changed files with 222 additions and 4 deletions

View file

@ -1,7 +1,7 @@
from app.api.alpha import bp
from app.api.alpha.utils import get_site, post_site_block, \
get_search, \
get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, post_post, put_post, post_post_delete, post_post_report, post_post_lock, \
get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, post_post, put_post, post_post_delete, post_post_report, post_post_lock, post_post_feature, \
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_user, post_user_block
@ -218,6 +218,19 @@ def post_alpha_post_lock():
except Exception as ex:
return jsonify({"error": str(ex)}), 400
@bp.route('/api/alpha/post/feature', methods=['POST'])
def post_alpha_post_feature():
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_feature(auth, data))
except Exception as ex:
return jsonify({"error": str(ex)}), 400
# Reply
@bp.route('/api/alpha/comment/list', methods=['GET'])
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.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, post_post_report, post_post_lock
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, post_post_lock, post_post_feature
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.user import get_user, post_user_block

View file

@ -3,7 +3,7 @@ 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.constants import POST_TYPE_ARTICLE, POST_TYPE_LINK, POST_TYPE_IMAGE, POST_TYPE_VIDEO
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, report_post, lock_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, lock_post, sticky_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 datetime import timedelta
@ -252,3 +252,18 @@ def post_post_lock(auth, data):
post_json = post_view(post=post, variant=4, user_id=user_id)
return post_json
def post_post_feature(auth, data):
required(['post_id', 'featured', 'feature_type'], data)
integer_expected(['post_id'], data)
boolean_expected(['featured'], data)
string_expected(['feature_type'], data)
post_id = data['post_id']
featured = data['featured']
user_id, post = sticky_post(post_id, featured, SRC_API, auth)
post_json = post_view(post=post, variant=4, user_id=user_id)
return post_json

View file

@ -561,5 +561,27 @@ def lock_post(post_id, locked, src, auth=None):
return user.id, post
def sticky_post(post_id, featured, src, auth=None):
if src == SRC_API:
user = authorise_api_user(auth, return_type='model')
else:
user = current_user
post = Post.query.filter_by(id=post_id).one()
community = post.community
if post.community.is_moderator(user) or post.community.is_instance_admin(user):
post.sticky = featured
if not community.ap_featured_url:
community.ap_featured_url = community.ap_profile_id + '/featured'
db.session.commit()
if featured:
task_selector('sticky_post', user_id=user.id, post_id=post_id)
else:
task_selector('unsticky_post', user_id=user.id, post_id=post_id)
return user.id, post

View file

@ -5,6 +5,8 @@ from app.shared.tasks.deletes import delete_reply, restore_reply, delete_post, r
from app.shared.tasks.flags import report_reply, report_post
from app.shared.tasks.pages import make_post, edit_post
from app.shared.tasks.locks import lock_post, unlock_post
from app.shared.tasks.adds import sticky_post
from app.shared.tasks.removes import unsticky_post
from flask import current_app
@ -26,7 +28,9 @@ def task_selector(task_key, send_async=True, **kwargs):
'restore_post': restore_post,
'report_post': report_post,
'lock_post': lock_post,
'unlock_post': unlock_post
'unlock_post': unlock_post,
'sticky_post': sticky_post,
'unsticky_post': unsticky_post
}
if current_app.debug:

82
app/shared/tasks/adds.py Normal file
View file

@ -0,0 +1,82 @@
from app import celery
from app.activitypub.signature import default_context, post_request
from app.models import Community, Post, User
from app.utils import gibberish, instance_banned
from flask import current_app
""" JSON format
Add:
{
'id':
'type':
'actor':
'object':
'target': (featured_url or moderators_url)
'@context':
'audience':
'to': []
'cc': []
}
For Announce, remove @context from inner object, and use same fields except audience
"""
@celery.task
def sticky_post(send_async, user_id, post_id):
post = Post.query.filter_by(id=post_id).one()
add_object(user_id, post)
@celery.task
def add_mod(send_async, user_id, mod_id, community_id):
mod = User.query.filter_by(id=mod_id).one()
add_object(user_id, mod, community_id)
def add_object(user_id, object, community_id=None):
user = User.query.filter_by(id=user_id).one()
if not community_id:
community = object.community
else:
community = Community.query.filter_by(id=community_id).one()
if community.local_only or not community.instance.online():
return
add_id = f"https://{current_app.config['SERVER_NAME']}/activities/add/{gibberish(15)}"
to = ["https://www.w3.org/ns/activitystreams#Public"]
cc = [community.public_url()]
add = {
'id': add_id,
'type': 'Add',
'actor': user.public_url(),
'object': object.public_url(),
'target': community.ap_moderators_url if community_id else community.ap_featured_url,
'@context': default_context(),
'audience': community.public_url(),
'to': to,
'cc': cc
}
if community.is_local():
del add['@context']
announce_id = f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}"
actor = community.public_url()
cc = [community.ap_followers_url]
announce = {
'id': announce_id,
'type': 'Announce',
'actor': actor,
'object': add,
'@context': default_context(),
'to': to,
'cc': cc
}
for instance in community.following_instances():
if instance.inbox and instance.online() and not user.has_blocked_instance(instance.id) and not instance_banned(instance.domain):
post_request(instance.inbox, announce, community.private_key, community.public_url() + '#main-key')
else:
post_request(community.ap_inbox_url, add, user.private_key, user.public_url() + '#main-key')

View file

@ -0,0 +1,82 @@
from app import celery
from app.activitypub.signature import default_context, post_request
from app.models import Community, Post, User
from app.utils import gibberish, instance_banned
from flask import current_app
""" JSON format
Remove:
{
'id':
'type':
'actor':
'object':
'target': (featured_url or moderators_url)
'@context':
'audience':
'to': []
'cc': []
}
For Announce, remove @context from inner object, and use same fields except audience
"""
@celery.task
def unsticky_post(send_async, user_id, post_id):
post = Post.query.filter_by(id=post_id).one()
remove_object(user_id, post)
@celery.task
def remove_mod(send_async, user_id, mod_id, community_id):
mod = User.query.filter_by(id=mod_id).one()
remove_object(user_id, mod, community_id)
def remove_object(user_id, object, community_id=None):
user = User.query.filter_by(id=user_id).one()
if not community_id:
community = object.community
else:
community = Community.query.filter_by(id=community_id).one()
if community.local_only or not community.instance.online():
return
remove_id = f"https://{current_app.config['SERVER_NAME']}/activities/remove/{gibberish(15)}"
to = ["https://www.w3.org/ns/activitystreams#Public"]
cc = [community.public_url()]
remove = {
'id': remove_id,
'type': 'Remove',
'actor': user.public_url(),
'object': object.public_url(),
'target': community.ap_moderators_url if community_id else community.ap_featured_url,
'@context': default_context(),
'audience': community.public_url(),
'to': to,
'cc': cc
}
if community.is_local():
del remove['@context']
announce_id = f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}"
actor = community.public_url()
cc = [community.ap_followers_url]
announce = {
'id': announce_id,
'type': 'Announce',
'actor': actor,
'object': remove,
'@context': default_context(),
'to': to,
'cc': cc
}
for instance in community.following_instances():
if instance.inbox and instance.online() and not user.has_blocked_instance(instance.id) and not instance_banned(instance.domain):
post_request(instance.inbox, announce, community.private_key, community.public_url() + '#main-key')
else:
post_request(community.ap_inbox_url, remove, user.private_key, user.public_url() + '#main-key')