mirror of https://github.com/nextcloud/contacts
use Mastodon API instead of scraping
fixes #3151 Signed-off-by: call-me-matt <nextcloud@matthiasheinisch.de>
This commit is contained in:
parent
753a66b309
commit
4d8449d971
|
@ -31,7 +31,7 @@ class MastodonProvider implements ISocialProvider {
|
|||
private $httpClient;
|
||||
|
||||
/** @var string */
|
||||
public $name = "mastodon";
|
||||
public $name = 'mastodon';
|
||||
|
||||
public function __construct(IClientService $httpClient) {
|
||||
$this->httpClient = $httpClient->NewClient();
|
||||
|
@ -45,7 +45,7 @@ class MastodonProvider implements ISocialProvider {
|
|||
* @return bool
|
||||
*/
|
||||
public function supportsContact(array $contact):bool {
|
||||
if (!array_key_exists("X-SOCIALPROFILE",$contact)) {
|
||||
if (!array_key_exists('X-SOCIALPROFILE', $contact)) {
|
||||
return false;
|
||||
}
|
||||
$profiles = $this->getProfileIds($contact);
|
||||
|
@ -62,7 +62,6 @@ class MastodonProvider implements ISocialProvider {
|
|||
public function getImageUrls(array $contact):array {
|
||||
$profileIds = $this->getProfileIds($contact);
|
||||
$urls = [];
|
||||
|
||||
foreach ($profileIds as $profileId) {
|
||||
$url = $this->getImageUrl($profileId);
|
||||
if (isset($url)) {
|
||||
|
@ -82,21 +81,15 @@ class MastodonProvider implements ISocialProvider {
|
|||
public function getImageUrl(string $profileUrl):?string {
|
||||
try {
|
||||
$result = $this->httpClient->get($profileUrl);
|
||||
|
||||
$htmlResult = new \DOMDocument();
|
||||
$htmlResult->loadHTML($result->getBody());
|
||||
$img = $htmlResult->getElementById('profile_page_avatar');
|
||||
if (!is_null($img)) {
|
||||
return $img->getAttribute("data-original");
|
||||
}
|
||||
return null;
|
||||
$jsonResult = json_decode($result->getBody());
|
||||
return $jsonResult->avatar;
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all possible profile ids for contact
|
||||
* Returns all possible profile URI for contact by searching the mastodon instance
|
||||
*
|
||||
* @param {array} contact information
|
||||
*
|
||||
|
@ -108,9 +101,21 @@ class MastodonProvider implements ISocialProvider {
|
|||
if (isset($socialprofiles)) {
|
||||
foreach ($socialprofiles as $profile) {
|
||||
if (strtolower($profile['type']) == $this->name) {
|
||||
$profileId = $this->cleanupId($profile['value']);
|
||||
if (isset($profileId)) {
|
||||
$profileIds[] = $profileId;
|
||||
$masto_user_server = $this->cleanupId($profile['value']);
|
||||
if (isset($masto_user_server)) {
|
||||
try {
|
||||
[$masto_user, $masto_server] = $masto_user_server;
|
||||
# search for user on Mastodon
|
||||
$search = $masto_server . '/api/v2/search?q=' . $masto_user;
|
||||
$result = $this->httpClient->get($search);
|
||||
$jsonResult = json_decode($result->getBody());
|
||||
# take first search result
|
||||
$masto_id = $jsonResult->accounts[0]->id;
|
||||
$profileId = $masto_server . "/api/v1/accounts/" . $masto_id;
|
||||
$profileIds[] = $profileId;
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,18 +128,25 @@ class MastodonProvider implements ISocialProvider {
|
|||
*
|
||||
* @param {string} the value from the contact's x-socialprofile
|
||||
*
|
||||
* @return string
|
||||
* @return array username and server instance
|
||||
*/
|
||||
protected function cleanupId(string $candidate):?string {
|
||||
protected function cleanupId(string $candidate):?array {
|
||||
$candidate = preg_replace('/^' . preg_quote('x-apple:', '/') . '/', '', $candidate);
|
||||
try {
|
||||
$user_server = explode('@', $candidate);
|
||||
if (strpos($candidate, 'http') !== 0) {
|
||||
$user_server = explode('@', $candidate);
|
||||
$candidate = 'https://' . array_pop($user_server) . '/@' . array_pop($user_server);
|
||||
$masto_server = "https://" . array_pop($user_server);
|
||||
$masto_user = array_pop($user_server);
|
||||
} else {
|
||||
$masto_user = array_pop($user_server);
|
||||
$masto_server = array_pop($user_server);
|
||||
}
|
||||
if ((empty($masto_server)) || (empty($masto_user))) {
|
||||
return null;
|
||||
}
|
||||
return [$masto_user, $masto_server];
|
||||
} catch (\Exception $e) {
|
||||
$candidate = null;
|
||||
return null;
|
||||
}
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,20 +90,33 @@ class MastodonProviderTest extends TestCase {
|
|||
$contactWithSocial = [
|
||||
'X-SOCIALPROFILE' => [
|
||||
["value" => "user1@cloud1", "type" => "mastodon"],
|
||||
["value" => "user2@cloud2", "type" => "mastodon"]
|
||||
["value" => "@user2@cloud2", "type" => "mastodon"],
|
||||
["value" => "https://cloud3/@user3", "type" => "mastodon"],
|
||||
["value" => "https://cloud/wrongSyntax", "type" => "mastodon"],
|
||||
["value" => "@wrongSyntax", "type" => "mastodon"],
|
||||
["value" => "wrongSyntax", "type" => "mastodon"]
|
||||
]
|
||||
];
|
||||
$contactWithSocialUrls = [
|
||||
"https://cloud1/@user1",
|
||||
"https://cloud2/@user2",
|
||||
"https://cloud1/api/v2/search?q=user1",
|
||||
"https://cloud2/api/v2/search?q=user2",
|
||||
"https://cloud3//api/v2/search?q=user3",
|
||||
"https://cloud1/api/v1/accounts/1",
|
||||
"https://cloud2/api/v1/accounts/2",
|
||||
"https://cloud3//api/v1/accounts/3"
|
||||
];
|
||||
$contactWithSocialHtml = [
|
||||
'<html><profile id="profile_page_avatar" data-original="user1.jpg" /></html>',
|
||||
'<html><profile id="profile_page_avatar" data-original="user2.jpg" /></html>'
|
||||
$contactWithSocialApi = [
|
||||
'{"accounts":[{"id":"1","username":"user1"}]}',
|
||||
'{"accounts":[{"id":"2","username":"user2"}]}',
|
||||
'{"accounts":[{"id":"3","username":"user3"}]}',
|
||||
'{"id":"1","avatar":"user1.jpg"}',
|
||||
'{"id":"2","avatar":"user2.jpg"}',
|
||||
'{"id":"3","avatar":"user3.jpg"}'
|
||||
];
|
||||
$contactWithSocialImgs = [
|
||||
"user1.jpg",
|
||||
"user2.jpg"
|
||||
"user2.jpg",
|
||||
"user3.jpg"
|
||||
];
|
||||
|
||||
$contactWithoutSocial = [
|
||||
|
@ -113,19 +126,19 @@ class MastodonProviderTest extends TestCase {
|
|||
]
|
||||
];
|
||||
$contactWithoutSocialUrls = [];
|
||||
$contactWithoutSocialHtml = [];
|
||||
$contactWithoutSocialApi = [];
|
||||
$contactWithoutSocialImgs = [];
|
||||
|
||||
return [
|
||||
'contact with mastodon fields' => [
|
||||
$contactWithSocial,
|
||||
$contactWithSocialHtml,
|
||||
$contactWithSocialApi,
|
||||
$contactWithSocialUrls,
|
||||
$contactWithSocialImgs
|
||||
],
|
||||
'contact without mastodon fields' => [
|
||||
$contactWithoutSocial,
|
||||
$contactWithoutSocialHtml,
|
||||
$contactWithoutSocialApi,
|
||||
$contactWithoutSocialUrls,
|
||||
$contactWithoutSocialImgs
|
||||
]
|
||||
|
@ -135,9 +148,9 @@ class MastodonProviderTest extends TestCase {
|
|||
/**
|
||||
* @dataProvider dataProviderGetImageUrls
|
||||
*/
|
||||
public function testGetImageUrls($contact, $htmls, $urls, $imgs) {
|
||||
public function testGetImageUrls($contact, $api, $urls, $imgs) {
|
||||
if (count($urls)) {
|
||||
$this->response->method("getBody")->willReturnOnConsecutiveCalls(...$htmls);
|
||||
$this->response->method("getBody")->willReturnOnConsecutiveCalls(...$api);
|
||||
$this->client
|
||||
->expects($this->exactly(count($urls)))
|
||||
->method("get")
|
||||
|
@ -146,8 +159,6 @@ class MastodonProviderTest extends TestCase {
|
|||
}, $urls))
|
||||
->willReturn($this->response);
|
||||
}
|
||||
|
||||
|
||||
$result = $this->provider->getImageUrls($contact);
|
||||
$this->assertEquals($imgs, $result);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue