bluesky graph
This commit is contained in:
parent
e29b921d34
commit
196ac2a616
6 changed files with 235 additions and 173 deletions
|
@ -4,12 +4,14 @@ from functools import cached_property
|
|||
|
||||
from atproto import Client, SessionEvent, client_utils
|
||||
from atproto_client import models
|
||||
from atproto_client.exceptions import AtProtocolError
|
||||
from atproto_identity.did.resolver import DidResolver
|
||||
from atproto_identity.handle.resolver import HandleResolver
|
||||
from django.utils import timezone
|
||||
from loguru import logger
|
||||
|
||||
from catalog.common import jsondata
|
||||
from takahe.utils import Takahe
|
||||
|
||||
from .common import SocialAccount
|
||||
|
||||
|
@ -172,6 +174,52 @@ class BlueskyAccount(SocialAccount):
|
|||
)
|
||||
return True
|
||||
|
||||
def refresh_graph(self, save=True) -> bool:
|
||||
try:
|
||||
r = self._client.get_followers(self.uid)
|
||||
self.followers = [p.did for p in r.followers]
|
||||
r = self._client.get_follows(self.uid)
|
||||
self.following = [p.did for p in r.follows]
|
||||
r = self._client.app.bsky.graph.get_mutes(
|
||||
models.AppBskyGraphGetMutes.Params(cursor=None, limit=None)
|
||||
)
|
||||
self.mutes = [p.did for p in r.mutes]
|
||||
except AtProtocolError as e:
|
||||
logger.warning(f"{self} refresh_graph error: {e}")
|
||||
return False
|
||||
if save:
|
||||
self.save(
|
||||
update_fields=[
|
||||
"followers",
|
||||
"following",
|
||||
"mutes",
|
||||
]
|
||||
)
|
||||
return True
|
||||
|
||||
def sync_graph(self):
|
||||
c = 0
|
||||
|
||||
def get_identity_ids(accts: list):
|
||||
return set(
|
||||
BlueskyAccount.objects.filter(
|
||||
domain=Bluesky._DOMAIN, uid__in=accts
|
||||
).values_list("user__identity", flat=True)
|
||||
)
|
||||
|
||||
me = self.user.identity.pk
|
||||
for target_identity in get_identity_ids(self.following):
|
||||
if not Takahe.get_is_following(me, target_identity):
|
||||
Takahe.follow(me, target_identity, True)
|
||||
c += 1
|
||||
|
||||
for target_identity in get_identity_ids(self.mutes):
|
||||
if not Takahe.get_is_muting(me, target_identity):
|
||||
Takahe.mute(me, target_identity)
|
||||
c += 1
|
||||
|
||||
return c
|
||||
|
||||
def post(
|
||||
self,
|
||||
content,
|
||||
|
|
|
@ -44,13 +44,13 @@ class SocialAccount(TypedModel):
|
|||
last_refresh = models.DateTimeField(default=None, null=True)
|
||||
last_reachable = models.DateTimeField(default=None, null=True)
|
||||
|
||||
sync_profile = jsondata.BooleanField(
|
||||
json_field_name="preference_data", default=True
|
||||
)
|
||||
sync_graph = jsondata.BooleanField(json_field_name="preference_data", default=True)
|
||||
sync_timeline = jsondata.BooleanField(
|
||||
json_field_name="preference_data", default=True
|
||||
)
|
||||
# sync_profile = jsondata.BooleanField(
|
||||
# json_field_name="preference_data", default=True
|
||||
# )
|
||||
# sync_graph = jsondata.BooleanField(json_field_name="preference_data", default=True)
|
||||
# sync_timeline = jsondata.BooleanField(
|
||||
# json_field_name="preference_data", default=True
|
||||
# )
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
|
@ -124,3 +124,6 @@ class SocialAccount(TypedModel):
|
|||
self.refresh_graph()
|
||||
logger.debug(f"{self} refreshed")
|
||||
return True
|
||||
|
||||
def sync_graph(self) -> int:
|
||||
return 0
|
||||
|
|
|
@ -778,6 +778,46 @@ class MastodonAccount(SocialAccount):
|
|||
)
|
||||
return True
|
||||
|
||||
def sync_graph(self):
|
||||
c = 0
|
||||
|
||||
def get_identity_ids(accts: list):
|
||||
return set(
|
||||
MastodonAccount.objects.filter(handle__in=accts).values_list(
|
||||
"user__identity", flat=True
|
||||
)
|
||||
)
|
||||
|
||||
def get_identity_ids_in_domains(domains: list):
|
||||
return set(
|
||||
MastodonAccount.objects.filter(domain__in=domains).values_list(
|
||||
"user__identity", flat=True
|
||||
)
|
||||
)
|
||||
|
||||
me = self.user.identity.pk
|
||||
for target_identity in get_identity_ids(self.following):
|
||||
if not Takahe.get_is_following(me, target_identity):
|
||||
Takahe.follow(me, target_identity, True)
|
||||
c += 1
|
||||
|
||||
for target_identity in get_identity_ids(self.blocks):
|
||||
if not Takahe.get_is_blocking(me, target_identity):
|
||||
Takahe.block(me, target_identity)
|
||||
c += 1
|
||||
|
||||
for target_identity in get_identity_ids_in_domains(self.domain_blocks):
|
||||
if not Takahe.get_is_blocking(me, target_identity):
|
||||
Takahe.block(me, target_identity)
|
||||
c += 1
|
||||
|
||||
for target_identity in get_identity_ids(self.mutes):
|
||||
if not Takahe.get_is_muting(me, target_identity):
|
||||
Takahe.mute(me, target_identity)
|
||||
c += 1
|
||||
|
||||
return c
|
||||
|
||||
def boost(self, post_url: str):
|
||||
boost_toot(self._api_domain, self.access_token, post_url)
|
||||
|
||||
|
|
|
@ -254,40 +254,6 @@ class User(AbstractUser):
|
|||
self.identity.save()
|
||||
self.social_accounts.all().delete()
|
||||
|
||||
def sync_relationship(self):
|
||||
def get_identity_ids(accts: list):
|
||||
return set(
|
||||
MastodonAccount.objects.filter(handle__in=accts).values_list(
|
||||
"user__identity", flat=True
|
||||
)
|
||||
)
|
||||
|
||||
def get_identity_ids_in_domains(domains: list):
|
||||
return set(
|
||||
MastodonAccount.objects.filter(domain__in=domains).values_list(
|
||||
"user__identity", flat=True
|
||||
)
|
||||
)
|
||||
|
||||
me = self.identity.pk
|
||||
if not self.mastodon:
|
||||
return
|
||||
for target_identity in get_identity_ids(self.mastodon.following):
|
||||
if not Takahe.get_is_following(me, target_identity):
|
||||
Takahe.follow(me, target_identity, True)
|
||||
|
||||
for target_identity in get_identity_ids(self.mastodon.blocks):
|
||||
if not Takahe.get_is_blocking(me, target_identity):
|
||||
Takahe.block(me, target_identity)
|
||||
|
||||
for target_identity in get_identity_ids_in_domains(self.mastodon.domain_blocks):
|
||||
if not Takahe.get_is_blocking(me, target_identity):
|
||||
Takahe.block(me, target_identity)
|
||||
|
||||
for target_identity in get_identity_ids(self.mastodon.mutes):
|
||||
if not Takahe.get_is_muting(me, target_identity):
|
||||
Takahe.mute(me, target_identity)
|
||||
|
||||
def sync_identity(self):
|
||||
"""sync display name, bio, and avatar from available sources"""
|
||||
identity = self.identity.takahe_identity
|
||||
|
@ -356,17 +322,18 @@ class User(AbstractUser):
|
|||
if skip_graph:
|
||||
return
|
||||
if not self.preference.mastodon_skip_relationship:
|
||||
self.sync_relationship()
|
||||
return
|
||||
c = 0
|
||||
for account in self.social_accounts.all():
|
||||
c += account.sync_graph()
|
||||
if c:
|
||||
logger.debug(f"{self} graph updated with {c} new relationship.")
|
||||
|
||||
@staticmethod
|
||||
def sync_accounts_task(user_id):
|
||||
user = User.objects.get(pk=user_id)
|
||||
logger.info(f"{user} accounts sync start")
|
||||
if user.sync_accounts():
|
||||
logger.info(f"{user} accounts sync done")
|
||||
else:
|
||||
logger.warning(f"{user} accounts sync failed")
|
||||
user.sync_accounts()
|
||||
logger.info(f"{user} accounts sync end")
|
||||
|
||||
def sync_accounts_later(self):
|
||||
django_rq.get_queue("mastodon").enqueue(User.sync_accounts_task, self.pk)
|
||||
|
|
|
@ -129,6 +129,7 @@
|
|||
{% endif %}
|
||||
</details>
|
||||
</article>
|
||||
{% if enable_threads %}
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans "Threads.net" %}</summary>
|
||||
|
@ -165,6 +166,8 @@
|
|||
{% endif %}
|
||||
</details>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% if enable_bluesky %}
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans "Bluesky (ATProto)" %}</summary>
|
||||
|
@ -186,7 +189,6 @@
|
|||
</label>
|
||||
{% endif %}
|
||||
<input required
|
||||
type="email"
|
||||
name="username"
|
||||
autofocus
|
||||
placeholder="{% trans 'Bluesky Login ID' %}"
|
||||
|
@ -220,6 +222,8 @@
|
|||
</details>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if request.user.social_accounts.all %}
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans 'Sync and import social account' %}</summary>
|
||||
|
@ -243,9 +247,7 @@
|
|||
{% trans 'Sync follow, mute and block' %}
|
||||
</label>
|
||||
</fieldset>
|
||||
<input type="submit"
|
||||
value="{% trans 'Save sync settings' %}"
|
||||
{% if not request.user.mastodon_username %}disabled{% endif %} />
|
||||
<input type="submit" value="{% trans 'Save sync settings' %}" />
|
||||
<small>
|
||||
{% trans "New follow, mute and blocks in the associated identity may be automatically imported; removal has to be done manually." %}
|
||||
</small>
|
||||
|
@ -255,9 +257,7 @@
|
|||
enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<small>{% trans "Click button below to start sync now." %}</small>
|
||||
<input type="submit"
|
||||
value="{% trans 'Sync now' %}"
|
||||
{% if not request.user.mastodon_username %}disabled{% endif %} />
|
||||
<input type="submit" value="{% trans 'Sync now' %}" />
|
||||
<small>
|
||||
{% if request.user.mastodon.last_refresh %}
|
||||
{% trans "Last updated" %} {{ request.user.mastodon.last_refresh }}
|
||||
|
@ -266,6 +266,7 @@
|
|||
</form>
|
||||
</details>
|
||||
</article>
|
||||
{% endif %}
|
||||
<article>
|
||||
<details>
|
||||
<summary>{% trans 'Users you are following' %}</summary>
|
||||
|
|
|
@ -37,6 +37,9 @@ def account_info(request):
|
|||
"users/account.html",
|
||||
{
|
||||
"allow_any_site": settings.MASTODON_ALLOW_ANY_SITE,
|
||||
"enable_email": settings.ENABLE_LOGIN_EMAIL,
|
||||
"enable_threads": settings.ENABLE_LOGIN_THREADS,
|
||||
"enable_bluesky": settings.ENABLE_LOGIN_BLUESKY,
|
||||
"profile_form": profile_form,
|
||||
},
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue