Add New notice and reblog delivery events, various bug fixes and improvements
This commit is contained in:
parent
a5b56b3089
commit
b693dab832
@ -52,7 +52,7 @@ class ActivityPubPlugin extends Plugin
|
||||
['action' => 'showstream'],
|
||||
['nickname' => Nickname::DISPLAY_FMT],
|
||||
'apActorProfile');
|
||||
|
||||
|
||||
$m->connect (':nickname/liked.json',
|
||||
['action' => 'apActorLikedCollection'],
|
||||
['nickname' => Nickname::DISPLAY_FMT]);
|
||||
@ -101,7 +101,7 @@ class ActivityPubPlugin extends Plugin
|
||||
$schema->ensureTable ('Activitypub_profile', Activitypub_profile::schemaDef());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* Delivery Events *
|
||||
********************************************************/
|
||||
@ -176,13 +176,41 @@ class ActivityPubPlugin extends Plugin
|
||||
}
|
||||
|
||||
$other = array ();
|
||||
foreach ($notice->getAttentionProfileIDs () as $to_id) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile($notice->getProfile ());
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
foreach ($notice->getAttentionProfiles() as $to_profile) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile (Profile::getById ($to_id));
|
||||
$other[] = Activitypub_profile::from_profile ($to_profile);
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
}
|
||||
if ($notice->reply_to) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($notice->getParent ()->getProfile ());
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
try {
|
||||
$mentions = $notice->getParent ()->getAttentionProfiles ();
|
||||
foreach ($mentions as $to_profile) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($to_profile);
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
|
||||
$postman = new Activitypub_postman ($profile, $other);
|
||||
|
||||
$postman->like ($notice);
|
||||
@ -206,13 +234,41 @@ class ActivityPubPlugin extends Plugin
|
||||
}
|
||||
|
||||
$other = array ();
|
||||
foreach ($notice->getAttentionProfileIDs () as $to_id) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile($notice->getProfile ());
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
foreach ($notice->getAttentionProfiles() as $to_profile) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile (Profile::getById ($to_id));
|
||||
$other[] = Activitypub_profile::from_profile ($to_profile);
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
}
|
||||
if ($notice->reply_to) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($notice->getParent ()->getProfile ());
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
try {
|
||||
$mentions = $notice->getParent ()->getAttentionProfiles ();
|
||||
foreach ($mentions as $to_profile) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($to_profile);
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
|
||||
$postman = new Activitypub_postman ($profile, $other);
|
||||
|
||||
$postman->undo_like ($notice);
|
||||
@ -236,17 +292,123 @@ class ActivityPubPlugin extends Plugin
|
||||
}
|
||||
|
||||
$other = array ();
|
||||
foreach ($notice->getAttentionProfileIDs () as $to_id) {
|
||||
|
||||
foreach ($notice->getAttentionProfiles() as $to_profile) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile (Profile::getById ($to_id));
|
||||
$other[] = Activitypub_profile::from_profile ($to_profile);
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
}
|
||||
if ($notice->reply_to) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($notice->getParent ()->getProfile ());
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
try {
|
||||
$mentions = $notice->getParent ()->getAttentionProfiles ();
|
||||
foreach ($mentions as $to_profile) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($to_profile);
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
|
||||
$postman = new Activitypub_postman ($profile, $other);
|
||||
$postman->delete ($notice);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert notifications for replies, mentions and repeats
|
||||
*
|
||||
* @return boolean hook flag
|
||||
*/
|
||||
function onStartNoticeDistribute ($notice)
|
||||
{
|
||||
assert ($notice->id > 0); // Ignore if not a valid notice
|
||||
|
||||
$profile = Profile::getKV ($notice->profile_id);
|
||||
|
||||
$other = array ();
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile($notice->getProfile ());
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
foreach ($notice->getAttentionProfiles() as $to_profile) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($to_profile);
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
}
|
||||
|
||||
// Is Announce
|
||||
if ($notice->isRepeat ()) {
|
||||
$repeated_notice = Notice::getKV ('id', $notice->repeat_of);
|
||||
if ($repeated_notice instanceof Notice) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($repeated_notice->getProfile ());
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
|
||||
$postman = new Activitypub_postman ($profile, $other);
|
||||
|
||||
// That was it
|
||||
$postman->announce ($repeated_notice);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore for activity/non-post-verb notices
|
||||
if (method_exists ('ActivityUtils', 'compareVerbs')) {
|
||||
$is_post_verb = ActivityUtils::compareVerbs ($notice->verb,
|
||||
array (ActivityVerb::POST));
|
||||
} else {
|
||||
$is_post_verb = ($notice->verb == ActivityVerb::POST ? true : false);
|
||||
}
|
||||
if ($notice->source == 'activity' || !$is_post_verb) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create
|
||||
if ($notice->reply_to) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($notice->getParent ()->getProfile ());
|
||||
} catch (Exception $e) {
|
||||
// Local user can be ignored
|
||||
}
|
||||
try {
|
||||
$mentions = $notice->getParent ()->getAttentionProfiles ();
|
||||
foreach ($mentions as $to_profile) {
|
||||
try {
|
||||
$other[] = Activitypub_profile::from_profile ($to_profile);
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
$postman = new Activitypub_postman ($profile, $other);
|
||||
|
||||
$postman->delete ($notice);
|
||||
|
||||
// That was it
|
||||
$postman->create ($notice);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -58,13 +58,13 @@ class apSharedInboxAction extends ManagedAction
|
||||
$data = json_decode (file_get_contents ('php://input'));
|
||||
|
||||
// Validate data
|
||||
if (!isset($data->type)) {
|
||||
if (!isset ($data->type)) {
|
||||
ActivityPubReturn::error ("Type was not specified.");
|
||||
}
|
||||
if (!isset($data->actor)) {
|
||||
if (!isset ($data->actor)) {
|
||||
ActivityPubReturn::error ("Actor was not specified.");
|
||||
}
|
||||
if (!isset($data->object)) {
|
||||
if (!isset ($data->object)) {
|
||||
ActivityPubReturn::error ("Object was not specified.");
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ class apSharedInboxAction extends ManagedAction
|
||||
if (!isset($data->to)) {
|
||||
ActivityPubReturn::error ("To was not specified.");
|
||||
}
|
||||
$discovery = new Activitypub_Discovery;
|
||||
$discovery = new Activitypub_explorer;
|
||||
$to_profiles = array ();
|
||||
// Generate To objects
|
||||
if (is_array ($data->to)) {
|
||||
@ -128,6 +128,9 @@ class apSharedInboxAction extends ManagedAction
|
||||
case "Undo":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Undo.php";
|
||||
break;
|
||||
case "Delete":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Delete.php";
|
||||
break;
|
||||
default:
|
||||
ActivityPubReturn::error ("Invalid type value.");
|
||||
}
|
||||
|
@ -38,6 +38,9 @@ if (!(isset ($data->object->type) && in_array ($data->object->type, $valid_objec
|
||||
if (!isset ($data->object->content)) {
|
||||
ActivityPubReturn::error ("Object content was not specified.");
|
||||
}
|
||||
if (!isset ($data->object->url)) {
|
||||
ActivityPubReturn::error ("Object url was not specified.");
|
||||
}
|
||||
|
||||
$content = $data->object->content;
|
||||
|
||||
@ -50,9 +53,13 @@ $act->context = new ActivityContext ();
|
||||
|
||||
// Is this a reply?
|
||||
if (isset ($data->object->reply_to)) {
|
||||
$reply_to = Notice::getByUri ($data->object->reply_to);
|
||||
$act->context->replyToID = $reply_to->getUri ();
|
||||
$act->context->replyToUrl = $data->object->reply_to;
|
||||
try {
|
||||
$reply_to = Notice::getByUri ($data->object->reply_to);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Object reply_to value.");
|
||||
}
|
||||
$act->context->replyToID = $reply_to->getUri ();
|
||||
$act->context->replyToUrl = $reply_to->getUrl ();
|
||||
} else {
|
||||
$reply_to = null;
|
||||
}
|
||||
@ -70,7 +77,7 @@ if (Notice::contentTooLong ($content)) {
|
||||
ActivityPubReturn::error ("That's too long. Maximum notice size is %d character.");
|
||||
}
|
||||
|
||||
$options = array ('source' => 'ActivityPub', 'uri' => $data->id);
|
||||
$options = array ('source' => 'ActivityPub', 'uri' => $data->id, 'url' => $data->object->url);
|
||||
// $options gets filled with possible scoping settings
|
||||
ToSelector::fillActivity ($this, $act, $options);
|
||||
|
||||
@ -84,6 +91,7 @@ $act->objects[] = $actobj;
|
||||
try {
|
||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => $data->id,
|
||||
"url" => $data->object->url,
|
||||
"type" => "Create",
|
||||
"actor" => $data->actor,
|
||||
"object" => Activitypub_notice::notice_to_array (Notice::saveActivity ($act, $actor_profile, $options)));
|
||||
|
@ -30,7 +30,7 @@ if (!defined ('GNUSOCIAL')) {
|
||||
}
|
||||
|
||||
try {
|
||||
Activitypub_notice::getKV ("url", $data->object)->deleteAs ($actor_profile);
|
||||
Notice::getByUri ($data->object)->deleteAs ($actor_profile);
|
||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Delete",
|
||||
"actor" => $data->actor,
|
||||
|
@ -30,7 +30,7 @@ if (!defined ('GNUSOCIAL')) {
|
||||
}
|
||||
|
||||
try {
|
||||
Fave::addNew ($actor_profile, Notice::getKV ("url", $data->object));
|
||||
Fave::addNew ($actor_profile, Notice::getByUri ($data->object));
|
||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Like",
|
||||
"actor" => $data->actor,
|
||||
|
@ -41,8 +41,7 @@ case "Like":
|
||||
if (!isset ($data->object->object)) {
|
||||
ActivityPubReturn::error ("Object Notice URL was not specified.");
|
||||
}
|
||||
|
||||
Fave::removeEntry ($actor_profile, Notice::getKV ("url", $data->object->object));
|
||||
Fave::removeEntry ($actor_profile, Notice::getByUri ($data->object->object));
|
||||
ActivityPubReturn::answer ("Notice disfavorited successfully.");
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ($e->getMessage (), 403);
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
require_once dirname (__DIR__) . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "explorer.php";
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
@ -178,7 +179,13 @@ class Activitypub_profile extends Profile
|
||||
|
||||
$aprofile = Activitypub_profile::getKV ('profile_id', $profile_id);
|
||||
if (!$aprofile instanceof Activitypub_profile) {
|
||||
throw new Exception ('No Activitypub_profile for Profile ID: '.$profile_id);
|
||||
// No Activitypub_profile for this profile_id,
|
||||
if (!$profile->isLocal ()) {
|
||||
// create one!
|
||||
$aprofile = self::create_from_local_profile ($profile);
|
||||
} else {
|
||||
throw new Exception ('No Activitypub_profile for Profile ID: '.$profile_id. ', this probably is a local profile.');
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($profile as $key => $value) {
|
||||
@ -188,6 +195,35 @@ class Activitypub_profile extends Profile
|
||||
return $aprofile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an existent local profile creates an ActivityPub profile.
|
||||
* One must be careful not to give a user profile to this function
|
||||
* as only remote users have ActivityPub_profiles on local instance
|
||||
*
|
||||
* @param Profile $profile
|
||||
* @return Activitypub_profile
|
||||
*/
|
||||
private static function create_from_local_profile (Profile $profile)
|
||||
{
|
||||
$url = $profile->getURL ();
|
||||
$inboxes = Activitypub_explorer::get_actor_inboxes_uri ($url);
|
||||
|
||||
$aprofile->created = $aprofile->modified = common_sql_now ();
|
||||
|
||||
$aprofile = new Activitypub_profile;
|
||||
$aprofile->profile_id = $profile->getID ();
|
||||
$aprofile->uri = $url;
|
||||
$aprofile->nickname = $profile->getNickname ();
|
||||
$aprofile->fullname = $profile->getFullname ();
|
||||
$aprofile->bio = substr ($profile->getDescription (), 0, 1000);
|
||||
$aprofile->inboxuri = $inboxes["inbox"];
|
||||
$aprofile->sharedInboxuri = $inboxes["sharedInbox"];
|
||||
|
||||
$aprofile->insert ();
|
||||
|
||||
return $aprofile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sharedInbox if possible, inbox otherwise
|
||||
*
|
||||
|
@ -141,7 +141,7 @@ class Activitypub_explorer
|
||||
$this->_lookup ($res["next"]);
|
||||
}
|
||||
return true;
|
||||
} else if ($this->validate_remote_response ($res)) {
|
||||
} else if (self::validate_remote_response ($res)) {
|
||||
$this->discovered_actor_profiles[]= $this->store_profile ($res);
|
||||
return true;
|
||||
}
|
||||
@ -163,7 +163,7 @@ class Activitypub_explorer
|
||||
$aprofile->fullname = $res["display_name"];
|
||||
$aprofile->bio = substr ($res["summary"], 0, 1000);
|
||||
$aprofile->inboxuri = $res["inbox"];
|
||||
$aprofile->sharedInboxuri = $res["sharedInbox"];
|
||||
$aprofile->sharedInboxuri = isset ($res["sharedInbox"]) ? $res["sharedInbox"] : $res["inbox"];
|
||||
|
||||
$aprofile->do_insert ();
|
||||
|
||||
@ -177,9 +177,9 @@ class Activitypub_explorer
|
||||
* @param array $res remote response
|
||||
* @return boolean success state
|
||||
*/
|
||||
private function validate_remote_response ($res)
|
||||
private static function validate_remote_response ($res)
|
||||
{
|
||||
if (!isset ($res["url"], $res["nickname"], $res["display_name"], $res["summary"], $res["inbox"], $res["sharedInbox"])) {
|
||||
if (!isset ($res["url"], $res["nickname"], $res["display_name"], $res["summary"], $res["inbox"])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -210,4 +210,29 @@ class Activitypub_explorer
|
||||
}
|
||||
return $i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a valid actor profile url returns its inboxes
|
||||
*
|
||||
* @param string $url of Actor profile
|
||||
* @return boolean|array false if fails | array with inbox and shared inbox if successful
|
||||
*/
|
||||
static function get_actor_inboxes_uri ($url)
|
||||
{
|
||||
$client = new HTTPClient ();
|
||||
$headers = array();
|
||||
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||
$response = $client->get ($url, $headers);
|
||||
if (!$response->isOk ()) {
|
||||
throw new Exception ("Invalid Actor URL.");
|
||||
}
|
||||
$res = json_decode ($response->getBody (), JSON_UNESCAPED_SLASHES);
|
||||
if (self::validate_remote_response ($res)) {
|
||||
return array ("inbox" => $res["inbox"],
|
||||
"sharedInbox" => isset ($res["sharedInbox"]) ? $res["sharedInbox"] : $res["inbox"]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ class Activitypub_postman
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Like",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"object" => $notice->getUrl ());
|
||||
"object" => $notice->getUri ());
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
$this->client->post ($inbox, $this->headers);
|
||||
@ -123,7 +123,7 @@ class Activitypub_postman
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"object" => array (
|
||||
"type" => "Like",
|
||||
"object" => $notice->getUrl ()
|
||||
"object" => $notice->getUri ()
|
||||
)
|
||||
);
|
||||
$this->client->setBody (json_encode ($data));
|
||||
@ -132,6 +132,54 @@ class Activitypub_postman
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Announce notification to remote instances
|
||||
*
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function announce ($notice)
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => $notice->getUri (),
|
||||
"url" => $notice->getUrl (),
|
||||
"type" => "Announce",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||
"object" => $notice->getUri ()
|
||||
);
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
$this->client->post ($inbox, $this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Create notification to remote instances
|
||||
*
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function create ($notice)
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => $notice->getUri (),
|
||||
"type" => "Create",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||
"object" => array (
|
||||
"type" => "Note",
|
||||
"url" => $notice->getUrl (),
|
||||
"content" => $notice->getContent ()
|
||||
)
|
||||
);
|
||||
if (isset ($notice->reply_to)) {
|
||||
$data["object"]["reply_to"] = $notice->getParent ()->getUri ();
|
||||
}
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
$this->client->post ($inbox, $this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Delete notification to remote instances holding the notice
|
||||
*
|
||||
@ -142,7 +190,7 @@ class Activitypub_postman
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Delete",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"object" => $notice->getUrl ()
|
||||
"object" => $notice->getUri ()
|
||||
);
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
|
Reference in New Issue
Block a user