Last active
December 5, 2025 14:31
-
-
Save obenland/32197e7322ee1bad75f6979107378834 to your computer and use it in GitHub Desktop.
ActivityPub Relay Mode - Standalone plugin to add relay functionality to WordPress ActivityPub
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Plugin Name: ActivityPub Relay Mode | |
| * Description: Adds relay mode to the ActivityPub plugin to forward public activities to all followers. | |
| * Version: 1.0.0 | |
| * Author: Automattic | |
| * License: GPL-2.0-or-later | |
| * Requires Plugins: activitypub | |
| */ | |
| namespace Activitypub_Relay_Mode; | |
| use Activitypub\Activity\Activity; | |
| use Activitypub\Collection\Actors; | |
| use Activitypub\Collection\Outbox; | |
| use function Activitypub\is_activity_public; | |
| use function Activitypub\is_single_user; | |
| defined( 'ABSPATH' ) || exit; | |
| /** | |
| * Initialize the plugin. | |
| */ | |
| function init() { | |
| // Register setting and handle option changes. | |
| add_action( 'admin_init', __NAMESPACE__ . '\register_setting' ); | |
| add_action( 'update_option_activitypub_relay_mode', __NAMESPACE__ . '\relay_mode_changed', 10, 2 ); | |
| // Only load relay functionality if relay mode is enabled. | |
| if ( get_option( 'activitypub_relay_mode', false ) ) { | |
| add_action( 'activitypub_handled_create', __NAMESPACE__ . '\handle_activity', 10, 3 ); | |
| add_action( 'activitypub_handled_update', __NAMESPACE__ . '\handle_activity', 10, 3 ); | |
| add_action( 'activitypub_handled_delete', __NAMESPACE__ . '\handle_activity', 10, 3 ); | |
| add_action( 'activitypub_handled_announce', __NAMESPACE__ . '\handle_activity', 10, 3 ); | |
| add_action( 'load-settings_page_activitypub', __NAMESPACE__ . '\unhook_settings_fields', 11 ); | |
| add_filter( 'activitypub_blog_actor_type', __NAMESPACE__ . '\set_actor_type_service' ); | |
| } | |
| } | |
| add_action( 'plugins_loaded', __NAMESPACE__ . '\init' ); | |
| /** | |
| * Set the blog actor type to Service when relay mode is enabled. | |
| * | |
| * @param string $type The actor type. | |
| * @return string The modified actor type. | |
| */ | |
| function set_actor_type_service( $type ) { | |
| return 'Service'; | |
| } | |
| /** | |
| * Handle incoming activity and relay if needed. | |
| * | |
| * @param array $activity The activity data. | |
| * @param array $user_ids The user IDs that are recipients. | |
| * @param bool $success Whether the activity was handled successfully. | |
| */ | |
| function handle_activity( $activity, $user_ids, $success ) { | |
| // Only relay if: successfully handled, Blog actor is recipient, activity is public, and in single-user mode. | |
| if ( | |
| ! $success || | |
| ! in_array( Actors::BLOG_USER_ID, (array) $user_ids, true ) || | |
| ! is_activity_public( $activity ) || | |
| ! is_single_user() | |
| ) { | |
| return; | |
| } | |
| // Create Announce wrapper. | |
| $announce = new Activity(); | |
| $announce->set_type( 'Announce' ); | |
| $announce->set_actor( Actors::BLOG_USER_ID ); | |
| $announce->set_object( $activity ); | |
| $announce->set_published( gmdate( ACTIVITYPUB_DATE_TIME_RFC3339 ) ); | |
| // Add to outbox for distribution. The outbox will generate the ID. | |
| Outbox::add( $announce, Actors::BLOG_USER_ID ); | |
| } | |
| /** | |
| * Unhook settings fields when relay mode is enabled. | |
| * | |
| * Removes all settings sections except moderation when relay mode is active. | |
| */ | |
| function unhook_settings_fields() { | |
| global $wp_settings_sections; | |
| if ( ! isset( $wp_settings_sections['activitypub_settings'] ) ) { | |
| return; | |
| } | |
| // Keep only the moderation section. | |
| foreach ( $wp_settings_sections['activitypub_settings'] as $section_id => $section ) { | |
| if ( 'activitypub_moderation' !== $section_id ) { | |
| unset( $wp_settings_sections['activitypub_settings'][ $section_id ] ); | |
| } | |
| } | |
| } | |
| /** | |
| * Register the relay mode setting. | |
| */ | |
| function register_setting() { | |
| \register_setting( | |
| 'activitypub_advanced', | |
| 'activitypub_relay_mode', | |
| array( | |
| 'type' => 'integer', | |
| 'description' => 'Enable relay mode to forward public activities to all followers.', | |
| 'default' => 0, | |
| 'sanitize_callback' => 'absint', | |
| ) | |
| ); | |
| // Add settings field to advanced section. | |
| \add_settings_field( | |
| 'activitypub_relay_mode', | |
| \__( 'Relay Mode', 'activitypub-relay-mode' ), | |
| __NAMESPACE__ . '\relay_mode_field_callback', | |
| 'activitypub_settings', | |
| 'activitypub_advanced', | |
| array( 'label_for' => 'activitypub_relay_mode' ) | |
| ); | |
| } | |
| /** | |
| * Render the relay mode settings field. | |
| */ | |
| function relay_mode_field_callback() { | |
| $value = get_option( 'activitypub_relay_mode', 0 ); | |
| ?> | |
| <label> | |
| <input type="checkbox" name="activitypub_relay_mode" id="activitypub_relay_mode" value="1" <?php checked( $value, 1 ); ?>> | |
| <?php esc_html_e( 'Enable relay mode to forward public activities to all followers.', 'activitypub-relay-mode' ); ?> | |
| </label> | |
| <p class="description"> | |
| <?php esc_html_e( 'When enabled, this site will act as a relay, forwarding public activities from followed accounts to all followers. The blog actor will be set to "Service" type with username "relay".', 'activitypub-relay-mode' ); ?> | |
| </p> | |
| <?php | |
| } | |
| /** | |
| * Handle relay mode option changes. | |
| * | |
| * When relay mode is enabled, switch to blog-only mode and set username to "relay". | |
| * When disabled, restore previous settings. | |
| * | |
| * @param mixed $old_value The old option value. | |
| * @param mixed $new_value The new option value. | |
| */ | |
| function relay_mode_changed( $old_value, $new_value ) { | |
| if ( $new_value && ! $old_value ) { | |
| // Enabling relay mode. | |
| // Store previous username and actor mode for restoration. | |
| update_option( 'activitypub_relay_previous_blog_identifier', get_option( 'activitypub_blog_identifier' ) ); | |
| update_option( 'activitypub_relay_previous_actor_mode', get_option( 'activitypub_actor_mode' ) ); | |
| // Set blog username to "relay". | |
| update_option( 'activitypub_blog_identifier', 'relay' ); | |
| // Switch to blog-only mode. | |
| update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE ); | |
| } elseif ( ! $new_value && $old_value ) { | |
| // Disabling relay mode - restore previous settings. | |
| $previous_identifier = get_option( 'activitypub_relay_previous_blog_identifier' ); | |
| $previous_actor_mode = get_option( 'activitypub_relay_previous_actor_mode' ); | |
| if ( $previous_identifier ) { | |
| update_option( 'activitypub_blog_identifier', $previous_identifier ); | |
| delete_option( 'activitypub_relay_previous_blog_identifier' ); | |
| } | |
| if ( $previous_actor_mode ) { | |
| update_option( 'activitypub_actor_mode', $previous_actor_mode ); | |
| delete_option( 'activitypub_relay_previous_actor_mode' ); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment