From b902b019fbd7d1b735a92d5a2d02386503e4ad4c Mon Sep 17 00:00:00 2001 From: tenma Date: Tue, 6 Aug 2019 21:28:55 +0100 Subject: [PATCH] [ActivityPub] Use queues for notice distribution ActivityPubPlugin: - Change event-based notice distribution to queues logic ActivityPub/lib: - Add queue handler class activitypubqueuehandler.php Misc: - Add documentation for the (Start/End)InitializeQueueManager events --- DOCUMENTATION/DEVELOPERS/EVENTS.txt | 6 + plugins/ActivityPub/ActivityPubPlugin.php | 133 +++++------------ .../lib/activitypubqueuehandler.php | 135 ++++++++++++++++++ 3 files changed, 178 insertions(+), 96 deletions(-) create mode 100644 plugins/ActivityPub/lib/activitypubqueuehandler.php diff --git a/DOCUMENTATION/DEVELOPERS/EVENTS.txt b/DOCUMENTATION/DEVELOPERS/EVENTS.txt index e3181faadb..005260dd1f 100644 --- a/DOCUMENTATION/DEVELOPERS/EVENTS.txt +++ b/DOCUMENTATION/DEVELOPERS/EVENTS.txt @@ -355,6 +355,12 @@ EndAvatarSaveForm: after saving the avatar StartNewQueueManager: before trying to start a new queue manager; good for plugins implementing new queue manager classes - $qm: empty queue manager to set +StartInitializeQueueManager: about to register queue handlers in the queue manager; good for plugins to register own handlers +- $qm: queue manager + +EndInitializeQueueManager: after registering queue handlers in the queue manager; good for plugins to register own handlers +- $qm: queue manager + RedirectToLogin: event when we force a redirect to login (like when going to a settings page on a remembered login) - $action: action object being shown - $user: current user diff --git a/plugins/ActivityPub/ActivityPubPlugin.php b/plugins/ActivityPub/ActivityPubPlugin.php index c4eaab183d..bdc703f95f 100644 --- a/plugins/ActivityPub/ActivityPubPlugin.php +++ b/plugins/ActivityPub/ActivityPubPlugin.php @@ -32,6 +32,7 @@ require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'disc require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'explorer.php'; require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'postman.php'; require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'inbox_handler.php'; +require_once __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'activitypubqueuehandler.php'; // So that this isn't hardcoded everywhere define('ACTIVITYPUB_BASE_ACTOR_URI', common_root_url().'index.php/user/'); @@ -218,6 +219,42 @@ class ActivityPubPlugin extends Plugin return true; } + /** + * Set up queue handlers for required interactions + * + * @param QueueManager $qm + * @return bool event hook return + */ + public function onEndInitializeQueueManager(QueueManager $qm): bool + { + // Notice distribution + $qm->connect('activitypub', 'ActivityPubQueueHandler'); + return true; + } + + /** + * Enqueue saved notices for distribution + * + * @param Notice $notice notice to be distributed + * @param Array &$transports list of transports to queue for + * @return bool event hook return + */ + public function onStartEnqueueNotice(Notice $notice, Array &$transports): bool + { + try { + $id = $notice->getID(); + + if ($id > 0) { + $transports[] = 'activitypub'; + $this->log(LOG_INFO, "Notice:{$id} queued for distribution"); + } + } catch (Exception $e) { + $this->log(LOG_ERR, "Invalid notice, not queueing for distribution"); + } + + return true; + } + /** * Plugin Nodeinfo information * @@ -787,102 +824,6 @@ class ActivityPubPlugin extends Plugin return true; } - /** - * Insert notifications for replies, mentions and repeats - * - * @param $notice - * @return boolean hook flag - * @throws EmptyPkeyValueException - * @throws HTTP_Request2_Exception - * @throws InvalidUrlException - * @throws ServerException - * @author Diogo Cordeiro - */ - public function onStartNoticeDistribute($notice) - { - assert($notice->id > 0); // Ignore if not a valid notice - - $profile = $notice->getProfile(); - - if (!$profile->isLocal()) { - return true; - } - - // Ignore for activity/non-(post/share)-verb notices - if (method_exists('ActivityUtils', 'compareVerbs')) { - $is_valid_verb = ActivityUtils::compareVerbs($notice->verb, - [ActivityVerb::POST, - ActivityVerb::SHARE]); - } else { - $is_valid_verb = ($notice->verb == ActivityVerb::POST || - $notice->verb == ActivityVerb::SHARE); - } - - if ($notice->source == 'activity' || !$is_valid_verb) { - common_log(LOG_DEBUG, "Ignoring distribution of notice with id {$notice->id} due to invalid Verb"); - return true; - } - - $other = Activitypub_profile::from_profile_collection( - $notice->getAttentionProfiles() - ); - - // Is a reply? - if ($notice->reply_to) { - try { - $parent_notice = $notice->getParent(); - - try { - $other[] = Activitypub_profile::from_profile($parent_notice->getProfile()); - } catch (Exception $e) { - // Local user can be ignored - } - - foreach ($parent_notice->getAttentionProfiles() as $mention) { - try { - $other[] = Activitypub_profile::from_profile($mention); - } catch (Exception $e) { - // Local user can be ignored - } - } - } catch (NoParentNoticeException $e) { - // This is not a reply to something (has no parent) - } catch (NoResultException $e) { - // Parent author's profile not found! Complain louder? - common_log(LOG_ERR, "Parent notice's author not found: ".$e->getMessage()); - } - } - - // Is an Announce? - if ($notice->isRepeat()) { - $repeated_notice = Notice::getKV('id', $notice->repeat_of); - if ($repeated_notice instanceof Notice) { - $other = array_merge($other, - Activitypub_profile::from_profile_collection( - $repeated_notice->getAttentionProfiles() - )); - - try { - $other[] = Activitypub_profile::from_profile($repeated_notice->getProfile()); - } catch (Exception $e) { - // Local user can be ignored - } - - // That was it - $postman = new Activitypub_postman($profile, $other); - $postman->announce($repeated_notice); - } - - // either made the announce or found nothing to repeat - return true; - } - - // That was it - $postman = new Activitypub_postman($profile, $other); - $postman->create_note($notice); - return true; - } - /** * Override the "from ActivityPub" bit in notice lists to link to the * original post and show the domain it came from. diff --git a/plugins/ActivityPub/lib/activitypubqueuehandler.php b/plugins/ActivityPub/lib/activitypubqueuehandler.php new file mode 100644 index 0000000000..af129a3d73 --- /dev/null +++ b/plugins/ActivityPub/lib/activitypubqueuehandler.php @@ -0,0 +1,135 @@ +. + +/** + * ActivityPub queue handler for notice distribution + * + * @package GNUsocial + * @author Bruno Casteleiro + * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later + */ + +defined('GNUSOCIAL') || die(); + +/** + * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later + */ +class ActivityPubQueueHandler extends QueueHandler +{ + /** + * Getter of the queue transport name. + * + * @return string transport name + */ + public function transport(): string + { + return 'activitypub'; + } + + /** + * Notice distribution handler. + * + * @param Notice $notice notice to be distributed. + * @return bool true on success, false otherwise + * @throws HTTP_Request2_Exception + * @throws InvalidUrlException + * @throws ServerException + * @author Diogo Cordeiro + */ + public function handle($notice): bool + { + if (!($notice instanceof Notice)) { + common_log(LOG_ERR, "Got a bogus notice, not distributing"); + return true; + } + + $profile = $notice->getProfile(); + + if (!$profile->isLocal()) { + return true; + } + + // Ignore activity/non-post/share-verb notices + $is_valid_verb = ($notice->verb == ActivityVerb::POST || + $notice->verb == ActivityVerb::SHARE); + + if ($notice->source == 'activity' || !$is_valid_verb) { + common_log(LOG_ERR, "Ignoring distribution of notice:{$notice->id}: activity source or invalid Verb"); + return true; + } + + $other = Activitypub_profile::from_profile_collection( + $notice->getAttentionProfiles() + ); + + // Handling a reply? + if ($notice->reply_to) { + try { + $parent_notice = $notice->getParent(); + + try { + $other[] = Activitypub_profile::from_profile($parent_notice->getProfile()); + } catch (Exception $e) { + // Local user can be ignored + } + + foreach ($parent_notice->getAttentionProfiles() as $mention) { + try { + $other[] = Activitypub_profile::from_profile($mention); + } catch (Exception $e) { + // Local user can be ignored + } + } + } catch (NoParentNoticeException $e) { + // This is not a reply to something (has no parent) + } catch (NoResultException $e) { + // Parent author's profile not found! Complain louder? + common_log(LOG_ERR, "Parent notice's author not found: ".$e->getMessage()); + } + } + + // Handling an Announce? + if ($notice->isRepeat()) { + $repeated_notice = Notice::getKV('id', $notice->repeat_of); + if ($repeated_notice instanceof Notice) { + $other = array_merge($other, + Activitypub_profile::from_profile_collection( + $repeated_notice->getAttentionProfiles() + )); + + try { + $other[] = Activitypub_profile::from_profile($repeated_notice->getProfile()); + } catch (Exception $e) { + // Local user can be ignored + } + + // That was it + $postman = new Activitypub_postman($profile, $other); + $postman->announce($repeated_notice); + } + + // either made the announce or found nothing to repeat + return true; + } + + // That was it + $postman = new Activitypub_postman($profile, $other); + $postman->create_note($notice); + return true; + } +} \ No newline at end of file