Enable Mastodon Apps: Add followers_count to internal accounts (#676)

* Mock http requests, add followers_count to account object

* Remove one more MockAction

* Add one more assert

* cleanup

* cleanup

* use Users class, to check if user is really an ActivityPub user

---------

Co-authored-by: Matthias Pfefferle <pfefferle@users.noreply.github.com>
This commit is contained in:
Alex Kirk 2024-01-26 17:34:02 +01:00 committed by GitHub
parent e560d1b5b6
commit dad6b73cdb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 166 additions and 62 deletions

View file

@ -3,6 +3,7 @@ namespace Activitypub\Integration;
use DateTime;
use Activitypub\Webfinger as Webfinger_Util;
use Activitypub\Collection\Users;
use Activitypub\Collection\Followers;
use Enable_Mastodon_Apps\Entity\Account;
@ -21,6 +22,7 @@ class Enable_Mastodon_Apps {
*/
public static function init() {
\add_filter( 'mastodon_api_account_followers', array( self::class, 'api_account_followers' ), 10, 2 );
\add_filter( 'mastodon_api_account', array( self::class, 'api_account_add_followers' ), 20, 2 );
\add_filter( 'mastodon_api_account', array( self::class, 'api_account_external' ), 10, 2 );
}
@ -84,6 +86,21 @@ class Enable_Mastodon_Apps {
return $followers;
}
public static function api_account_add_followers( $account, $user_id ) {
if ( ! $account instanceof Account ) {
return $account;
}
$user = Users::get_by_id( $user_id );
if ( ! $user || is_wp_error( $user ) ) {
return $account;
}
$account->followers_count = Followers::count_followers( $user_id );
return $account;
}
/**
* Resolve external accounts for Mastodon API
*
@ -106,7 +123,7 @@ class Enable_Mastodon_Apps {
$acct = Webfinger_Util::uri_to_acct( $uri );
$data = get_remote_metadata_by_actor( $uri );
if ( ! $data ) {
if ( ! $data || is_wp_error( $data ) ) {
return $user_data;
}
@ -121,7 +138,9 @@ class Enable_Mastodon_Apps {
$account->acct = $acct;
$account->display_name = $data['name'];
$account->url = $uri;
if ( ! empty( $data['summary'] ) ) {
$account->note = $data['summary'];
}
if ( isset( $data['icon']['type'] ) && isset( $data['icon']['url'] ) && 'Image' === $data['icon']['type'] ) {
$account->avatar = $data['icon']['url'];

View file

@ -64,9 +64,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
public function test_get_followers() {
$followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' );
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
foreach ( $followers as $follower ) {
$response = \Activitypub\Collection\Followers::add_follower( 1, $follower );
}
@ -86,9 +83,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
}
public function test_add_follower() {
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
$follower = 'https://12345.example.com';
$follower2 = 'https://user2.example.com';
\Activitypub\Collection\Followers::add_follower( 1, $follower );
@ -103,9 +97,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
}
public function test_add_follower_error() {
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
$follower = 'error@example.com';
$result = \Activitypub\Collection\Followers::add_follower( 1, $follower );
@ -127,9 +118,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
$followers = array( 'https://example.com/author/jon' );
$followers2 = array( 'https://user2.example.com' );
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
foreach ( $followers as $follower ) {
\Activitypub\Collection\Followers::add_follower( 1, $follower );
}
@ -162,9 +150,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
);
$followers2 = array( 'https://user2.example.com' );
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
foreach ( $followers as $follower ) {
\Activitypub\Collection\Followers::add_follower( 1, $follower );
\Activitypub\Collection\Followers::add_follower( 1, $follower );
@ -200,9 +185,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
public function test_get_outdated_followers() {
$followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' );
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
foreach ( $followers as $follower ) {
\Activitypub\Collection\Followers::add_follower( 1, $follower );
}
@ -240,9 +222,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
public function test_get_faulty_followers() {
$followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' );
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
foreach ( $followers as $follower ) {
\Activitypub\Collection\Followers::add_follower( 1, $follower );
}
@ -272,9 +251,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
}
public function test_add_duplicate_follower() {
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
$follower = 'https://12345.example.com';
\Activitypub\Collection\Followers::add_follower( 1, $follower );
@ -295,9 +271,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
}
public function test_migration() {
$pre_http_request = new MockAction();
add_filter( 'pre_http_request', array( $pre_http_request, 'filter' ), 10, 3 );
$followers = array(
'https://example.com/author/jon',
'https://example.og/errors',
@ -405,36 +378,6 @@ class Test_Activitypub_Followers extends WP_UnitTestCase {
$this->assertCount( 30, $followers );
}
public static function http_request_host_is_external( $in, $host ) {
if ( in_array( $host, array( 'example.com', 'example.org' ), true ) ) {
return true;
}
return $in;
}
public static function http_request_args( $args, $url ) {
if ( in_array( wp_parse_url( $url, PHP_URL_HOST ), array( 'example.com', 'example.org' ), true ) ) {
$args['reject_unsafe_urls'] = false;
}
return $args;
}
public static function pre_http_request( $preempt, $request, $url ) {
return array(
'headers' => array(
'content-type' => 'text/json',
),
'body' => '',
'response' => array(
'code' => 202,
),
);
}
public static function http_response( $response, $args, $url ) {
return $response;
}
public static function pre_get_remote_metadata_by_actor( $pre, $actor ) {
if ( isset( self::$users[ $actor ] ) ) {
return self::$users[ $actor ];

View file

@ -1,15 +1,75 @@
<?php
class Test_Enable_Mastodon_Apps extends WP_UnitTestCase {
public static $users = array(
'username@example.org' => array(
'id' => 'https://example.org/users/username',
'url' => 'https://example.org/users/username',
'inbox' => 'https://example.org/users/username/inbox',
'name' => 'username',
'preferredUsername' => 'username',
'published' => '2024-01-01T00:00:00+00:00',
),
'jon@example.com' => array(
'id' => 'https://example.com/author/jon',
'url' => 'https://example.com/author/jon',
'inbox' => 'https://example.com/author/jon/inbox',
'name' => 'jon',
'preferredUsername' => 'jon',
),
'doe@example.org' => array(
'id' => 'https://example.org/author/doe',
'url' => 'https://example.org/author/doe',
'inbox' => 'https://example.org/author/doe/inbox',
'name' => 'doe',
'preferredUsername' => 'doe',
),
'sally@example.org' => array(
'id' => 'http://sally.example.org',
'url' => 'http://sally.example.org',
'inbox' => 'http://sally.example.org/inbox',
'name' => 'jon',
'preferredUsername' => 'jon',
),
'12345@example.com' => array(
'id' => 'https://12345.example.com',
'url' => 'https://12345.example.com',
'inbox' => 'https://12345.example.com/inbox',
'name' => '12345',
'preferredUsername' => '12345',
),
'user2@example.com' => array(
'id' => 'https://user2.example.com',
'url' => 'https://user2.example.com',
'inbox' => 'https://user2.example.com/inbox',
'name' => 'úser2',
'preferredUsername' => 'user2',
),
'error@example.com' => array(
'url' => 'https://error.example.com',
'name' => 'error',
'preferredUsername' => 'error',
),
);
public function set_up() {
parent::set_up();
if ( ! class_exists( '\Enable_Mastodon_Apps\Entity\Entity' ) ) {
self::markTestSkipped( 'The Enable_Mastodon_Apps plugin is not active.' );
}
add_filter( 'pre_get_remote_metadata_by_actor', array( get_called_class(), 'pre_get_remote_metadata_by_actor' ), 10, 2 );
add_filter( 'pre_http_request', array( $this, 'pre_http_request' ), 10, 3 );
_delete_all_posts();
}
public function tear_down() {
remove_filter( 'pre_get_remote_metadata_by_actor', array( get_called_class(), 'pre_get_remote_metadata_by_actor' ) );
remove_filter( 'pre_http_request', array( $this, 'pre_http_request' ), 10, 3 );
parent::tear_down();
}
public function test_api_account_external() {
$account = apply_filters( 'mastodon_api_account', array(), 'alex@kirk.at' );
$account = apply_filters( 'mastodon_api_account', array(), 'username@example.org' );
$this->assertNotEmpty( $account );
$account = $account->jsonSerialize();
$this->assertArrayHasKey( 'id', $account );
@ -17,7 +77,89 @@ class Test_Enable_Mastodon_Apps extends WP_UnitTestCase {
$this->assertArrayHasKey( 'acct', $account );
$this->assertArrayHasKey( 'display_name', $account );
$this->assertArrayHasKey( 'url', $account );
$this->assertEquals( 'https://alex.kirk.at/author/alex/', $account['url'] );
$this->assertEquals( 'Alex Kirk', $account['display_name'] );
$this->assertEquals( 'https://example.org/users/username', $account['url'] );
$this->assertEquals( 'username', $account['display_name'] );
}
public function test_api_account_followers_internal() {
$followers = array( 'https://example.com/author/jon', 'https://example.org/author/doe', 'http://sally.example.org' );
foreach ( $followers as $follower ) {
$response = \Activitypub\Collection\Followers::add_follower( 1, $follower );
}
$account = new \Enable_Mastodon_Apps\Entity\Account();
$this->assertEquals( 0, $account->followers_count );
$account = apply_filters( 'mastodon_api_account', $account, 1 );
$this->assertEquals( 3, $account->followers_count );
}
public static function pre_http_request( $preempt, $request, $url ) {
switch( $url ) {
case 'https://example.org/.well-known/webfinger?resource=acct%3Ausername%40example.org':
case 'https://example.org/.well-known/webfinger?resource=https%3A%2F%2Fexample.org%2Fusers%2Fusername':
return array(
'headers' => array(
'content-type' => 'text/json',
),
'body' => json_encode( array(
'subject' => 'acct:username@example.org',
'links' => array(
array(
'rel' => 'self',
'type' => 'application/activity+json',
'href' => 'https://example.org/users/username',
),
),
) ),
'response' => array(
'code' => 200,
),
);
case 'https://example.org/users/username':
return array(
'headers' => array(
'content-type' => 'application/activity+json',
),
'body' => json_encode( self::$users['username@example.org'] ),
'response' => array(
'code' => 200,
),
);
}
return $preempt;
}
public static function http_response( $response, $args, $url ) {
return $response;
}
public static function pre_get_remote_metadata_by_actor( $pre, $actor ) {
if ( isset( self::$users[ $actor ] ) ) {
return self::$users[ $actor ];
}
foreach ( self::$users as $username => $data ) {
if ( $data['url'] === $actor ) {
return $data;
}
}
return $pre;
}
public function extract_name_from_uri_content_provider() {
return array(
array( 'https://example.com/@user', 'user' ),
array( 'https://example.com/@user/', 'user' ),
array( 'https://example.com/users/user', 'user' ),
array( 'https://example.com/users/user/', 'user' ),
array( 'https://example.com/@user?as=asasas', 'user' ),
array( 'https://example.com/@user#asass', 'user' ),
array( '@user@example.com', 'user' ),
array( 'acct:user@example.com', 'user' ),
array( 'user@example.com', 'user' ),
array( 'https://example.com', 'https://example.com' ),
);
}
}