ActivityPub 插件

@hellekin 感谢您的报告。在 ActivityPub 服务上,随着 Fediverse 中参与者(actors)的出现和消失,总会发生一定数量的失败请求。例如,看起来是这样的:

  • 第一个日志中的 URL https://activitypub.example 不是一个真实的 URL(您更改过它吗?)
  • 第二个日志中的用户 https://mas.to/users/rikvipcode 已不存在
  • 第三个日志中的用户 https://mastodon.social/users/ejovoni46709 已不存在

日志的详细程度是为了帮助调试,但是如果它们干扰了您的日志,您可以使用站点设置 activity pub verbose logging 来切换它们。默认是关闭的。

我们将根据需要,在第二阶段改进错误处理,但到目前为止,您发布的片段似乎都在预期之中,即参与者确实已不再存在于 Fediverse 中。

目前,该插件处理投递失败的方式与 Mastodon 相同,即如果连续 7 天向某个端点投递失败,该端点将被标记为“不可用”,并且将不再尝试发送请求。

3 个赞

是的,正如它前面那一行所述。

确实如此,但状态码是 410,这意味着该账户可能已移动(是否有墓碑 — 是否检查了此条件?)

1 个赞

不。这是 410 Gone 错误,表示资源已被删除。我现在还缺少什么?

1 个赞

您错过了这个:

您好 @angus

我刚刚在 https://federation.cafe 上使用您的 AP 插件设置了一个全新的自托管 Discourse,并在 Discourse 错误日志中看到了一些 403 错误(帖子没有共享)。

我想知道这是否是因为可能存在连字符?

[Discourse Activity Pub] GET 请求到 https://bofh.social/internal/fetch 失败:Expected([200, 201, 202, 301, 302, 307, 308]) <=> Actual(403 Forbidden)

/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:66:in `rescue in perform'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:50:in `perform'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:34:in `get_json_ld'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:106:in `get_json_ld'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/json_ld.rb:52:in `request_object'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/json_ld.rb:48:in `resolve_object'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/ap/actor.rb:103:in `resolve_and_store'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:192:in `actor_from_key_id'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:57:in `signed_request_actor'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:27:in `ensure_verified_signature'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:400:in `block in make_lambda'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:180:in `block (2 levels) in halting_and_conditional'
actionpack-7.0.4.3/lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:181:in `block in halting_and_conditional'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:595:in `block in invoke_before'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:595:in `each'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:595:in `invoke_before'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:116:in `block in run_callbacks'
/var/www/discourse/app/controllers/application_controller.rb:418:in `block in with_resolved_locale'
i18n-1.14.1/lib/i18n.rb:322:in `with_locale'
/var/www/discourse/app/controllers/application_controller.rb:418:in `with_resolved_locale'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:127:in `block in run_callbacks'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:138:in `run_callbacks'
actionpack-7.0.4.3/lib/abstract_controller/callbacks.rb:233:in `process_action'
actionpack-7.0.4.3/lib/action_controller/metal/rescue.rb:22:in `process_action'
actionpack-7.0.4.3/lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
activesupport-7.0.4.3/lib/active_support/notifications.rb:206:in `block in instrument'
activesupport-7.0.4.3/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport-7.0.4.3/lib/active_support/notifications.rb:206:in `instrument'
actionpack-7.0.4.3/lib/action_controller/metal/instrumentation.rb:66:in `process_action'
actionpack-7.0.4.3/lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord-7.0.4.3/lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack-7.0.4.3/lib/abstract_controller/base.rb:151:in `process'
actionview-7.0.4.3/lib/action_view/rendering.rb:39:in `process'
rack-mini-profiler-3.1.0/lib/mini_profiler/profiling_methods.rb:85:in `block in profile_method'
actionpack-7.0.4.3/lib/action_controller/metal.rb:188:in `dispatch'
actionpack-7.0.4.3/lib/action_controller/metal.rb:251:in `dispatch'
actionpack-7.0.4.3/lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
actionpack-7.0.4.3/lib/action_dispatch/routing/route_set.rb:32:in `serve'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:32:in `each'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack-7.0.4.3/lib/action_dispatch/routing/route_set.rb:852:in `call'
railties-7.0.4.3/lib/rails/engine.rb:530:in `call'
railties-7.0.4.3/lib/rails/railtie.rb:226:in `public_send'
railties-7.0.4.3/lib/rails/railtie.rb:226:in `method_missing'
actionpack-7.0.4.3/lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>'
actionpack-7.0.4.3/lib/action_dispatch/routing/mapper.rb:48:in `serve'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:32:in `each'
actionpack-7.0.4.3/lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack-7.0.4.3/lib/action_dispatch/routing/route_set.rb:852:in `call'
/var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:74:in `call'
rack-2.2.7/lib/rack/tempfile_reaper.rb:15:in `call'
rack-2.2.7/lib/rack/conditional_get.rb:27:in `call'
rack-2.2.7/lib/rack/head.rb:12:in `call'
actionpack-7.0.4.3/lib/action_dispatch/http/permissions_policy.rb:38:in `call'
/var/www/discourse/lib/content_security_policy/middleware.rb:12:in `call'
/var/www/discourse/lib/middleware/anonymous_cache.rb:367:in `call'
rack-2.2.7/lib/rack/session/abstract/id.rb:266:in `context'
rack-2.2.7/lib/rack/session/abstract/id.rb:260:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/cookies.rb:704:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport-7.0.4.3/lib/active_support/callbacks.rb:99:in `run_callbacks'
actionpack-7.0.4.3/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/show_exceptions.rb:26:in `call'
logster-2.12.2/lib/logster/middleware/reporter.rb:43:in `call'
railties-7.0.4.3/lib/rails/rack/logger.rb:40:in `call_app'
railties-7.0.4.3/lib/rails/rack/logger.rb:27:in `call'
/var/www/discourse/config/initializers/100-quiet_logger.rb:20:in `call'
/var/www/discourse/config/initializers/100-silence_logger.rb:29:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/request_id.rb:26:in `call'
/var/www/discourse/lib/middleware/enforce_hostname.rb:24:in `call'
rack-2.2.7/lib/rack/method_override.rb:24:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/executor.rb:14:in `call'
rack-2.2.7/lib/rack/sendfile.rb:110:in `call'
actionpack-7.0.4.3/lib/action_dispatch/middleware/host_authorization.rb:131:in `call'
rack-mini-profiler-3.1.0/lib/mini_profiler.rb:260:in `call'
message_bus-4.3.2/lib/message_bus/rack/middleware.rb:60:in `call'
/var/www/discourse/lib/middleware/request_tracker.rb:228:in `call'
railties-7.0.4.3/lib/rails/engine.rb:530:in `call'
railties-7.0.4.3/lib/rails/railtie.rb:226:in `public_send'
railties-7.0.4.3/lib/rails/railtie.rb:226:in `method_missing'
rack-2.2.7/lib/rack/urlmap.rb:74:in `block in call'
rack-2.2.7/lib/rack/urlmap.rb:58:in `each'
rack-2.2.7/lib/rack/urlmap.rb:58:in `call'
unicorn-6.1.0/lib/unicorn/http_server.rb:634:in `process_client'
unicorn-6.1.0/lib/unicorn/http_server.rb:739:in `worker_loop'
unicorn-6.1.0/lib/unicorn/http_server.rb:547:in `spawn_missing_workers'
unicorn-6.1.0/lib/unicorn/http_server.rb:143:in `start'
unicorn-6.1.0/bin/unicorn:128:in `<top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `load'
/var/www/discourse/vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `<main>'

