Bluesky: Support personal data servers
parent
5f5c53ab49
commit
00e30b5c2b
|
@ -36,6 +36,7 @@ use Friendica\Core\Worker;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GServer;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\ItemURI;
|
||||
use Friendica\Model\Photo;
|
||||
|
@ -49,9 +50,15 @@ use Friendica\Util\DateTimeFormat;
|
|||
use Friendica\Util\Strings;
|
||||
|
||||
const BLUESKY_DEFAULT_POLL_INTERVAL = 10; // given in minutes
|
||||
const BLUESKY_HOST = 'https://bsky.app'; // Hard wired until Bluesky will run on multiple systems
|
||||
const BLUESKY_IMAGE_SIZE = [1000000, 500000, 100000, 50000];
|
||||
|
||||
/*
|
||||
* (Currently) hard wired paths for Bluesky services
|
||||
*/
|
||||
const BLUESKY_DIRECTORY = 'https://plc.directory'; // Path to the directory server service to fetch the PDS of a given DID
|
||||
const BLUESKY_PDS = 'https://bsky.social'; // Path to the personal data server service (PDS) to fetch the DID for a given handle
|
||||
const BLUESKY_WEB = 'https://bsky.app'; // Path to the web interface with the user profile and posts
|
||||
|
||||
function bluesky_install()
|
||||
{
|
||||
Hook::register('load_config', __FILE__, 'bluesky_load_config');
|
||||
|
@ -106,8 +113,8 @@ function bluesky_probe_detect(array &$hookData)
|
|||
|
||||
if (parse_url($hookData['uri'], PHP_URL_SCHEME) == 'did') {
|
||||
$did = $hookData['uri'];
|
||||
} elseif (preg_match('#^' . BLUESKY_HOST . '/profile/(.+)#', $hookData['uri'], $matches)) {
|
||||
$did = bluesky_get_did($pconfig['uid'], $matches[1]);
|
||||
} elseif (preg_match('#^' . BLUESKY_WEB . '/profile/(.+)#', $hookData['uri'], $matches)) {
|
||||
$did = bluesky_get_did($matches[1]);
|
||||
if (empty($did)) {
|
||||
return;
|
||||
}
|
||||
|
@ -127,6 +134,8 @@ function bluesky_probe_detect(array &$hookData)
|
|||
|
||||
$hookData['result'] = bluesky_get_contact_fields($data, 0, false);
|
||||
|
||||
$hookData['result']['baseurl'] = bluesky_get_pds($did);
|
||||
|
||||
// Preparing probe data. This differs slightly from the contact array
|
||||
$hookData['result']['about'] = HTML::toBBCode($data->description ?? '');
|
||||
$hookData['result']['photo'] = $data->avatar ?? '';
|
||||
|
@ -152,11 +161,11 @@ function bluesky_item_by_link(array &$hookData)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!preg_match('#^' . BLUESKY_HOST . '/profile/(.+)/post/(.+)#', $hookData['uri'], $matches)) {
|
||||
if (!preg_match('#^' . BLUESKY_WEB . '/profile/(.+)/post/(.+)#', $hookData['uri'], $matches)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$did = bluesky_get_did($hookData['uid'], $matches[1]);
|
||||
$did = bluesky_get_did($matches[1]);
|
||||
if (empty($did)) {
|
||||
return;
|
||||
}
|
||||
|
@ -306,7 +315,7 @@ function bluesky_settings(array &$data)
|
|||
|
||||
$enabled = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'post') ?? false;
|
||||
$def_enabled = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'post_by_default') ?? false;
|
||||
$host = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'host') ?: 'https://bsky.social';
|
||||
$pds = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'pds');
|
||||
$handle = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'handle');
|
||||
$did = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'did');
|
||||
$token = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'access_token');
|
||||
|
@ -321,7 +330,7 @@ function bluesky_settings(array &$data)
|
|||
'$bydefault' => ['bluesky_bydefault', DI::l10n()->t('Post to Bluesky by default'), $def_enabled],
|
||||
'$import' => ['bluesky_import', DI::l10n()->t('Import the remote timeline'), $import],
|
||||
'$import_feeds' => ['bluesky_import_feeds', DI::l10n()->t('Import the pinned feeds'), $import_feeds, DI::l10n()->t('When activated, Posts will be imported from all the feeds that you pinned in Bluesky.')],
|
||||
'$host' => ['bluesky_host', DI::l10n()->t('Bluesky host'), $host, '', '', 'readonly'],
|
||||
'$pds' => ['bluesky_pds', DI::l10n()->t('Personal Data Server'), $pds, DI::l10n()->t('The personal data server (PDS) is the system that hosts your profile.'), '', 'readonly'],
|
||||
'$handle' => ['bluesky_handle', DI::l10n()->t('Bluesky handle'), $handle],
|
||||
'$did' => ['bluesky_did', DI::l10n()->t('Bluesky DID'), $did, DI::l10n()->t('This is the unique identifier. It will be fetched automatically, when the handle is entered.'), '', 'readonly'],
|
||||
'$password' => ['bluesky_password', DI::l10n()->t('Bluesky app password'), '', DI::l10n()->t("Please don't add your real password here, but instead create a specific app password in the Bluesky settings.")],
|
||||
|
@ -343,26 +352,28 @@ function bluesky_settings_post(array &$b)
|
|||
return;
|
||||
}
|
||||
|
||||
$old_host = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'host');
|
||||
$old_pds = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'pds');
|
||||
$old_handle = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'handle');
|
||||
$old_did = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'did');
|
||||
|
||||
$host = $_POST['bluesky_host'];
|
||||
$handle = $_POST['bluesky_handle'];
|
||||
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'post', intval($_POST['bluesky']));
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'post_by_default', intval($_POST['bluesky_bydefault']));
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'host', $host);
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'handle', $handle);
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'import', intval($_POST['bluesky_import']));
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'import_feeds', intval($_POST['bluesky_import_feeds']));
|
||||
|
||||
if (!empty($host) && !empty($handle)) {
|
||||
if (empty($old_did) || $old_host != $host || $old_handle != $handle) {
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'did', bluesky_get_did(DI::userSession()->getLocalUserId(), DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'handle')));
|
||||
if (empty($old_did) || $old_handle != $handle) {
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'did', bluesky_get_did(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'handle')));
|
||||
}
|
||||
if (empty($old_pds) || $old_handle != $handle) {
|
||||
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'pds', bluesky_get_pds(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'did')));
|
||||
}
|
||||
} else {
|
||||
DI::pConfig()->delete(DI::userSession()->getLocalUserId(), 'bluesky', 'did');
|
||||
DI::pConfig()->delete(DI::userSession()->getLocalUserId(), 'bluesky', 'pds');
|
||||
}
|
||||
|
||||
if (!empty($_POST['bluesky_password'])) {
|
||||
|
@ -1452,10 +1463,9 @@ function bluesky_get_contact_fields(stdClass $author, int $uid, bool $update): a
|
|||
'blocked' => false,
|
||||
'readonly' => false,
|
||||
'pending' => false,
|
||||
'baseurl' => BLUESKY_HOST,
|
||||
'url' => $author->did,
|
||||
'nurl' => $author->did,
|
||||
'alias' => BLUESKY_HOST . '/profile/' . $author->handle,
|
||||
'alias' => BLUESKY_WEB . '/profile/' . $author->handle,
|
||||
'name' => $author->displayName ?? $author->handle,
|
||||
'nick' => $author->handle,
|
||||
'addr' => $author->handle,
|
||||
|
@ -1466,6 +1476,12 @@ function bluesky_get_contact_fields(stdClass $author, int $uid, bool $update): a
|
|||
return $fields;
|
||||
}
|
||||
|
||||
$fields['baseurl'] = bluesky_get_pds($author->did);
|
||||
if (!empty($fields['baseurl'])) {
|
||||
GServer::check($fields['baseurl'], Protocol::BLUESKY);
|
||||
$fields['gsid'] = GServer::getID($fields['baseurl'], true);
|
||||
}
|
||||
|
||||
$data = bluesky_xrpc_get($uid, 'app.bsky.actor.getProfile', ['actor' => $author->did]);
|
||||
if (empty($data)) {
|
||||
Logger::debug('Error fetching contact fields', ['uid' => $uid, 'url' => $fields['url']]);
|
||||
|
@ -1524,9 +1540,9 @@ function bluesky_get_preferences(int $uid): stdClass
|
|||
return $data;
|
||||
}
|
||||
|
||||
function bluesky_get_did(int $uid, string $handle): string
|
||||
function bluesky_get_did(string $handle): string
|
||||
{
|
||||
$data = bluesky_get($uid, '/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle));
|
||||
$data = bluesky_get(BLUESKY_PDS . '/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle));
|
||||
if (empty($data)) {
|
||||
return '';
|
||||
}
|
||||
|
@ -1534,6 +1550,33 @@ function bluesky_get_did(int $uid, string $handle): string
|
|||
return $data->did;
|
||||
}
|
||||
|
||||
function bluesky_get_user_pds(int $uid): string
|
||||
{
|
||||
$pds = DI::pConfig()->get($uid, 'bluesky', 'pds');
|
||||
if (!empty($pds)) {
|
||||
return $pds;
|
||||
}
|
||||
$pds = bluesky_get_pds(DI::pConfig()->get($uid, 'bluesky', 'did'));
|
||||
DI::pConfig()->set($uid, 'bluesky', 'pds', $pds);
|
||||
return $pds;
|
||||
}
|
||||
|
||||
function bluesky_get_pds(string $did): ?string
|
||||
{
|
||||
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did);
|
||||
if (empty($data) || empty($data->service)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($data->service as $service) {
|
||||
if (($service->id == '#atproto_pds') && ($service->type == 'AtprotoPersonalDataServer') && !empty($service->serviceEndpoint)) {
|
||||
return $service->serviceEndpoint;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function bluesky_get_token(int $uid): string
|
||||
{
|
||||
$token = DI::pConfig()->get($uid, 'bluesky', 'access_token');
|
||||
|
@ -1588,7 +1631,7 @@ function bluesky_xrpc_post(int $uid, string $url, $parameters): ?stdClass
|
|||
function bluesky_post(int $uid, string $url, string $params, array $headers): ?stdClass
|
||||
{
|
||||
try {
|
||||
$curlResult = DI::httpClient()->post(DI::pConfig()->get($uid, 'bluesky', 'host') . $url, $params, $headers);
|
||||
$curlResult = DI::httpClient()->post(bluesky_get_user_pds($uid) . $url, $params, $headers);
|
||||
} catch (\Exception $e) {
|
||||
Logger::notice('Exception on post', ['exception' => $e]);
|
||||
return null;
|
||||
|
@ -1608,13 +1651,13 @@ function bluesky_xrpc_get(int $uid, string $url, array $parameters = []): ?stdCl
|
|||
$url .= '?' . http_build_query($parameters);
|
||||
}
|
||||
|
||||
return bluesky_get($uid, '/xrpc/' . $url, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . bluesky_get_token($uid)]]]);
|
||||
return bluesky_get(bluesky_get_user_pds($uid) . '/xrpc/' . $url, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . bluesky_get_token($uid)]]]);
|
||||
}
|
||||
|
||||
function bluesky_get(int $uid, string $url, string $accept_content = HttpClientAccept::DEFAULT, array $opts = []): ?stdClass
|
||||
function bluesky_get(string $url, string $accept_content = HttpClientAccept::DEFAULT, array $opts = []): ?stdClass
|
||||
{
|
||||
try {
|
||||
$curlResult = DI::httpClient()->get(DI::pConfig()->get($uid, 'bluesky', 'host') . $url, $accept_content, $opts);
|
||||
$curlResult = DI::httpClient()->get($url, $accept_content, $opts);
|
||||
} catch (\Exception $e) {
|
||||
Logger::notice('Exception on get', ['exception' => $e]);
|
||||
return null;
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-06-05 04:34+0000\n"
|
||||
"POT-Creation-Date: 2023-11-19 18:51+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -17,70 +17,74 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: bluesky.php:314
|
||||
#: bluesky.php:325
|
||||
msgid ""
|
||||
"You are authenticated to Bluesky. For security reasons the password isn't "
|
||||
"stored."
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:314
|
||||
#: bluesky.php:325
|
||||
msgid "You are not authenticated. Please enter the app password."
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:318
|
||||
#: bluesky.php:329
|
||||
msgid "Enable Bluesky Post Addon"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:319
|
||||
#: bluesky.php:330
|
||||
msgid "Post to Bluesky by default"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:320
|
||||
#: bluesky.php:331
|
||||
msgid "Import the remote timeline"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:321
|
||||
#: bluesky.php:332
|
||||
msgid "Import the pinned feeds"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:321
|
||||
#: bluesky.php:332
|
||||
msgid ""
|
||||
"When activated, Posts will be imported from all the feeds that you pinned in "
|
||||
"Bluesky."
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:322
|
||||
msgid "Bluesky host"
|
||||
#: bluesky.php:333
|
||||
msgid "Personal Data Server"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:323
|
||||
#: bluesky.php:333
|
||||
msgid "The personal data server (PDS) is the system that hosts your profile."
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:334
|
||||
msgid "Bluesky handle"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:324
|
||||
#: bluesky.php:335
|
||||
msgid "Bluesky DID"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:324
|
||||
#: bluesky.php:335
|
||||
msgid ""
|
||||
"This is the unique identifier. It will be fetched automatically, when the "
|
||||
"handle is entered."
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:325
|
||||
#: bluesky.php:336
|
||||
msgid "Bluesky app password"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:325
|
||||
#: bluesky.php:336
|
||||
msgid ""
|
||||
"Please don't add your real password here, but instead create a specific app "
|
||||
"password in the Bluesky settings."
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:331
|
||||
#: bluesky.php:342
|
||||
msgid "Bluesky Import/Export"
|
||||
msgstr ""
|
||||
|
||||
#: bluesky.php:382
|
||||
#: bluesky.php:395
|
||||
msgid "Post to Bluesky"
|
||||
msgstr ""
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{{include file="field_checkbox.tpl" field=$bydefault}}
|
||||
{{include file="field_checkbox.tpl" field=$import}}
|
||||
{{include file="field_checkbox.tpl" field=$import_feeds}}
|
||||
{{include file="field_input.tpl" field=$host}}
|
||||
{{include file="field_input.tpl" field=$pds}}
|
||||
{{include file="field_input.tpl" field=$handle}}
|
||||
{{include file="field_input.tpl" field=$did}}
|
||||
{{include file="field_input.tpl" field=$password}}
|
Loading…
Reference in New Issue