mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-24 03:43:42 -08:00
import community subscriptions from lemmy
This commit is contained in:
parent
47cdf79b20
commit
cd4fa6ad25
8 changed files with 119 additions and 17 deletions
|
@ -391,7 +391,7 @@ def actor_json_to_model(activity_json, address, server):
|
||||||
private_mods=activity_json['privateMods'] if 'privateMods' in activity_json else False,
|
private_mods=activity_json['privateMods'] if 'privateMods' in activity_json else False,
|
||||||
created_at=activity_json['published'] if 'published' in activity_json else utcnow(),
|
created_at=activity_json['published'] if 'published' in activity_json else utcnow(),
|
||||||
last_active=activity_json['updated'] if 'updated' in activity_json else utcnow(),
|
last_active=activity_json['updated'] if 'updated' in activity_json else utcnow(),
|
||||||
ap_id=f"{address[1:]}",
|
ap_id=f"{address[1:]}@{server}" if address.startswith('!') else f"{address}@{server}",
|
||||||
ap_public_url=activity_json['id'],
|
ap_public_url=activity_json['id'],
|
||||||
ap_profile_id=activity_json['id'],
|
ap_profile_id=activity_json['id'],
|
||||||
ap_followers_url=activity_json['followers'],
|
ap_followers_url=activity_json['followers'],
|
||||||
|
|
|
@ -291,6 +291,7 @@ def unsubscribe_everyone_then_delete_task(community_id):
|
||||||
sleep(5)
|
sleep(5)
|
||||||
community.delete_dependencies()
|
community.delete_dependencies()
|
||||||
db.session.delete(community) # todo: when a remote community is deleted it will be able to be re-created by using the 'Add remote' function. Not ideal. Consider soft-delete.
|
db.session.delete(community) # todo: when a remote community is deleted it will be able to be re-created by using the 'Add remote' function. Not ideal. Consider soft-delete.
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/topics', methods=['GET'])
|
@bp.route('/topics', methods=['GET'])
|
||||||
|
|
|
@ -230,11 +230,13 @@ def subscribe(actor):
|
||||||
banned = CommunityBan.query.filter_by(user_id=current_user.id, community_id=community.id).first()
|
banned = CommunityBan.query.filter_by(user_id=current_user.id, community_id=community.id).first()
|
||||||
if banned:
|
if banned:
|
||||||
flash('You cannot join this community')
|
flash('You cannot join this community')
|
||||||
|
else:
|
||||||
member = CommunityMember(user_id=current_user.id, community_id=community.id)
|
member = CommunityMember(user_id=current_user.id, community_id=community.id)
|
||||||
db.session.add(member)
|
db.session.add(member)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash('You joined ' + community.title)
|
flash('You joined ' + community.title)
|
||||||
referrer = request.headers.get('Referer', None)
|
referrer = request.headers.get('Referer', None)
|
||||||
|
cache.delete_memoized(community_membership, current_user, community)
|
||||||
if referrer is not None:
|
if referrer is not None:
|
||||||
return redirect(referrer)
|
return redirect(referrer)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -53,7 +53,7 @@ def search_for_community(address: str):
|
||||||
community_json = community_data.json()
|
community_json = community_data.json()
|
||||||
community_data.close()
|
community_data.close()
|
||||||
if community_json['type'] == 'Group':
|
if community_json['type'] == 'Group':
|
||||||
community = actor_json_to_model(community_json, address, server)
|
community = actor_json_to_model(community_json, name, server)
|
||||||
if current_app.debug:
|
if current_app.debug:
|
||||||
retrieve_mods_and_backfill(community.id)
|
retrieve_mods_and_backfill(community.id)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<div class="card-title">{{ _('Found a community:') }}</div>
|
<div class="card-title">{{ _('Found a community:') }}</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>
|
<p>
|
||||||
<a href="/c/{{ new_community.link() }}"><img src="{{ new_community.icon_image()}}" class="community_icon rounded-circle" /></a>
|
<a href="/c/{{ new_community.link() }}"><img src="{{ new_community.icon_image()}}" class="community_icon rounded-circle" style="width: 30px; vertical-align: middle;" /></a>
|
||||||
<a href="/c/{{ new_community.link() }}">{{ new_community.title }}@{{ new_community.ap_domain }}</a>
|
<a href="/c/{{ new_community.link() }}">{{ new_community.title }}@{{ new_community.ap_domain }}</a>
|
||||||
</p>
|
</p>
|
||||||
<p> {% if subscribed %}
|
<p> {% if subscribed %}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% from 'bootstrap/form.html' import render_form %}
|
{% from 'bootstrap/form.html' import render_field %}
|
||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -12,8 +12,16 @@
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
<h1 class="mt-2">{{ _('Change settings') }}</h1>
|
<h1 class="mt-2">{{ _('Change settings') }}</h1>
|
||||||
<form method='post'>
|
<form method='post' enctype="multipart/form-data">
|
||||||
{{ render_form(form) }}
|
{{ form.csrf_token() }}
|
||||||
|
{{ render_field(form.newsletter) }}
|
||||||
|
{{ render_field(form.ignore_bots) }}
|
||||||
|
{{ render_field(form.nsfw) }}
|
||||||
|
{{ render_field(form.nsfl) }}
|
||||||
|
{{ render_field(form.searchable) }}
|
||||||
|
{{ render_field(form.indexable) }}
|
||||||
|
{{ render_field(form.import_file) }}
|
||||||
|
{{ render_field(form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -33,6 +33,7 @@ class SettingsForm(FlaskForm):
|
||||||
searchable = BooleanField(_l('Show profile in user list'))
|
searchable = BooleanField(_l('Show profile in user list'))
|
||||||
indexable = BooleanField(_l('Allow search engines to index this profile'))
|
indexable = BooleanField(_l('Allow search engines to index this profile'))
|
||||||
manually_approves_followers = BooleanField(_l('Manually approve followers'))
|
manually_approves_followers = BooleanField(_l('Manually approve followers'))
|
||||||
|
import_file = FileField(_('Import community subscriptions and user blocks from Lemmy'))
|
||||||
submit = SubmitField(_l('Save settings'))
|
submit = SubmitField(_l('Save settings'))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from flask import redirect, url_for, flash, request, make_response, session, Markup, current_app, abort
|
from flask import redirect, url_for, flash, request, make_response, session, Markup, current_app, abort, json
|
||||||
from flask_login import login_user, logout_user, current_user, login_required
|
from flask_login import login_user, logout_user, current_user, login_required
|
||||||
from flask_babel import _
|
from flask_babel import _
|
||||||
|
|
||||||
from app import db, cache, celery
|
from app import db, cache, celery
|
||||||
from app.activitypub.signature import post_request
|
from app.activitypub.signature import post_request
|
||||||
from app.activitypub.util import default_context
|
from app.activitypub.util import default_context, find_actor_or_create
|
||||||
from app.community.util import save_icon_file, save_banner_file
|
from app.community.util import save_icon_file, save_banner_file, retrieve_mods_and_backfill
|
||||||
|
from app.constants import SUBSCRIPTION_MEMBER, SUBSCRIPTION_PENDING
|
||||||
from app.models import Post, Community, CommunityMember, User, PostReply, PostVote, Notification, utcnow, File, Site, \
|
from app.models import Post, Community, CommunityMember, User, PostReply, PostVote, Notification, utcnow, File, Site, \
|
||||||
Instance, Report, UserBlock
|
Instance, Report, UserBlock, CommunityBan, CommunityJoinRequest, CommunityBlock
|
||||||
from app.user import bp
|
from app.user import bp
|
||||||
from app.user.forms import ProfileForm, SettingsForm, DeleteAccountForm, ReportUserForm
|
from app.user.forms import ProfileForm, SettingsForm, DeleteAccountForm, ReportUserForm
|
||||||
from app.utils import get_setting, render_template, markdown_to_html, user_access, markdown_to_text, shorten_string, \
|
from app.utils import get_setting, render_template, markdown_to_html, user_access, markdown_to_text, shorten_string, \
|
||||||
is_image_url
|
is_image_url, ensure_directory_exists, gibberish, file_get_contents, community_membership
|
||||||
from sqlalchemy import desc, or_, text
|
from sqlalchemy import desc, or_, text
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/people', methods=['GET', 'POST'])
|
@bp.route('/people', methods=['GET', 'POST'])
|
||||||
|
@ -142,7 +144,24 @@ def change_settings(actor):
|
||||||
current_user.show_nsfl = form.nsfl.data
|
current_user.show_nsfl = form.nsfl.data
|
||||||
current_user.searchable = form.searchable.data
|
current_user.searchable = form.searchable.data
|
||||||
current_user.indexable = form.indexable.data
|
current_user.indexable = form.indexable.data
|
||||||
current_user.ap_manually_approves_followers = form.manually_approves_followers.data
|
import_file = request.files['import_file']
|
||||||
|
if import_file and import_file.filename != '':
|
||||||
|
file_ext = os.path.splitext(import_file.filename)[1]
|
||||||
|
if file_ext.lower() != '.json':
|
||||||
|
abort(400)
|
||||||
|
new_filename = gibberish(15) + '.json'
|
||||||
|
|
||||||
|
directory = f'app/static/media/'
|
||||||
|
|
||||||
|
# save the file
|
||||||
|
final_place = os.path.join(directory, new_filename + file_ext)
|
||||||
|
import_file.save(final_place)
|
||||||
|
|
||||||
|
# import settings in background task
|
||||||
|
import_settings(final_place)
|
||||||
|
|
||||||
|
flash(_('Your subscriptions and blocks are being imported. If you have many it could take a few minutes.'))
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
flash(_('Your changes have been saved.'), 'success')
|
flash(_('Your changes have been saved.'), 'success')
|
||||||
|
@ -154,7 +173,6 @@ def change_settings(actor):
|
||||||
form.nsfl.data = current_user.show_nsfl
|
form.nsfl.data = current_user.show_nsfl
|
||||||
form.searchable.data = current_user.searchable
|
form.searchable.data = current_user.searchable
|
||||||
form.indexable.data = current_user.indexable
|
form.indexable.data = current_user.indexable
|
||||||
form.manually_approves_followers.data = current_user.ap_manually_approves_followers
|
|
||||||
|
|
||||||
return render_template('user/edit_settings.html', title=_('Edit profile'), form=form, user=current_user)
|
return render_template('user/edit_settings.html', title=_('Edit profile'), form=form, user=current_user)
|
||||||
|
|
||||||
|
@ -475,3 +493,75 @@ def notifications_all_read():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash(_('All notifications marked as read.'))
|
flash(_('All notifications marked as read.'))
|
||||||
return redirect(url_for('user.notifications'))
|
return redirect(url_for('user.notifications'))
|
||||||
|
|
||||||
|
|
||||||
|
def import_settings(filename):
|
||||||
|
if current_app.debug:
|
||||||
|
import_settings_task(current_user.id, filename)
|
||||||
|
else:
|
||||||
|
import_settings_task.delay(current_user.id, filename)
|
||||||
|
|
||||||
|
|
||||||
|
@celery.task
|
||||||
|
def import_settings_task(user_id, filename):
|
||||||
|
user = User.query.get(user_id)
|
||||||
|
contents = file_get_contents(filename)
|
||||||
|
contents_json = json.loads(contents)
|
||||||
|
|
||||||
|
# Follow communities
|
||||||
|
for community_ap_id in contents_json['followed_communities'] if 'followed_communities' in contents_json else []:
|
||||||
|
community = find_actor_or_create(community_ap_id)
|
||||||
|
if community:
|
||||||
|
if community.posts.count() == 0:
|
||||||
|
if current_app.debug:
|
||||||
|
retrieve_mods_and_backfill(community.id)
|
||||||
|
else:
|
||||||
|
retrieve_mods_and_backfill.delay(community.id)
|
||||||
|
if community_membership(user, community) != SUBSCRIPTION_MEMBER and community_membership(
|
||||||
|
user, community) != SUBSCRIPTION_PENDING:
|
||||||
|
if not community.is_local():
|
||||||
|
# send ActivityPub message to remote community, asking to follow. Accept message will be sent to our shared inbox
|
||||||
|
join_request = CommunityJoinRequest(user_id=user.id, community_id=community.id)
|
||||||
|
db.session.add(join_request)
|
||||||
|
db.session.commit()
|
||||||
|
follow = {
|
||||||
|
"actor": f"https://{current_app.config['SERVER_NAME']}/u/{user.user_name}",
|
||||||
|
"to": [community.ap_profile_id],
|
||||||
|
"object": community.ap_profile_id,
|
||||||
|
"type": "Follow",
|
||||||
|
"id": f"https://{current_app.config['SERVER_NAME']}/activities/follow/{join_request.id}"
|
||||||
|
}
|
||||||
|
success = post_request(community.ap_inbox_url, follow, user.private_key,
|
||||||
|
user.profile_id() + '#main-key')
|
||||||
|
if not success:
|
||||||
|
sleep(5) # give them a rest
|
||||||
|
else: # for local communities, joining is instant
|
||||||
|
banned = CommunityBan.query.filter_by(user_id=user.id, community_id=community.id).first()
|
||||||
|
if not banned:
|
||||||
|
member = CommunityMember(user_id=user.id, community_id=community.id)
|
||||||
|
db.session.add(member)
|
||||||
|
db.session.commit()
|
||||||
|
cache.delete_memoized(community_membership, current_user, community)
|
||||||
|
|
||||||
|
for community_ap_id in contents_json['blocked_communities'] if 'blocked_communities' in contents_json else []:
|
||||||
|
community = find_actor_or_create(community_ap_id)
|
||||||
|
if community:
|
||||||
|
existing_block = CommunityBlock.query.filter_by(user_id=user.id, community_id=community.id).first()
|
||||||
|
if not existing_block:
|
||||||
|
block = CommunityBlock(user_id=user.id, community_id=community.id)
|
||||||
|
db.session.add(block)
|
||||||
|
|
||||||
|
for user_ap_id in contents_json['blocked_users'] if 'blocked_users' in contents_json else []:
|
||||||
|
blocked_user = find_actor_or_create(user_ap_id)
|
||||||
|
if blocked_user:
|
||||||
|
existing_block = UserBlock.query.filter_by(blocker_id=user.id, blocked_id=blocked_user.id).first()
|
||||||
|
if not existing_block:
|
||||||
|
user_block = UserBlock(blocker_id=user.id, blocked_id=blocked_user.id)
|
||||||
|
db.session.add(user_block)
|
||||||
|
if not blocked_user.is_local():
|
||||||
|
... # todo: federate block
|
||||||
|
|
||||||
|
for instance_domain in contents_json['blocked_instances']:
|
||||||
|
...
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
Loading…
Add table
Reference in a new issue