hostname	LILEJAP07-app
process_id	658
application_version	1abfe2e61d12b1b559aab0132ec3fb7cc8b87232
HTTP_HOST	federation.cafe
REQUEST_URI	/ap/actor/5ce52c043e670476a1426f9a66472c07
REQUEST_METHOD	GET
HTTP_USER_AGENT	Akkoma 3.8.0-0-gccae7ef; https://bofh.social <admin@bofh.social>
HTTP_ACCEPT	application/activity+json
HTTP_X_FORWARDED_FOR	91.107.215.39, 172.71.250.70
HTTP_X_REAL_IP	172.71.250.70
time	2:16 pm

有什么想法吗?

我也对长篇帖子感兴趣。在这种情况下,应该使用“article”类型而不是“note”。

事实上,公告对我来说不如联合论坛功能有趣。

是否有路线图和/或可以捐款的地方来支持此类功能?

@gme 感谢您试用该插件

这里有几点需要初步注意

  1. MVP 插件已针对 Mastodon 进行测试。我看到您在那里使用的是 Pleroma。我知道 Pleroma 符合 ActivityPub 标准并且可以与 Mastodon 一起使用,但我们尚未仔细研究可能需要进行哪些调整(如果有的话)来确保支持它。但仍然有兴趣看看这里发生了什么。

  2. 看起来请求由于您的 Pleroma 服务器上的身份验证错误而失败(403 错误意味着这个)。由于我能够仅使用未经身份验证的 cURL 请求 GET 该端点,我怀疑可能是 Pleroma 端上的 http 身份验证失败了。

为了测试后者(即 2),您能否查看您的 Pleroma 日志(看起来您也是该服务器的管理员?)如果可能的话,看看您是否能在此处获得更多详细信息?

感谢您的反馈 @bmann。您能否详细说明您在此处考虑的用例?如果可能,请提供示例。

