bluesky login

This commit is contained in:
Your Name 2024-07-04 00:17:12 -04:00 committed by Henri Dickson
parent 43e9db72b7
commit bf92528331
20 changed files with 549 additions and 240 deletions

View file

@ -104,7 +104,8 @@ env = environ.FileAwareEnv(
SLACK_API_TOKEN=(str, ""),
THREADS_APP_ID=(str, ""),
THREADS_APP_SECRET=(str, ""),
BLUESKY_LOGIN_ENABLED=(bool, False),
NEODB_ENABLE_LOGIN_BLUESKY=(bool, False),
NEODB_ENABLE_LOGIN_THREADS=(bool, False),
# SSL only, better be True for production security
SSL_ONLY=(bool, False),
NEODB_SENTRY_DSN=(str, ""),
@ -181,7 +182,8 @@ else:
THREADS_APP_ID = env("THREADS_APP_ID")
THREADS_APP_SECRET = env("THREADS_APP_SECRET")
BLUESKY_LOGIN_ENABLED = env("BLUESKY_LOGIN_ENABLED")
ENABLE_LOGIN_BLUESKY = env("NEODB_ENABLE_LOGIN_BLUESKY")
ENABLE_LOGIN_THREADS = env("NEODB_ENABLE_LOGIN_THREADS")
SITE_DOMAIN = env("NEODB_SITE_DOMAIN").lower()
SITE_INFO = {

View file

@ -54,26 +54,7 @@
</a>
</span>
{% endif %}
{% if identity.user.mastodon %}
<span>
<a href="{{ identity.user.mastodon.url }}"
target="_blank"
rel="noopener"
title="@{{ identity.user.mastodon.handle }}">
<i class="fa-brands fa-mastodon"></i>
</a>
</span>
{% endif %}
{% if identity.user.threads %}
<span>
<a href="{{ identity.user.threads.url }}"
target="_blank"
rel="noopener"
title="@{{ identity.user.threads.handle }}">
<i class="fa-brands fa-threads"></i>
</a>
</span>
{% endif %}
{% include "users/_profile_social_icons.html" %}
{% elif request.user.is_authenticated %}
{% include 'users/profile_actions.html' %}
{% endif %}

View file

@ -1,7 +1,7 @@
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.http import JsonResponse
from django.http import HttpRequest, JsonResponse
from django.shortcuts import redirect, render
from django.urls import reverse
@ -11,7 +11,7 @@ from takahe.utils import Takahe
from .api import api
def render_error(request, title, message=""):
def render_error(request: HttpRequest, title, message=""):
return render(
request, "common/error.html", {"msg": title, "secondary_msg": message}
)

View file

@ -43,6 +43,8 @@ x-shared:
NEODB_EMAIL_URL:
NEODB_EMAIL_FROM: no-reply@${NEODB_SITE_DOMAIN}
NEODB_ENABLE_LOCAL_ONLY:
NEODB_ENABLE_LOGIN_BLUESKY:
NEODB_ENABLE_LOGIN_THREADS:
NEODB_EXTRA_APPS:
NEODB_FANOUT_LIMIT_DAYS:
TAKAHE_FANOUT_LIMIT_DAYS:
@ -69,7 +71,6 @@ x-shared:
TAKAHE_VENV: /takahe-venv
THREADS_APP_ID:
THREADS_APP_SECRET:
BLUESKY_LOGIN_ENABLED:
SPOTIFY_API_KEY:
TMDB_API_V3_KEY:
GOOGLE_API_KEY:

View file

@ -9,7 +9,7 @@ import django_rq
# from deepmerge import always_merger
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.core.exceptions import PermissionDenied, RequestAborted
from django.core.signing import b62_decode, b62_encode
from django.db import models
from django.db.models import CharField, Q
@ -309,7 +309,7 @@ class Piece(PolymorphicModel, UserOwnedObjectMixin):
return
try:
r = threads.post(**params)
except Exception:
except RequestAborted:
logger.warning(f"{self} post to {threads} failed")
messages.error(threads.user, _("A recent post was not posted to Threads."))
return False
@ -338,9 +338,8 @@ class Piece(PolymorphicModel, UserOwnedObjectMixin):
mastodon = self.owner.user.mastodon
if not mastodon:
return False
r = mastodon.post(**params)
try:
pass
r = mastodon.post(**params)
except PermissionDenied:
messages.error(
mastodon.user,
@ -348,7 +347,7 @@ class Piece(PolymorphicModel, UserOwnedObjectMixin):
meta={"url": mastodon.get_reauthorize_url()},
)
return False
except Exception:
except RequestAborted:
logger.warning(f"{self} post to {mastodon} failed")
messages.error(
mastodon.user, _("A recent post was not posted to Mastodon.")

View file

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-03 16:40-0400\n"
"POT-Creation-Date: 2024-07-04 00:13-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,15 +15,15 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: boofilsic/settings.py:406
#: boofilsic/settings.py:408
msgid "English"
msgstr "英语"
#: boofilsic/settings.py:407
#: boofilsic/settings.py:409
msgid "Simplified Chinese"
msgstr "简体中文"
#: boofilsic/settings.py:408
#: boofilsic/settings.py:410
msgid "Traditional Chinese"
msgstr "繁体中文"
@ -1108,8 +1108,7 @@ msgstr "热门标签"
#: catalog/templates/discover.html:177 catalog/templates/item_base.html:236
#: catalog/templates/item_mark_list.html:54
#: catalog/templates/item_review_list.html:50
#: common/templates/_sidebar.html:109
#: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:90
#: common/templates/_sidebar_anonymous.html:43
#: common/templates/_sidebar_anonymous.html:58
#: journal/templates/collection_items.html:8 journal/templates/posts.html:49
@ -1472,7 +1471,7 @@ msgstr "开发者"
msgid "Source Code"
msgstr "源代码"
#: common/templates/_footer.html:16 users/templates/users/login.html:173
#: common/templates/_footer.html:16 users/templates/users/login.html:191
#, python-format
msgid "You are visiting an alternative domain for %(site_name)s, please always use <a href=\"%(site_url)s%(request.get_full_path)s\">original version</a> if possible."
msgstr "这是%(site_name)s的临时镜像请尽可能使用<a href=\"%(site_url)s%(request.get_full_path)s\">原始站点</a>。"
@ -1545,50 +1544,50 @@ msgstr "打开"
msgid "approving followers manually"
msgstr "已开启关注审核"
#: common/templates/_sidebar.html:94
#: common/templates/_sidebar.html:75
msgid "Current Targets"
msgstr "当前目标"
#: common/templates/_sidebar.html:107
#: common/templates/_sidebar.html:88
msgid "Set a collection as target, its progress will show up here."
msgstr "将自己或他人的收藏单设为目标,这里就会显示进度"
#: common/templates/_sidebar.html:120
#: common/templates/_sidebar.html:101
msgid "Recent podcast episodes"
msgstr "近期播客节目"
#: common/templates/_sidebar.html:155
#: common/templates/_sidebar.html:136
msgid "Currently reading"
msgstr "正在阅读"
#: common/templates/_sidebar.html:178
#: common/templates/_sidebar.html:159
msgid "Currently watching"
msgstr "正在追看"
#: common/templates/_sidebar.html:201 journal/templates/user_tag_list.html:12
#: common/templates/_sidebar.html:182 journal/templates/user_tag_list.html:12
#: journal/templates/user_tagmember_list.html:4
msgid "Tags"
msgstr "标签"
#: common/templates/_sidebar.html:207 journal/templates/user_tag_list.html:26
#: common/templates/_sidebar.html:188 journal/templates/user_tag_list.html:26
#: journal/templates/user_tagmember_list.html:10
msgid "featured tag"
msgstr "置顶标签"
#: common/templates/_sidebar.html:210 journal/templates/user_tag_list.html:29
#: common/templates/_sidebar.html:191 journal/templates/user_tag_list.html:29
#: journal/templates/user_tagmember_list.html:15
msgid "personal tag"
msgstr "个人标签"
#: common/templates/_sidebar.html:217 journal/templates/user_tag_list.html:37
#: common/templates/_sidebar.html:198 journal/templates/user_tag_list.html:37
msgid "no tags so far."
msgstr "暂无标签。"
#: common/templates/_sidebar.html:221
#: common/templates/_sidebar.html:202
msgid "show all"
msgstr "显示全部"
#: common/templates/_sidebar.html:231
#: common/templates/_sidebar.html:212
msgid "Recent Posts"
msgstr "近期帖文"
@ -1746,11 +1745,11 @@ msgstr "自己和提到的人"
msgid "A recent post was not posted to Threads."
msgstr "帖文未能发布到Threads。"
#: journal/models/common.py:347
#: journal/models/common.py:346
msgid "A recent post was not posted to Mastodon, please re-authorize."
msgstr "帖文未能发布到联邦实例,请重新验证登录。"
#: journal/models/common.py:354
#: journal/models/common.py:353
msgid "A recent post was not posted to Mastodon."
msgstr "帖文未能发布到联邦实例。"
@ -2781,7 +2780,7 @@ msgstr "Mastodon"
msgid "Threads"
msgstr "Threads"
#: mastodon/models/common.py:14 users/templates/users/login.html:83
#: mastodon/models/common.py:14
msgid "Bluesky"
msgstr "Bluesky"
@ -2879,47 +2878,63 @@ msgstr "转播"
msgid "New Post"
msgstr "新帖文"
#: mastodon/views/bluesky.py:22 mastodon/views/bluesky.py:28
#: mastodon/views/common.py:29 mastodon/views/mastodon.py:37
#: mastodon/views/mastodon.py:44 mastodon/views/mastodon.py:54
#: mastodon/views/mastodon.py:61 mastodon/views/threads.py:40
#: mastodon/views/threads.py:45 users/views/account.py:104
#: mastodon/views/mastodon.py:61 mastodon/views/threads.py:41
#: mastodon/views/threads.py:47 users/views/account.py:104
msgid "Authentication failed"
msgstr "认证失败"
#: mastodon/views/common.py:29 mastodon/views/common.py:79
#: mastodon/views/bluesky.py:23
msgid "Username and app password is required."
msgstr "请输入用户名和密码。"
#: mastodon/views/bluesky.py:28
msgid "Invalid account data from Bluesky."
msgstr "Bluesky返回了无效的账号数据。"
#: mastodon/views/common.py:29 mastodon/views/common.py:94
msgid "Invalid user."
msgstr "无效用户。"
#: mastodon/views/common.py:44
#: mastodon/views/common.py:45
msgid "Registration failed"
msgstr "注册失败"
#: mastodon/views/common.py:44
#: mastodon/views/common.py:45
msgid "User already logged in."
msgstr "用户已经登录了。"
#: mastodon/views/common.py:52
msgid "Unable to update login information: identical identity."
msgstr "无法更新登录信息:该身份与当前账号相同。"
#: mastodon/views/common.py:57
#, python-brace-format
msgid "Continue login as {handle}."
msgstr "继续以 {handle} 登录。"
#: mastodon/views/common.py:56
msgid "Unable to update login information: identity in use."
msgstr "无法更新登录信息:该身份已被其它账号使用。"
#: mastodon/views/common.py:63
msgid "Unable to update login information"
msgstr "无法更新登录信息"
#: mastodon/views/common.py:70
msgid "Login information updated."
msgstr "登录信息已更新"
#: mastodon/views/common.py:64
#, python-brace-format
msgid "Identity {handle} in use by a different user."
msgstr "身份 {handle} 已被其它账号使用。"
#: mastodon/views/common.py:77 mastodon/views/common.py:79
#: mastodon/views/common.py:83
#: mastodon/views/common.py:80
#, python-brace-format
msgid "Login information updated as {handle}."
msgstr "登录信息已更新为{handle}。"
#: mastodon/views/common.py:90 mastodon/views/common.py:94
#: mastodon/views/common.py:100
msgid "Disconnect identity failed"
msgstr "未能取消身份关联"
#: mastodon/views/common.py:77
#: mastodon/views/common.py:90
msgid "Identity not found."
msgstr "身份未找到"
#: mastodon/views/common.py:84
#: mastodon/views/common.py:101
msgid "You cannot disconnect last login identity."
msgstr "无法取消唯一的登录用身份。"
@ -2967,7 +2982,7 @@ msgstr "联邦实例返回了无效的认证令牌。"
msgid "Invalid account data from Fediverse instance."
msgstr "联邦实例返回了无效的账号数据。"
#: mastodon/views/threads.py:45
#: mastodon/views/threads.py:47
msgid "Invalid account data from Threads."
msgstr "Threads返回了无效的账号数据。"
@ -3305,7 +3320,8 @@ msgid "Verified Identity"
msgstr "已验证的身份"
#: users/templates/users/account.html:86 users/templates/users/account.html:147
#: users/templates/users/account.html:209
#: users/templates/users/account.html:183
#: users/templates/users/account.html:263
msgid "Last updated"
msgstr "最近更新"
@ -3335,6 +3351,7 @@ msgstr "关联联邦宇宙身份后可发现更多用户,并使用本站完整
#: users/templates/users/account.html:123
#: users/templates/users/account.html:159
#: users/templates/users/account.html:213
msgid "Once disconnected, you will no longer be able login with this identity. Are you sure to continue?"
msgstr "取消关联后将无法使用这个身份登录本站,确认继续吗?"
@ -3362,75 +3379,107 @@ msgstr "关联一个threads.net账号"
msgid "Disconnect with Threads"
msgstr "取消关联"
#: users/templates/users/account.html:171
#: users/templates/users/account.html:170 users/templates/users/login.html:83
msgid "Bluesky (ATProto)"
msgstr "Bluesky (ATProto)"
#: users/templates/users/account.html:176
msgid "Verified ATProto identity"
msgstr "已验证的ATProto身份"
#: users/templates/users/account.html:192 users/templates/users/login.html:151
msgid "Bluesky Login ID"
msgstr "Bluesky 登录名"
#: users/templates/users/account.html:200 users/templates/users/login.html:159
msgid "Bluesky app password"
msgstr "Bluesky 应用密码"
#: users/templates/users/account.html:206
msgid "Link with a different ATProto identity"
msgstr "关联另一个ATProto身份"
#: users/templates/users/account.html:206
msgid "Link with an ATProto identity"
msgstr "关联ATProto身份"
#: users/templates/users/account.html:207 users/templates/users/login.html:164
msgid "App password can be created on <a href=\"https://bsky.app/settings/app-passwords\" target=\"_blank\">bsky.app</a>."
msgstr "应用密码可在 <a href=\"https://bsky.app/settings/app-passwords\" target=\"_blank\">bsky.app</a> 创建管理。"
#: users/templates/users/account.html:216
msgid "Disconnect with ATProto identity"
msgstr "取消关联"
#: users/templates/users/account.html:225
msgid "Sync and import social account"
msgstr "同步第三方社交网络上的个人信息和社交数据"
#: users/templates/users/account.html:181
#: users/templates/users/account.html:235
msgid "Sync display name, bio and avatar"
msgstr "自动同步用户昵称等基本信息"
#: users/templates/users/account.html:189
#: users/templates/users/account.html:243
msgid "Sync follow, mute and block"
msgstr "自动导入新增的关注、屏蔽和隐藏列表"
#: users/templates/users/account.html:193
#: users/templates/users/account.html:247
msgid "Save sync settings"
msgstr "保存同步设置"
#: users/templates/users/account.html:196
#: users/templates/users/account.html:250
msgid "New follow, mute and blocks in the associated identity may be automatically imported; removal has to be done manually."
msgstr "本站会按照以上设置每天自动导入你在联邦宇宙实例等社交网络中新增的关注、屏蔽和隐藏列表;如果你在联邦宇宙实例中关注的用户加入了本站,你会自动关注她;如果你在联邦宇宙实例中取消了关注、屏蔽或隐藏,本站不会自动取消,但你可以手动移除。"
#: users/templates/users/account.html:203
#: users/templates/users/account.html:257
msgid "Click button below to start sync now."
msgstr "如果希望立即开始同步,可以点击下方按钮。"
#: users/templates/users/account.html:205
#: users/templates/users/account.html:259
msgid "Sync now"
msgstr "立即同步"
#: users/templates/users/account.html:217
#: users/templates/users/account.html:271
msgid "Users you are following"
msgstr "正在关注的用户"
#: users/templates/users/account.html:223
#: users/templates/users/account.html:277
msgid "Users who follow you"
msgstr "关注了你的用户"
#: users/templates/users/account.html:229
#: users/templates/users/account.html:283
msgid "Users who request to follow you"
msgstr "请求关注你的用户"
#: users/templates/users/account.html:235
#: users/templates/users/account.html:289
msgid "Users you are muting"
msgstr "已隐藏的用户"
#: users/templates/users/account.html:241
#: users/templates/users/account.html:295
msgid "Users you are blocking"
msgstr "已屏蔽的用户"
#: users/templates/users/account.html:248
#: users/templates/users/account.html:302
msgid "Delete Account"
msgstr "删除账号"
#: users/templates/users/account.html:251
#: users/templates/users/account.html:305
msgid "Once deleted, account data cannot be recovered. Sure to proceed?"
msgstr "账号数据一旦删除后将无法恢复,确定继续吗?"
#: users/templates/users/account.html:254
#: users/templates/users/account.html:308
msgid "Enter full <code>username@instance.social</code> or <code>email@domain.com</code> to confirm deletion."
msgstr "输入完整的登录用 <code>用户名@实例名</code> 或 <code>电子邮件地址</code> 以确认删除"
#: users/templates/users/account.html:264
#: users/templates/users/account.html:318
msgid "Once deleted, account data cannot be recovered."
msgstr "账号数据一旦删除后将无法恢复"
#: users/templates/users/account.html:266
#: users/templates/users/account.html:320
msgid "Importing in progress, can't delete now."
msgstr "暂时无法删除,因为有导入任务正在进行"
#: users/templates/users/account.html:269
#: users/templates/users/account.html:323
msgid "Permanently Delete"
msgstr "永久删除"
@ -3586,7 +3635,7 @@ msgstr "实例域名(不含@和@之前的部分)如mastodon.social"
msgid "Please enter domain of your instance; e.g. if your id is <i>@neodb@mastodon.social</i>, only enter <i>mastodon.social</i>."
msgstr "请输入你的实例域名(不含@和@之前的部分);如果你的联邦账号是<i>@neodb@mastodon.social</i>,只需要在此输入<i>mastodon.social</i>。"
#: users/templates/users/login.html:125 users/templates/users/login.html:156
#: users/templates/users/login.html:125 users/templates/users/login.html:174
msgid "Authorize via Fediverse instance"
msgstr "去联邦实例授权注册或登录"
@ -3602,35 +3651,35 @@ msgstr "去Threads授权注册或登录"
msgid "If you have already account here registered via a different way, you may login through there and link with your Threads account in account settings."
msgstr "如果你已通过其它方式注册过本站帐号请用该方式登录后再关联Threads。"
#: users/templates/users/login.html:147
msgid "Authorize via Bluesky"
msgstr "去Bluesky授权注册或登录"
#: users/templates/users/login.html:165
msgid "Authorize via bsky.app"
msgstr "使用Bluesky账号信息注册或登录"
#: users/templates/users/login.html:148
#: users/templates/users/login.html:166
msgid "If you have already account here registered via a different way, you may login through there and link with your Bluesky account in account settings."
msgstr "如果你已通过其它方式注册过本站帐号请用该方式登录后再关联Bluesky。"
#: users/templates/users/login.html:163
#: users/templates/users/login.html:181
msgid "Valid invitation code, please login or register."
msgstr "邀请链接有效,可注册新用户"
#: users/templates/users/login.html:165
#: users/templates/users/login.html:183
msgid "Please use invitation link to register a new account; existing user may login."
msgstr "本站目前为邀请注册,已有账户可直接登入,新用户请使用有效邀请链接注册"
#: users/templates/users/login.html:167
#: users/templates/users/login.html:185
msgid "Invitation code invalid or expired."
msgstr "邀请链接无效,已有账户可直接登入,新用户请使用有效邀请链接注册"
#: users/templates/users/login.html:175
#: users/templates/users/login.html:193
msgid "Loading timed out, please check your network (VPN) settings."
msgstr "部分模块加载超时,请检查网络(翻墙)设置。"
#: users/templates/users/login.html:181
#: users/templates/users/login.html:199
msgid "Continue using this site implies consent to our <a href=\"/pages/rules/\">rules</a> and <a href=\"/pages/terms/\">terms</a>, including using cookies to provide necessary functionality."
msgstr "继续访问或注册视为同意<a href=\"/pages/rules/\">站规</a>与<a href=\"/pages/terms/\">协议</a>及使用cookie提供必要功能"
#: users/templates/users/login.html:187
#: users/templates/users/login.html:205
msgid "Domain of your instance (excl. @)"
msgstr "实例域名(不含@和@之前的部分)"
@ -3786,59 +3835,59 @@ msgstr "用户资料"
msgid "original home"
msgstr "原始主页"
#: users/templates/users/profile_actions.html:82
#: users/templates/users/profile_actions.html:63
msgid "accept follow request"
msgstr "接受关注请求"
#: users/templates/users/profile_actions.html:84
#: users/templates/users/profile_actions.html:65
msgid "sure to accept follow request?"
msgstr "确定接受关注请求吗?"
#: users/templates/users/profile_actions.html:92
#: users/templates/users/profile_actions.html:73
msgid "reject follow request"
msgstr "拒绝关注请求"
#: users/templates/users/profile_actions.html:94
#: users/templates/users/profile_actions.html:75
msgid "sure to reject follow request?"
msgstr "确定拒绝关注请求吗?"
#: users/templates/users/profile_actions.html:104
#: users/templates/users/profile_actions.html:85
msgid "click to unfollow"
msgstr "点击可取消关注"
#: users/templates/users/profile_actions.html:106
#: users/templates/users/profile_actions.html:87
msgid "sure to unfollow?"
msgstr "确定取消关注该用户吗?"
#: users/templates/users/profile_actions.html:115
#: users/templates/users/profile_actions.html:96
msgid "click to cancel follow request"
msgstr "点击可取消关注请求"
#: users/templates/users/profile_actions.html:117
#: users/templates/users/profile_actions.html:98
msgid "sure to cancel follow request?"
msgstr "确定取消关注请求吗?"
#: users/templates/users/profile_actions.html:126
#: users/templates/users/profile_actions.html:107
msgid "click to follow"
msgstr "点击可关注"
#: users/templates/users/profile_actions.html:127
#: users/templates/users/profile_actions.html:108
msgid "sure to follow?"
msgstr "确定关注该用户吗?"
#: users/templates/users/profile_actions.html:137
#: users/templates/users/profile_actions.html:118
msgid "click to mute"
msgstr "点击可静音"
#: users/templates/users/profile_actions.html:146
#: users/templates/users/profile_actions.html:127
msgid "click to unmute"
msgstr "点击可取消静音"
#: users/templates/users/profile_actions.html:156
#: users/templates/users/profile_actions.html:137
msgid "click to block"
msgstr "点击可屏蔽"
#: users/templates/users/profile_actions.html:157
#: users/templates/users/profile_actions.html:138
msgid "sure to block?"
msgstr "确定屏蔽该用户吗?"

View file

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-03 16:40-0400\n"
"POT-Creation-Date: 2024-07-04 00:13-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,15 +15,15 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: boofilsic/settings.py:406
#: boofilsic/settings.py:408
msgid "English"
msgstr "英語"
#: boofilsic/settings.py:407
#: boofilsic/settings.py:409
msgid "Simplified Chinese"
msgstr "簡體中文"
#: boofilsic/settings.py:408
#: boofilsic/settings.py:410
msgid "Traditional Chinese"
msgstr "繁體中文"
@ -1108,8 +1108,7 @@ msgstr "熱門標籤"
#: catalog/templates/discover.html:177 catalog/templates/item_base.html:236
#: catalog/templates/item_mark_list.html:54
#: catalog/templates/item_review_list.html:50
#: common/templates/_sidebar.html:109
#: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:90
#: common/templates/_sidebar_anonymous.html:43
#: common/templates/_sidebar_anonymous.html:58
#: journal/templates/collection_items.html:8 journal/templates/posts.html:49
@ -1472,7 +1471,7 @@ msgstr "開發者"
msgid "Source Code"
msgstr "源代碼"
#: common/templates/_footer.html:16 users/templates/users/login.html:173
#: common/templates/_footer.html:16 users/templates/users/login.html:191
#, python-format
msgid "You are visiting an alternative domain for %(site_name)s, please always use <a href=\"%(site_url)s%(request.get_full_path)s\">original version</a> if possible."
msgstr "這是%(site_name)s的臨時鏡像請儘可能使用<a href=\"%(site_url)s%(request.get_full_path)s\">原始站點</a>。"
@ -1545,50 +1544,50 @@ msgstr "打開"
msgid "approving followers manually"
msgstr "已開啓關注審覈"
#: common/templates/_sidebar.html:94
#: common/templates/_sidebar.html:75
msgid "Current Targets"
msgstr "當前目標"
#: common/templates/_sidebar.html:107
#: common/templates/_sidebar.html:88
msgid "Set a collection as target, its progress will show up here."
msgstr "將自己或他人的收藏單設爲目標,這裏就會顯示進度"
#: common/templates/_sidebar.html:120
#: common/templates/_sidebar.html:101
msgid "Recent podcast episodes"
msgstr "近期播客節目"
#: common/templates/_sidebar.html:155
#: common/templates/_sidebar.html:136
msgid "Currently reading"
msgstr "正在閱讀"
#: common/templates/_sidebar.html:178
#: common/templates/_sidebar.html:159
msgid "Currently watching"
msgstr "正在追看"
#: common/templates/_sidebar.html:201 journal/templates/user_tag_list.html:12
#: common/templates/_sidebar.html:182 journal/templates/user_tag_list.html:12
#: journal/templates/user_tagmember_list.html:4
msgid "Tags"
msgstr "標籤"
#: common/templates/_sidebar.html:207 journal/templates/user_tag_list.html:26
#: common/templates/_sidebar.html:188 journal/templates/user_tag_list.html:26
#: journal/templates/user_tagmember_list.html:10
msgid "featured tag"
msgstr "置頂標籤"
#: common/templates/_sidebar.html:210 journal/templates/user_tag_list.html:29
#: common/templates/_sidebar.html:191 journal/templates/user_tag_list.html:29
#: journal/templates/user_tagmember_list.html:15
msgid "personal tag"
msgstr "個人標籤"
#: common/templates/_sidebar.html:217 journal/templates/user_tag_list.html:37
#: common/templates/_sidebar.html:198 journal/templates/user_tag_list.html:37
msgid "no tags so far."
msgstr "暫無標籤。"
#: common/templates/_sidebar.html:221
#: common/templates/_sidebar.html:202
msgid "show all"
msgstr "顯示全部"
#: common/templates/_sidebar.html:231
#: common/templates/_sidebar.html:212
msgid "Recent Posts"
msgstr "近期帖文"
@ -1746,11 +1745,11 @@ msgstr "自己和提到的人"
msgid "A recent post was not posted to Threads."
msgstr "帖文未能發佈到Threads。"
#: journal/models/common.py:347
#: journal/models/common.py:346
msgid "A recent post was not posted to Mastodon, please re-authorize."
msgstr "帖文未能發佈到聯邦實例,請重新驗證登錄。"
#: journal/models/common.py:354
#: journal/models/common.py:353
msgid "A recent post was not posted to Mastodon."
msgstr "帖文未能發佈到聯邦實例。"
@ -2781,7 +2780,7 @@ msgstr "Mastodon"
msgid "Threads"
msgstr "Threads"
#: mastodon/models/common.py:14 users/templates/users/login.html:83
#: mastodon/models/common.py:14
msgid "Bluesky"
msgstr "Bluesky"
@ -2879,47 +2878,63 @@ msgstr "轉播"
msgid "New Post"
msgstr "新帖文"
#: mastodon/views/bluesky.py:22 mastodon/views/bluesky.py:28
#: mastodon/views/common.py:29 mastodon/views/mastodon.py:37
#: mastodon/views/mastodon.py:44 mastodon/views/mastodon.py:54
#: mastodon/views/mastodon.py:61 mastodon/views/threads.py:40
#: mastodon/views/threads.py:45 users/views/account.py:104
#: mastodon/views/mastodon.py:61 mastodon/views/threads.py:41
#: mastodon/views/threads.py:47 users/views/account.py:104
msgid "Authentication failed"
msgstr "認證失敗"
#: mastodon/views/common.py:29 mastodon/views/common.py:79
#: mastodon/views/bluesky.py:23
msgid "Username and app password is required."
msgstr "請輸入用戶名和密碼。"
#: mastodon/views/bluesky.py:28
msgid "Invalid account data from Bluesky."
msgstr "Bluesky返回了無效的賬號數據。"
#: mastodon/views/common.py:29 mastodon/views/common.py:94
msgid "Invalid user."
msgstr "無效用戶。"
#: mastodon/views/common.py:44
#: mastodon/views/common.py:45
msgid "Registration failed"
msgstr "註冊失敗"
#: mastodon/views/common.py:44
#: mastodon/views/common.py:45
msgid "User already logged in."
msgstr "用戶已經登錄了。"
#: mastodon/views/common.py:52
msgid "Unable to update login information: identical identity."
msgstr "無法更新登錄信息:該身份與當前賬號相同。"
#: mastodon/views/common.py:57
#, python-brace-format
msgid "Continue login as {handle}."
msgstr "繼續以 {handle} 登錄。"
#: mastodon/views/common.py:56
msgid "Unable to update login information: identity in use."
msgstr "無法更新登錄信息:該身份已被其它賬號使用。"
#: mastodon/views/common.py:63
msgid "Unable to update login information"
msgstr "無法更新登錄信息"
#: mastodon/views/common.py:70
msgid "Login information updated."
msgstr "登錄信息已更新"
#: mastodon/views/common.py:64
#, python-brace-format
msgid "Identity {handle} in use by a different user."
msgstr "身份 {handle} 已被其它賬號使用。"
#: mastodon/views/common.py:77 mastodon/views/common.py:79
#: mastodon/views/common.py:83
#: mastodon/views/common.py:80
#, python-brace-format
msgid "Login information updated as {handle}."
msgstr "登錄信息已更新爲{handle}。"
#: mastodon/views/common.py:90 mastodon/views/common.py:94
#: mastodon/views/common.py:100
msgid "Disconnect identity failed"
msgstr "未能取消身份關聯"
#: mastodon/views/common.py:77
#: mastodon/views/common.py:90
msgid "Identity not found."
msgstr "身份未找到"
#: mastodon/views/common.py:84
#: mastodon/views/common.py:101
msgid "You cannot disconnect last login identity."
msgstr "無法取消唯一的登錄用身份。"
@ -2967,7 +2982,7 @@ msgstr "聯邦實例返回了無效的認證令牌。"
msgid "Invalid account data from Fediverse instance."
msgstr "聯邦實例返回了無效的賬號數據。"
#: mastodon/views/threads.py:45
#: mastodon/views/threads.py:47
msgid "Invalid account data from Threads."
msgstr "Threads返回了無效的賬號數據。"
@ -3305,7 +3320,8 @@ msgid "Verified Identity"
msgstr "已驗證的身份"
#: users/templates/users/account.html:86 users/templates/users/account.html:147
#: users/templates/users/account.html:209
#: users/templates/users/account.html:183
#: users/templates/users/account.html:263
msgid "Last updated"
msgstr "最近更新"
@ -3335,6 +3351,7 @@ msgstr "關聯聯邦宇宙身份後可發現更多用戶,並使用本站完整
#: users/templates/users/account.html:123
#: users/templates/users/account.html:159
#: users/templates/users/account.html:213
msgid "Once disconnected, you will no longer be able login with this identity. Are you sure to continue?"
msgstr "取消關聯後將無法使用這個身份登錄本站,確認繼續嗎?"
@ -3362,75 +3379,107 @@ msgstr "關聯一個threads.net賬號"
msgid "Disconnect with Threads"
msgstr "取消關聯"
#: users/templates/users/account.html:171
#: users/templates/users/account.html:170 users/templates/users/login.html:83
msgid "Bluesky (ATProto)"
msgstr "Bluesky (ATProto)"
#: users/templates/users/account.html:176
msgid "Verified ATProto identity"
msgstr "已驗證的ATProto身份"
#: users/templates/users/account.html:192 users/templates/users/login.html:151
msgid "Bluesky Login ID"
msgstr "Bluesky 登錄名"
#: users/templates/users/account.html:200 users/templates/users/login.html:159
msgid "Bluesky app password"
msgstr "Bluesky 應用密碼"
#: users/templates/users/account.html:206
msgid "Link with a different ATProto identity"
msgstr "關聯另一個ATProto身份"
#: users/templates/users/account.html:206
msgid "Link with an ATProto identity"
msgstr "關聯ATProto身份"
#: users/templates/users/account.html:207 users/templates/users/login.html:164
msgid "App password can be created on <a href=\"https://bsky.app/settings/app-passwords\" target=\"_blank\">bsky.app</a>."
msgstr "應用密碼可在 <a href=\"https://bsky.app/settings/app-passwords\" target=\"_blank\">bsky.app</a> 創建管理。"
#: users/templates/users/account.html:216
msgid "Disconnect with ATProto identity"
msgstr "取消關聯"
#: users/templates/users/account.html:225
msgid "Sync and import social account"
msgstr "同步第三方社交網絡上的個人信息和社交數據"
#: users/templates/users/account.html:181
#: users/templates/users/account.html:235
msgid "Sync display name, bio and avatar"
msgstr "自動同步用戶暱稱等基本信息"
#: users/templates/users/account.html:189
#: users/templates/users/account.html:243
msgid "Sync follow, mute and block"
msgstr "自動導入新增的關注、屏蔽和隱藏列表"
#: users/templates/users/account.html:193
#: users/templates/users/account.html:247
msgid "Save sync settings"
msgstr "保存同步設置"
#: users/templates/users/account.html:196
#: users/templates/users/account.html:250
msgid "New follow, mute and blocks in the associated identity may be automatically imported; removal has to be done manually."
msgstr "本站會按照以上設置每天自動導入你在聯邦宇宙實例等社交網絡中新增的關注、屏蔽和隱藏列表;如果你在聯邦宇宙實例中關注的用戶加入了本站,你會自動關注她;如果你在聯邦宇宙實例中取消了關注、屏蔽或隱藏,本站不會自動取消,但你可以手動移除。"
#: users/templates/users/account.html:203
#: users/templates/users/account.html:257
msgid "Click button below to start sync now."
msgstr "如果希望立即開始同步,可以點擊下方按鈕。"
#: users/templates/users/account.html:205
#: users/templates/users/account.html:259
msgid "Sync now"
msgstr "立即同步"
#: users/templates/users/account.html:217
#: users/templates/users/account.html:271
msgid "Users you are following"
msgstr "正在關注的用戶"
#: users/templates/users/account.html:223
#: users/templates/users/account.html:277
msgid "Users who follow you"
msgstr "關注了你的用戶"
#: users/templates/users/account.html:229
#: users/templates/users/account.html:283
msgid "Users who request to follow you"
msgstr "請求關注你的用戶"
#: users/templates/users/account.html:235
#: users/templates/users/account.html:289
msgid "Users you are muting"
msgstr "已隱藏的用戶"
#: users/templates/users/account.html:241
#: users/templates/users/account.html:295
msgid "Users you are blocking"
msgstr "已屏蔽的用戶"
#: users/templates/users/account.html:248
#: users/templates/users/account.html:302
msgid "Delete Account"
msgstr "刪除賬號"
#: users/templates/users/account.html:251
#: users/templates/users/account.html:305
msgid "Once deleted, account data cannot be recovered. Sure to proceed?"
msgstr "賬號數據一旦刪除後將無法恢復,確定繼續嗎?"
#: users/templates/users/account.html:254
#: users/templates/users/account.html:308
msgid "Enter full <code>username@instance.social</code> or <code>email@domain.com</code> to confirm deletion."
msgstr "輸入完整的登錄用 <code>用戶名@實例名</code> 或 <code>電子郵件地址</code> 以確認刪除"
#: users/templates/users/account.html:264
#: users/templates/users/account.html:318
msgid "Once deleted, account data cannot be recovered."
msgstr "賬號數據一旦刪除後將無法恢復"
#: users/templates/users/account.html:266
#: users/templates/users/account.html:320
msgid "Importing in progress, can't delete now."
msgstr "暫時無法刪除,因爲有導入任務正在進行"
#: users/templates/users/account.html:269
#: users/templates/users/account.html:323
msgid "Permanently Delete"
msgstr "永久刪除"
@ -3586,7 +3635,7 @@ msgstr "實例域名(不含@和@之前的部分)如mastodon.social"
msgid "Please enter domain of your instance; e.g. if your id is <i>@neodb@mastodon.social</i>, only enter <i>mastodon.social</i>."
msgstr "請輸入你的實例域名(不含@和@之前的部分);如果你的聯邦賬號是<i>@neodb@mastodon.social</i>,只需要在此輸入<i>mastodon.social</i>。"
#: users/templates/users/login.html:125 users/templates/users/login.html:156
#: users/templates/users/login.html:125 users/templates/users/login.html:174
msgid "Authorize via Fediverse instance"
msgstr "去聯邦實例授權註冊或登錄"
@ -3602,35 +3651,35 @@ msgstr "去Threads授權註冊或登錄"
msgid "If you have already account here registered via a different way, you may login through there and link with your Threads account in account settings."
msgstr "如果你已通過其它方式註冊過本站帳號請用該方式登錄後再關聯Threads。"
#: users/templates/users/login.html:147
msgid "Authorize via Bluesky"
msgstr "去Bluesky授權註冊或登錄"
#: users/templates/users/login.html:165
msgid "Authorize via bsky.app"
msgstr "使用Bluesky賬號信息註冊或登錄"
#: users/templates/users/login.html:148
#: users/templates/users/login.html:166
msgid "If you have already account here registered via a different way, you may login through there and link with your Bluesky account in account settings."
msgstr "如果你已通過其它方式註冊過本站帳號請用該方式登錄後再關聯Bluesky。"
#: users/templates/users/login.html:163
#: users/templates/users/login.html:181
msgid "Valid invitation code, please login or register."
msgstr "邀請鏈接有效,可註冊新用戶"
#: users/templates/users/login.html:165
#: users/templates/users/login.html:183
msgid "Please use invitation link to register a new account; existing user may login."
msgstr "本站目前爲邀請註冊,已有賬戶可直接登入,新用戶請使用有效邀請鏈接註冊"
#: users/templates/users/login.html:167
#: users/templates/users/login.html:185
msgid "Invitation code invalid or expired."
msgstr "邀請鏈接無效,已有賬戶可直接登入,新用戶請使用有效邀請鏈接註冊"
#: users/templates/users/login.html:175
#: users/templates/users/login.html:193
msgid "Loading timed out, please check your network (VPN) settings."
msgstr "部分模塊加載超時,請檢查網絡(翻牆)設置。"
#: users/templates/users/login.html:181
#: users/templates/users/login.html:199
msgid "Continue using this site implies consent to our <a href=\"/pages/rules/\">rules</a> and <a href=\"/pages/terms/\">terms</a>, including using cookies to provide necessary functionality."
msgstr "繼續訪問或註冊視爲同意<a href=\"/pages/rules/\">站規</a>與<a href=\"/pages/terms/\">協議</a>及使用cookie提供必要功能"
#: users/templates/users/login.html:187
#: users/templates/users/login.html:205
msgid "Domain of your instance (excl. @)"
msgstr "實例域名(不含@和@之前的部分)"
@ -3786,59 +3835,59 @@ msgstr "用戶資料"
msgid "original home"
msgstr "原始主頁"
#: users/templates/users/profile_actions.html:82
#: users/templates/users/profile_actions.html:63
msgid "accept follow request"
msgstr "接受關注請求"
#: users/templates/users/profile_actions.html:84
#: users/templates/users/profile_actions.html:65
msgid "sure to accept follow request?"
msgstr "確定接受關注請求嗎?"
#: users/templates/users/profile_actions.html:92
#: users/templates/users/profile_actions.html:73
msgid "reject follow request"
msgstr "拒絕關注請求"
#: users/templates/users/profile_actions.html:94
#: users/templates/users/profile_actions.html:75
msgid "sure to reject follow request?"
msgstr "確定拒絕關注請求嗎?"
#: users/templates/users/profile_actions.html:104
#: users/templates/users/profile_actions.html:85
msgid "click to unfollow"
msgstr "點擊可取消關注"
#: users/templates/users/profile_actions.html:106
#: users/templates/users/profile_actions.html:87
msgid "sure to unfollow?"
msgstr "確定取消關注該用戶嗎?"
#: users/templates/users/profile_actions.html:115
#: users/templates/users/profile_actions.html:96
msgid "click to cancel follow request"
msgstr "點擊可取消關注請求"
#: users/templates/users/profile_actions.html:117
#: users/templates/users/profile_actions.html:98
msgid "sure to cancel follow request?"
msgstr "確定取消關注請求嗎?"
#: users/templates/users/profile_actions.html:126
#: users/templates/users/profile_actions.html:107
msgid "click to follow"
msgstr "點擊可關注"
#: users/templates/users/profile_actions.html:127
#: users/templates/users/profile_actions.html:108
msgid "sure to follow?"
msgstr "確定關注該用戶嗎?"
#: users/templates/users/profile_actions.html:137
#: users/templates/users/profile_actions.html:118
msgid "click to mute"
msgstr "點擊可靜音"
#: users/templates/users/profile_actions.html:146
#: users/templates/users/profile_actions.html:127
msgid "click to unmute"
msgstr "點擊可取消靜音"
#: users/templates/users/profile_actions.html:156
#: users/templates/users/profile_actions.html:137
msgid "click to block"
msgstr "點擊可屏蔽"
#: users/templates/users/profile_actions.html:157
#: users/templates/users/profile_actions.html:138
msgid "sure to block?"
msgstr "確定屏蔽該用戶嗎?"

View file

@ -1,15 +1,91 @@
from functools import cached_property
from atproto import Client, SessionEvent, client_utils
from django.utils import timezone
from loguru import logger
from catalog.common import jsondata
from .common import SocialAccount
class Bluesky:
pass
BASE_DOMAIN = "bsky.app" # TODO support alternative servers
@staticmethod
def authenticate(username: str, password: str) -> "BlueskyAccount | None":
try:
client = Client()
profile = client.login(username, password)
session_string = client.export_session_string()
except Exception as e:
logger.debug(f"Bluesky login {username} exception {e}")
return None
existing_account = BlueskyAccount.objects.filter(
uid=profile.did, domain=Bluesky.BASE_DOMAIN
).first()
if existing_account:
existing_account.session_string = session_string
existing_account.save(update_fields=["access_data"])
existing_account.refresh(save=True, profile=profile)
return existing_account
account = BlueskyAccount(uid=profile.did, domain=Bluesky.BASE_DOMAIN)
account.session_string = session_string
account.refresh(save=False, profile=profile)
return account
class BlueskyAccount(SocialAccount):
app_username = jsondata.CharField(json_field_name="access_data", default="")
app_password = jsondata.EncryptedTextField(
# app_username = jsondata.CharField(json_field_name="access_data", default="")
# app_password = jsondata.EncryptedTextField(
# json_field_name="access_data", default=""
# )
session_string = jsondata.EncryptedTextField(
json_field_name="access_data", default=""
)
pass
def on_session_change(self, event, session) -> None:
logger.debug("Bluesky session changed:", event, repr(session))
if event in (SessionEvent.CREATE, SessionEvent.REFRESH):
session_string = session.export()
if session_string != self.session_string:
self.session_string = session_string
if self.pk:
self.save(update_fields=["access_data"])
@cached_property
def _client(self):
client = Client()
client.on_session_change(self.on_session_change)
self._profile = client.login(session_string=self.session_string)
return client
@property
def url(self):
return f"https://bsky.app/profile/{self.handle}"
def refresh(self, save=True, profile=None):
if not profile:
_ = self._client
profile = self._profile
self.handle = profile.handle
self.account_data = {
k: v for k, v in profile.__dict__.items() if isinstance(v, (int, str))
}
self.last_refresh = timezone.now()
self.last_reachable = self.last_refresh
if save:
self.save(
update_fields=[
"account_data",
"handle",
"last_refresh",
"last_reachable",
]
)
def post(self, content):
text = client_utils.TextBuilder().text(content)
# .link("Python SDK", "https://atproto.blue")
post = self._client.send_post(text)
return {"id": post.cid, "url": post.uri}

View file

@ -83,6 +83,8 @@ class SocialAccount(TypedModel):
if k
not in [
"_state",
"_client",
"_profile",
"api_domain",
"created",
"modified",

View file

@ -851,7 +851,7 @@ class MastodonAccount(SocialAccount):
spoiler_text,
attachments,
)
if response:
if response is not None:
if response.status_code in [200, 201]:
j = response.json()
return {"id": j["id"], "url": j["url"]}
@ -864,4 +864,4 @@ class MastodonAccount(SocialAccount):
# TODO
def get_reauthorize_url(self):
return reverse("mastodon:connect") + "?domain=" + self.domain
return reverse("mastodon:login") + "?domain=" + self.domain

View file

@ -20,4 +20,7 @@ urlpatterns = [
path("threads/uninstall", threads_uninstall, name="threads_uninstall"),
path("threads/delete", threads_delete, name="threads_delete"),
# Bluesky
path("bluesky/login", bluesky_login, name="bluesky_login"),
path("bluesky/reconnect", bluesky_reconnect, name="bluesky_reconnect"),
path("bluesky/disconnect", bluesky_disconnect, name="bluesky_disconnect"),
]

View file

@ -1,3 +1,4 @@
from .bluesky import *
from .email import *
from .mastodon import *
from .threads import *

44
mastodon/views/bluesky.py Normal file
View file

@ -0,0 +1,44 @@
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext as _
from django.views.decorators.http import require_http_methods
from common.views import render_error
from mastodon.models import bluesky
from ..models import Bluesky
from .common import disconnect_identity, process_verified_account
@require_http_methods(["POST"])
def bluesky_login(request: HttpRequest):
username = request.POST.get("username", "").strip()
password = request.POST.get("password", "").strip()
if not username or not password:
return render_error(
request,
_("Authentication failed"),
_("Username and app password is required."),
)
account = Bluesky.authenticate(username, password)
if not account:
return render_error(
request, _("Authentication failed"), _("Invalid account data from Bluesky.")
)
return process_verified_account(request, account)
@require_http_methods(["POST"])
@login_required
def bluesky_reconnect(request: HttpRequest):
"""link another bluesky to an existing logged-in user"""
return bluesky_login(request)
@require_http_methods(["POST"])
@login_required
def bluesky_disconnect(request):
"""unlink bluesky from an existing logged-in user"""
return disconnect_identity(request, request.user.bluesky)

View file

@ -26,7 +26,7 @@ def process_verified_account(request: HttpRequest, account: SocialAccount):
def login_existing_user(request: HttpRequest, account: SocialAccount):
user = authenticate(request, social_account=account)
if not user:
return render_error(_("Authentication failed"), _("Invalid user."))
return render_error(request, _("Authentication failed"), _("Invalid user."))
existing_user = account.user
auth_login(request, existing_user)
account.sync_later()
@ -41,19 +41,29 @@ def login_existing_user(request: HttpRequest, account: SocialAccount):
def register_new_user(request: HttpRequest, account: SocialAccount):
if request.user.is_authenticated:
return render_error(_("Registration failed"), _("User already logged in."))
return render_error(
request, _("Registration failed"), _("User already logged in.")
)
request.session["verified_account"] = account.to_dict()
return redirect(reverse("users:register"))
def reconnect_account(request, account: SocialAccount):
if account.user == request.user:
return render_error(
request, _("Unable to update login information: identical identity.")
account.sync_later()
messages.add_message(
request,
messages.INFO,
_("Continue login as {handle}.").format(handle=account.handle),
)
return redirect(reverse("users:info"))
elif account.user:
return render_error(
request, _("Unable to update login information: identity in use.")
request,
_("Unable to update login information"),
_("Identity {handle} in use by a different user.").format(
handle=account.handle
),
)
else:
# TODO add confirmation screen
@ -67,19 +77,26 @@ def reconnect_account(request, account: SocialAccount):
messages.add_message(
request,
messages.INFO,
_("Login information updated.") + account.handle,
_("Login information updated as {handle}.").format(
handle=account.handle
),
)
return redirect(reverse("users:info"))
def disconnect_identity(request, account):
if not account:
return render_error(_("Disconnect identity failed"), _("Identity not found."))
return render_error(
request, _("Disconnect identity failed"), _("Identity not found.")
)
if request.user != account.user:
return render_error(_("Disconnect identity failed"), _("Invalid user."))
return render_error(
request, _("Disconnect identity failed"), _("Invalid user.")
)
with transaction.atomic():
if request.user.social_accounts.all().count() <= 1:
return render_error(
request,
_("Disconnect identity failed"),
_("You cannot disconnect last login identity."),
)

View file

@ -37,12 +37,14 @@ def threads_oauth(request: HttpRequest):
code = request.GET.get("code")
if not code:
return render_error(
_("Authentication failed"), request.GET.get("error_description", "")
request,
_("Authentication failed"),
request.GET.get("error_description", ""),
)
account = Threads.authenticate(request, code)
if not account:
return render_error(
_("Authentication failed"), _("Invalid account data from Threads.")
request, _("Authentication failed"), _("Invalid account data from Threads.")
)
return process_verified_account(request, account)

View file

@ -0,0 +1,30 @@
{% if identity.user.mastodon %}
<span>
<a href="{{ identity.user.mastodon.url }}"
target="_blank"
rel="noopener"
title="@{{ identity.user.mastodon.handle }}">
<i class="fa-brands fa-mastodon"></i>
</a>
</span>
{% endif %}
{% if identity.user.threads %}
<span>
<a href="{{ identity.user.threads.url }}"
target="_blank"
rel="noopener"
title="@{{ identity.user.threads.handle }}">
<i class="fa-brands fa-threads"></i>
</a>
</span>
{% endif %}
{% if identity.user.bluesky %}
<span>
<a href="{{ identity.user.bluesky.url }}"
target="_blank"
rel="noopener"
title="@{{ identity.user.bluesky.handle }}">
<i class="fa-brands fa-bluesky"></i>
</a>
</span>
{% endif %}

View file

@ -154,7 +154,7 @@
</fieldset>
</form>
{% if request.user.threads %}
<form action="{% url 'mastodon:threads_login' %}"
<form action="{% url 'mastodon:threads_disconnect' %}"
method="post"
onsubmit="return confirm('{% trans "Once disconnected, you will no longer be able login with this identity. Are you sure to continue?" %}')">
{% csrf_token %}
@ -165,6 +165,60 @@
{% endif %}
</details>
</article>
<article>
<details>
<summary>{% trans "Bluesky (ATProto)" %}</summary>
<form action="{% url 'mastodon:bluesky_reconnect' %}" method="post">
{% csrf_token %}
<fieldset>
{% if request.user.bluesky %}
<label>
<i class="fa-brands fa-bluesky"></i> {% trans "Verified ATProto identity" %}
<input type="input"
aria-invalid="false"
value="@{{ request.user.bluesky.handle }} {{ request.user.bluesky.uid }}"
readonly>
<small>
{% if request.user.bluesky.last_refresh %}
{% trans "Last updated" %} {{ request.user.bluesky.last_refresh }}
{% endif %}
</small>
</label>
{% endif %}
<input required
type="email"
name="username"
autofocus
placeholder="{% trans 'Bluesky Login ID' %}"
autocorrect="off"
autocapitalize="off"
autocomplete="off"
spellcheck="false" />
<input required
type="password"
name="password"
placeholder="{% trans 'Bluesky app password' %}"
autocorrect="off"
autocapitalize="off"
autocomplete="off"
spellcheck="false" />
<input type="submit"
value="{% if request.user.bluesky %} {% trans 'Link with a different ATProto identity' %} {% else %} {% trans "Link with an ATProto identity" %} {% endif %} " />
<small>{% blocktrans %}App password can be created on <a href="https://bsky.app/settings/app-passwords" target="_blank">bsky.app</a>.{% endblocktrans %}</small>
</fieldset>
</form>
{% if request.user.bluesky %}
<form action="{% url 'mastodon:bluesky_disconnect' %}"
method="post"
onsubmit="return confirm('{% trans "Once disconnected, you will no longer be able login with this identity. Are you sure to continue?" %}')">
{% csrf_token %}
<input type="submit"
value="{% trans 'Disconnect with ATProto identity' %}"
class="secondary" />
</form>
{% endif %}
</details>
</article>
{% endif %}
<article>
<details>

View file

@ -80,7 +80,7 @@
{% if enable_bluesky %}
<button class="platform outline" _="on click add .outline to .platform then remove .outline from me then hide
<form/>
then show #login-bluesky" id="platform-bluesky" title="{% trans "Bluesky" %}">
then show #login-bluesky" id="platform-bluesky" title="{% trans "Bluesky (ATProto)" %}">
<i class="fa-brands fa-bluesky" style="font-size:85%"></i>
</button>
{% endif %}
@ -139,12 +139,30 @@
</form>
<form id="login-bluesky"
style="display:none"
action="{% url 'mastodon:login' %}"
action="{% url 'mastodon:bluesky_login' %}"
method="post"
onsubmit="return login(this);">
{% csrf_token %}
<input name="method" value="bluesky" type="hidden" />
<input type='submit' value="{% trans 'Authorize via Bluesky' %}" />
<input required
type="email"
name="username"
autofocus
placeholder="{% trans 'Bluesky Login ID' %}"
autocorrect="off"
autocapitalize="off"
autocomplete="off"
spellcheck="false" />
<input required
type="password"
name="password"
placeholder="{% trans 'Bluesky app password' %}"
autocorrect="off"
autocapitalize="off"
autocomplete="off"
spellcheck="false" />
<small>{% blocktrans %}App password can be created on <a href="https://bsky.app/settings/app-passwords" target="_blank">bsky.app</a>.{% endblocktrans %}</small>
<input type='submit' value="{% trans 'Authorize via bsky.app' %}" />
<small>{% trans "If you have already account here registered via a different way, you may login through there and link with your Bluesky account in account settings." %}</small>
</form>
{% else %}

View file

@ -55,26 +55,7 @@
</span>
{% endif %}
{% if not identity.locked or request.user.is_superuser or relationship.requested or relationship.status %}
{% if identity.user.mastodon %}
<span>
<a href="{{ identity.user.mastodon.url }}"
target="_blank"
rel="noopener"
title="@{{ identity.user.mastodon.handle }}">
<i class="fa-brands fa-mastodon"></i>
</a>
</span>
{% endif %}
{% if identity.user.threads %}
<span>
<a href="{{ identity.user.threads.url }}"
target="_blank"
rel="noopener"
title="@{{ identity.user.threads.handle }}">
<i class="fa-brands fa-threads"></i>
</a>
</span>
{% endif %}
{% include "users/_profile_social_icons.html" %}
{% endif %}
{% endif %}
{% if relationship.requested %}

View file

@ -46,8 +46,8 @@ def login(request):
"selected_domain": selected_domain,
"allow_any_site": settings.MASTODON_ALLOW_ANY_SITE,
"enable_email": settings.ENABLE_LOGIN_EMAIL,
"enable_threads": bool(settings.THREADS_APP_ID),
"enable_bluesky": settings.BLUESKY_LOGIN_ENABLED,
"enable_threads": settings.ENABLE_LOGIN_THREADS,
"enable_bluesky": settings.ENABLE_LOGIN_BLUESKY,
"invite_status": invite_status,
},
)