[MastodonBridge] Add support for various platforms (#3133)

* [MastodonBridge] Add support for various platforms

* [MastodonBridge] satisfy the lint
This commit is contained in:
Austin Huang 2022-11-16 17:05:01 +00:00 committed by GitHub
parent a5779d30b5
commit e788e14baa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 38 additions and 15 deletions

View File

@ -12,8 +12,8 @@ class MastodonBridge extends BridgeAbstract
const NAME = 'ActivityPub Bridge'; const NAME = 'ActivityPub Bridge';
const CACHE_TIMEOUT = 900; // 15mn const CACHE_TIMEOUT = 900; // 15mn
const DESCRIPTION = 'Returns recent statuses. Supports Mastodon, Pleroma and Misskey, among others. Access to const DESCRIPTION = 'Returns recent statuses. Supports Mastodon, Pleroma and Misskey, among others. Access to
instances that have Authorized Fetch enabled requires instances that have Authorized Fetch enabled requires
<a href="https://rss-bridge.github.io/rss-bridge/Bridge_Specific/ActivityPub_(Mastodon).html">configuration</a>.'; <a href="https://rss-bridge.github.io/rss-bridge/Bridge_Specific/ActivityPub_(Mastodon).html">configuration</a>.';
const URI = 'https://mastodon.social'; const URI = 'https://mastodon.social';
// Some Mastodon instances use Secure Mode which requires all requests to be signed. // Some Mastodon instances use Secure Mode which requires all requests to be signed.
@ -38,21 +38,22 @@ class MastodonBridge extends BridgeAbstract
'norep' => [ 'norep' => [
'name' => 'Without replies', 'name' => 'Without replies',
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Only return statuses that are not replies, as determined by relations (not mentions).' 'title' => 'Hide replies, as determined by relations (not mentions).'
], ],
'noboost' => [ 'noboost' => [
'name' => 'Without boosts', 'name' => 'Without boosts',
'required' => false,
'type' => 'checkbox', 'type' => 'checkbox',
'title' => 'Hide boosts. Note that RSS-Bridge will fetch the original status from other federated instances.' 'title' => 'Hide boosts. This will reduce loading time as RSS-Bridge fetches the boosted status from other federated instances.'
], ],
'signaturetype' => [ 'signaturetype' => [
'type' => 'list', 'type' => 'list',
'name' => 'Signature Type', 'name' => 'Signature Type',
'title' => 'How to sign requests when fetching from Authorized Fetch enabled instances', 'title' => 'How to sign requests when fetching from instances.
Defaults to "nosig" for RSS-Bridge instances that did not set up signatures.',
'values' => [ 'values' => [
'Without Query (Mastodon)' => 'noquery', 'Without Query (Mastodon)' => 'noquery',
'With Query (GoToSocial)' => 'query', 'With Query (GoToSocial)' => 'query',
'Don\'t sign' => 'nosig',
], ],
'defaultValue' => 'noquery' 'defaultValue' => 'noquery'
], ],
@ -60,12 +61,15 @@ class MastodonBridge extends BridgeAbstract
public function collectData() public function collectData()
{ {
$url = $this->getURI() . '/outbox?page=true'; $user = $this->fetchAP($this->getURI());
$content = $this->fetchAP($url); $content = $this->fetchAP($user['outbox']);
if ($content['id'] !== $url) { if (is_array($content['first'])) { // mobilizon
throw new \Exception('Unexpected response from server.'); $content = $content['first'];
} else {
$content = $this->fetchAP($content['first']);
} }
foreach ($content['orderedItems'] as $status) { $items = $content['orderedItems'] ?? $content['items'];
foreach ($items as $status) {
$item = $this->parseItem($status); $item = $this->parseItem($status);
if ($item) { if ($item) {
$this->items[] = $item; $this->items[] = $item;
@ -104,15 +108,26 @@ class MastodonBridge extends BridgeAbstract
$item['uri'] = $content['object']; $item['uri'] = $content['object'];
} }
break; break;
case 'Note': // frendica posts
if ($this->getInput('norep') && isset($content['inReplyTo'])) {
return null;
}
$item['title'] = '';
$item['author'] = $this->getInput('canusername');
$item = $this->parseObject($content, $item);
break;
case 'Create': // posts case 'Create': // posts
if ($this->getInput('norep') && isset($content['object']['inReplyTo'])) { if ($this->getInput('norep') && isset($content['object']['inReplyTo'])) {
return null; return null;
} }
$item['author'] = $this->getInput('canusername');
$item['title'] = ''; $item['title'] = '';
$item['author'] = $this->getInput('canusername');
$item = $this->parseObject($content['object'], $item); $item = $this->parseObject($content['object'], $item);
break;
default:
return null;
} }
$item['timestamp'] = $content['published']; $item['timestamp'] = $content['published'] ?? $item['timestamp'];
$item['uid'] = $content['id']; $item['uid'] = $content['id'];
return $item; return $item;
} }
@ -127,13 +142,20 @@ class MastodonBridge extends BridgeAbstract
$item['content'] = $object['content']; $item['content'] = $object['content'];
$strippedContent = strip_tags(str_replace('<br>', ' ', $object['content'])); $strippedContent = strip_tags(str_replace('<br>', ' ', $object['content']));
if (mb_strlen($strippedContent) > 75) { if (isset($object['name'])) {
$item['title'] = $object['name'];
} else if (mb_strlen($strippedContent) > 75) {
$contentSubstring = mb_substr($strippedContent, 0, mb_strpos(wordwrap($strippedContent, 75), "\n")); $contentSubstring = mb_substr($strippedContent, 0, mb_strpos(wordwrap($strippedContent, 75), "\n"));
$item['title'] .= $contentSubstring . '...'; $item['title'] .= $contentSubstring . '...';
} else { } else {
$item['title'] .= $strippedContent; $item['title'] .= $strippedContent;
} }
$item['uri'] = $object['id']; $item['uri'] = $object['id'];
$item['timestamp'] = $object['published'];
if (!isset($object['attachment'])) {
return $item;
}
if (isset($object['attachment']['url'])) { if (isset($object['attachment']['url'])) {
// Normalize attachment (turn single attachment into array) // Normalize attachment (turn single attachment into array)
@ -214,6 +236,7 @@ class MastodonBridge extends BridgeAbstract
// Exclude query string when parsing URL // Exclude query string when parsing URL
'noquery' => '/https?:\/\/([a-z0-9-\.]{0,})(\/[^#?]+)/', 'noquery' => '/https?:\/\/([a-z0-9-\.]{0,})(\/[^#?]+)/',
'nosig' => '/https?:\/\/([a-z0-9-\.]{0,})(\/[^#?]+)/',
]; ];
preg_match($regex[$this->getInput('signaturetype')], $url, $matches); preg_match($regex[$this->getInput('signaturetype')], $url, $matches);
@ -224,7 +247,7 @@ class MastodonBridge extends BridgeAbstract
]; ];
$privateKey = $this->getOption('private_key'); $privateKey = $this->getOption('private_key');
$keyId = $this->getOption('key_id'); $keyId = $this->getOption('key_id');
if ($privateKey && $keyId) { if ($privateKey && $keyId && $this->getInput('signaturetype') !== 'nosig') {
$pkey = openssl_pkey_get_private('file://' . $privateKey); $pkey = openssl_pkey_get_private('file://' . $privateKey);
$toSign = '(request-target): get ' . $matches[2] . "\nhost: " . $matches[1] . "\ndate: " . $date; $toSign = '(request-target): get ' . $matches[2] . "\nhost: " . $matches[1] . "\ndate: " . $date;
$result = openssl_sign($toSign, $signature, $pkey, 'RSA-SHA256'); $result = openssl_sign($toSign, $signature, $pkey, 'RSA-SHA256');