此主题是了解最新动态的最佳场所。当我们确定了第二阶段计划后,我将在此处分享。在此期间,最好的帮助方式是分享您正在使用或希望使用的具体用例。

5 个赞

用例是使用 Discourse 作为更完整的 AP 节点。发布内容到 AP 有许多更简单的方法(例如,使用分类 RSS 源和 Zapier 或 Buffer)——但开发更完整的 AP 功能只能通过插件/集成来实现。

Article 是用于完整文章的 ActivityStream 类型。根据客户端界面,它会显示一个预览,然后点击进入以在内联显示整篇文章(非常类似于内容警告,但需要点击“阅读更多”)。

Note 是用于微博客的类型。

通过拥有完整的 Article 帖子,人们可以直接在他们的 AP 客户端中阅读/转推/回复。

当然,如果你们打算遵循更偏向微博客的 AP 实例,或者朝着像 Lemmy 或 Kbin 那样的联邦论坛方向发展,尤其是在考虑到最近的 Reddit 新闻的情况下,我很想听听你们的路线图。

1 个赞

@angus@pmusaraj,你们看到 NGI Sargasso 的开放资金申请了吗?通知时间很短,但也许有助于进一步开发此插件(除非你们已有其他计划)。

1 个赞

大家好,很高兴地告诉大家,该插件的第二阶段工作已获批准。这是我们已经开始着手进行的工作,目标是在大约 3.5 个月内发布。

支持发布后的笔记编辑

支持恢复笔记

支持公开以及仅限关注者发布帖子

  • 分类设置
  • 参见 受众定位 和 Mastodon 关于 to/cc 的文档
  • 将公开帖子设为默认值

改进笔记内容解析

  • 处理特殊字符(可能使用不同的解析器)。参见

支持使用 文章 而不是 笔记 作为帖子的对象。

  • 分类设置

支持接受对远程笔记的回复活动,并发布对 Discourse 笔记的回复活动。

  • 发布关于 Discourse 上回复的活动
    • 允许 Discourse 用户成为参与者
    • 为 Discourse 回复(帖子)创建笔记对象
    • 发布与其等效的 Discourse 操作相关的创建/删除/更新/撤销活动
  • 接受关于远程回复的活动
    • 将来自远程服务器的活动的参与者暂存为 Discourse 用户
    • 从笔记对象创建 Discourse 回复(帖子)
    • 将相关的创建/删除/更新/撤销活动转换为其等效的 Discourse 操作
  • 添加一个分类设置,在仅限首帖(当前)和支持回复活动的“完整主题”之间切换。

支持点赞活动

支持 Discourse 用户在 Mastodon 上验证其身份,以便从其 Toots 创建的 Discourse 帖子与他们的 Discourse 用户帐户相关联。

  • 允许用户执行 Mastodon OAuth 授权流程,与存储其帐户的 Mastodon 服务器进行交互。这从 Discourse 用户帐户设置中启动。
  • 使用 Discourse 用户的 Mastodon 访问令牌,获取并存储其 Mastodon 帐户的 AP ID,并将其与 Discourse 帐户一起存储。
  • 将与具有 Discourse 用户 AP ID 的参与者相关的 Discourse 活动与该 Discourse 用户相关联,无论这些活动是在用户验证身份之前还是之后执行的。
15 个赞

看到这个真是太令人兴奋了——非常高水平的联合和互动。 :tada:

你们有计划发布任何中间版本吗,还是说这将在大约 3.5 个月后一次性大版本发布?

可能还会有一些中间版本,但我目前无法对此作出任何承诺。随着进展,我会及时向您通报情况。

2 个赞

110% 同意——这包含了许多很棒的方面。 :tada:

有没有可能至少在这个方面发布一个中间版本?

老实说,添加一个“公开帖子作为默认值”的中间版本将立即受到欢迎。

2 个赞

请注意,公开张贴将取决于联合编辑操作(列出的第一个项目符号),我对此表示相信。

1 个赞

我现在无法做出任何承诺,但“更新联合”和“受众定位”(公开发布)很可能会有中间更新。

4 个赞

:eyes:

1 个赞

这是一个已知的限制。在支持联合编辑之前,该插件会阻止对联合内容的编辑,并且没有禁用此功能的配置。

2 个赞

需要澄清的是,我试图在此元论坛上编辑帖子,但遇到了此错误。

3 个赞

哦,抱歉我误解了。@feature@meta.discourse.org@announcements@meta.discourse.org 至少是从这里联合的,而这个选择是我没有为 Maker Forums 启用它的首要原因……

2 个赞

是的,这是我在第二阶段负责的第一个项目。事实上,已经有一个 PR 了,所以你很快就能在这方面得到一些缓解。

2 个赞