Supported mobile apps:
-- [Tusky](https://tusky.app)
-- [Husky](https://husky.fwgs.ru)
-- [twitlatte](https://github.com/moko256/twitlatte)
- [AndStatus](http://andstatus.org)
-- [Twidere](https://github.com/TwidereProject/)
+- [Husky](https://husky.fwgs.ru)
- [Subway Tooter](https://github.com/tateisu/SubwayTooter)
+- [Tusky](https://tusky.app)
+- [Twidere](https://github.com/TwidereProject/)
+- [twitlatte](https://github.com/moko256/twitlatte)
+- [Yuito](https://github.com/accelforce/Yuito)
Unsupported mobile apps:
-- [Mammut](https://github.com/jamiesanson/Mammut) There are problems with the token request, see issue https://github.com/jamiesanson/Mammut/issues/19
- [Fedilab](https://framagit.org/tom79/fedilab) Automatically uses the legacy API, see issue: https://framagit.org/tom79/fedilab/-/issues/520
+- [Mammut](https://github.com/jamiesanson/Mammut) There are problems with the token request, see issue https://github.com/jamiesanson/Mammut/issues/19
## Entities
{
parent::__construct($logger);
- $this->baseUrl = $baseURL;
+ $this->baseUrl = $baseURL;
$this->profileField = $profileField;
- $this->mstdnField = $mstdnField;
+ $this->mstdnField = $mstdnField;
}
/**
$cdata = Contact::getPublicAndUserContacID($contactId, $uid);
if (!empty($cdata)) {
$publicContact = Contact::getById($cdata['public']);
- $userContact = Contact::getById($cdata['user']);
+ $userContact = Contact::getById($cdata['user']);
} else {
$publicContact = Contact::getById($contactId);
$userContact = [];
return $this->createFromContact(Contact::getById($userContactId));
}
+ /**
+ * @param int $publicContactId Contact row id with uid = 0
+ * @param int $uid User ID
+ * @return RelationshipEntity
+ * @throws \Exception
+ */
+ public function createFromPublicContactId(int $publicContactId, int $uid)
+ {
+ $cdata = Contact::getPublicAndUserContacID($publicContactId, $uid);
+ if (!empty($cdata)) {
+ $cid = $cdata['user'];
+ } else {
+ $cid = $publicContactId;
+ }
+
+ return $this->createFromContact(Contact::getById($cid));
+ }
+
/**
* @param array $userContact Full contact row record with uid != 0
* @return RelationshipEntity
*/
public function createFromContact(array $userContact)
{
- return new RelationshipEntity($userContact['id'], $userContact);
+ return new RelationshipEntity($userContact['id'], $userContact,
+ Contact\User::isBlocked($userContact['id'], $userContact['uid']),
+ Contact\User::isIgnored($userContact['id'], $userContact['uid']));
}
/**
return $contact;
}
+ /**
+ * Follow a contact
+ *
+ * @param int $cid Public contact id
+ * @param int $uid User ID
+ *
+ * @return bool "true" if following had been successful
+ */
+ public static function follow(int $cid, int $uid)
+ {
+ $user = User::getById($uid);
+ if (empty($user)) {
+ return false;
+ }
+
+ $contact = self::getById($cid, ['url']);
+
+ $result = self::createFromProbe($user, $contact['url'], false);
+
+ return $result['cid'];
+ }
+
+ /**
+ * Unfollow a contact
+ *
+ * @param int $cid Public contact id
+ * @param int $uid User ID
+ *
+ * @return bool "true" if unfollowing had been successful
+ */
+ public static function unfollow(int $cid, int $uid)
+ {
+ $cdata = self::getPublicAndUserContacID($cid, $uid);
+ if (empty($cdata['user'])) {
+ return false;
+ }
+
+ $contact = self::getById($cdata['user']);
+
+ self::removeSharer([], $contact);
+
+ return true;
+ }
+
/**
* @param array $importer Owner (local user) data
* @param array $contact Existing owner-specific contact data we want to expand the relationship with. Optional.
return null;
}
- public static function removeFollower($importer, $contact, array $datarray = [], $item = "")
+ public static function removeFollower($importer, $contact)
{
if (($contact['rel'] == self::FRIEND) || ($contact['rel'] == self::SHARING)) {
DBA::update('contact', ['rel' => self::SHARING], ['id' => $contact['id']]);
}
}
- public static function removeSharer($importer, $contact, array $datarray = [], $item = "")
+ public static function removeSharer($importer, $contact)
{
if (($contact['rel'] == self::FRIEND) || ($contact['rel'] == self::FOLLOWER)) {
DBA::update('contact', ['rel' => self::FOLLOWER], ['id' => $contact['id']]);
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Api\Mastodon\Accounts;
+
+use Friendica\Core\System;
+use Friendica\DI;
+use Friendica\Model\Contact;
+use Friendica\Module\BaseApi;
+
+/**
+ * @see https://docs.joinmastodon.org/methods/accounts/
+ */
+class Block extends BaseApi
+{
+ public static function post(array $parameters = [])
+ {
+ self::login();
+ $uid = self::getCurrentUserID();
+
+ if (empty($parameters['id'])) {
+ DI::mstdnError()->UnprocessableEntity();
+ }
+
+ Contact\User::setBlocked($parameters['id'], $uid, true);
+
+ System::jsonExit(DI::mstdnRelationship()->createFromPublicContactId($parameters['id'], $uid)->toArray());
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Api\Mastodon\Accounts;
+
+use Friendica\Core\System;
+use Friendica\DI;
+use Friendica\Model\Contact;
+use Friendica\Module\BaseApi;
+
+/**
+ * @see https://docs.joinmastodon.org/methods/accounts/
+ */
+class Follow extends BaseApi
+{
+ public static function post(array $parameters = [])
+ {
+ self::login();
+ $uid = self::getCurrentUserID();
+
+ if (empty($parameters['id'])) {
+ DI::mstdnError()->UnprocessableEntity();
+ }
+
+ $cid = Contact::follow($parameters['id'], self::getCurrentUserID());
+
+ System::jsonExit(DI::mstdnRelationship()->createFromContactId($cid)->toArray());
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Api\Mastodon\Accounts;
+
+use Friendica\Core\System;
+use Friendica\DI;
+use Friendica\Model\Contact;
+use Friendica\Module\BaseApi;
+
+/**
+ * @see https://docs.joinmastodon.org/methods/accounts/
+ */
+class Mute extends BaseApi
+{
+ public static function post(array $parameters = [])
+ {
+ self::login();
+ $uid = self::getCurrentUserID();
+
+ if (empty($parameters['id'])) {
+ DI::mstdnError()->UnprocessableEntity();
+ }
+
+ Contact\User::setIgnored($parameters['id'], $uid, true);
+
+ System::jsonExit(DI::mstdnRelationship()->createFromPublicContactId($parameters['id'], $uid)->toArray());
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Api\Mastodon\Accounts;
+
+use Friendica\Core\System;
+use Friendica\DI;
+use Friendica\Model\Contact;
+use Friendica\Module\BaseApi;
+
+/**
+ * @see https://docs.joinmastodon.org/methods/accounts/
+ */
+class Unblock extends BaseApi
+{
+ public static function post(array $parameters = [])
+ {
+ self::login();
+ $uid = self::getCurrentUserID();
+
+ if (empty($parameters['id'])) {
+ DI::mstdnError()->UnprocessableEntity();
+ }
+
+ Contact\User::setBlocked($parameters['id'], $uid, false);
+
+ System::jsonExit(DI::mstdnRelationship()->createFromPublicContactId($parameters['id'], $uid)->toArray());
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Api\Mastodon\Accounts;
+
+use Friendica\Core\System;
+use Friendica\DI;
+use Friendica\Model\Contact;
+use Friendica\Module\BaseApi;
+
+/**
+ * @see https://docs.joinmastodon.org/methods/accounts/
+ */
+class Unfollow extends BaseApi
+{
+ public static function post(array $parameters = [])
+ {
+ self::login();
+ $uid = self::getCurrentUserID();
+
+ if (empty($parameters['id'])) {
+ DI::mstdnError()->UnprocessableEntity();
+ }
+
+ Contact::unfollow($parameters['id'], $uid);
+
+ System::jsonExit(DI::mstdnRelationship()->createFromPublicContactId($parameters['id'], $uid)->toArray());
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Api\Mastodon\Accounts;
+
+use Friendica\Core\System;
+use Friendica\DI;
+use Friendica\Model\Contact;
+use Friendica\Module\BaseApi;
+
+/**
+ * @see https://docs.joinmastodon.org/methods/accounts/
+ */
+class Unmute extends BaseApi
+{
+ public static function post(array $parameters = [])
+ {
+ self::login();
+ $uid = self::getCurrentUserID();
+
+ if (empty($parameters['id'])) {
+ DI::mstdnError()->UnprocessableEntity();
+ }
+
+ Contact\User::setIgnored($parameters['id'], $uid, false);
+
+ System::jsonExit(DI::mstdnRelationship()->createFromPublicContactId($parameters['id'], $uid)->toArray());
+ }
+}
$params = ['order' => ['thr-parent-id' => true], 'limit' => $limit];
- $condition = ['gravity' => GRAVITY_ACTIVITY, 'verb' => Activity::LIKE, 'uid' => $uid];
+ $condition = ['gravity' => GRAVITY_ACTIVITY, 'origin' => true, 'verb' => Activity::LIKE, 'uid' => $uid];
if (!empty($max_id)) {
$condition = DBA::mergeConditions($condition, ["`thr-parent-id` < ?", $max_id]);
/** @var bool */
protected $following = false;
/** @var bool */
- protected $followed_by = false;
+ protected $requested = false;
+ /**
+ * Unsupported
+ * @var bool
+ */
+ protected $endorsed = false;
/** @var bool */
- protected $blocking = false;
+ protected $followed_by = false;
/** @var bool */
protected $muting = false;
/** @var bool */
protected $muting_notifications = false;
+ /**
+ * Unsupported
+ * @var bool
+ */
+ protected $showing_reblogs = true;
/** @var bool */
- protected $requested = false;
+ protected $notifying = false;
+ /** @var bool */
+ protected $blocking = false;
/** @var bool */
protected $domain_blocking = false;
/**
* Unsupported
* @var bool
*/
- protected $showing_reblogs = true;
+ protected $blocked_by = false;
/**
* Unsupported
- * @var bool
+ * @var string
*/
- protected $endorsed = false;
+ protected $note = '';
/**
* @param int $userContactId Contact row Id with uid != 0
* @param array $userContact Full Contact table record with uid != 0
+ * @param bool $blocked "true" if user is blocked
+ * @param bool $muted "true" if user is muted
*/
- public function __construct(int $userContactId, array $userContact = [])
+ public function __construct(int $userContactId, array $userContact = [], bool $blocked = false, bool $muted = false)
{
$this->id = $userContactId;
$this->following = in_array($userContact['rel'] ?? 0, [Contact::SHARING, Contact::FRIEND]);
- $this->followed_by = in_array($userContact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND]);
- $this->blocking = (bool)$userContact['blocked'] ?? false;
- $this->muting = (bool)$userContact['readonly'] ?? false;
- $this->muting_notifications = (bool)$userContact['readonly'] ?? false;
$this->requested = (bool)$userContact['pending'] ?? false;
+ $this->endorsed = false;
+ $this->followed_by = in_array($userContact['rel'] ?? 0, [Contact::FOLLOWER, Contact::FRIEND]);
+ $this->muting = (bool)($userContact['readonly'] ?? false) || $muted;
+ $this->muting_notifications = $this->muting;
+ $this->showing_reblogs = true;
+ $this->notifying = (bool)$userContact['notify_new_posts'] ?? false;
+ $this->blocking = (bool)($userContact['blocked'] ?? false) || $blocked;
$this->domain_blocking = Network::isUrlBlocked($userContact['url'] ?? '');
+ $this->blocked_by = false;
+ $this->note = '';
return $this;
}
'/accounts/{id:\d+}/following' => [Module\Api\Mastodon\Accounts\Following::class, [R::GET ]],
'/accounts/{id:\d+}/lists' => [Module\Api\Mastodon\Accounts\Lists::class, [R::GET ]],
'/accounts/{id:\d+}/identity_proofs' => [Module\Api\Mastodon\Accounts\IdentityProofs::class, [R::GET ]], // Dummy, not supported
- '/accounts/{id:\d+}/follow' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // @todo
- '/accounts/{id:\d+}/unfollow' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // @todo
- '/accounts/{id:\d+}/block' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // @todo
- '/accounts/{id:\d+}/unblock' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // @todo
- '/accounts/{id:\d+}/mute' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // @todo
- '/accounts/{id:\d+}/unmute' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // @todo
+ '/accounts/{id:\d+}/follow' => [Module\Api\Mastodon\Accounts\Follow::class, [ R::POST]],
+ '/accounts/{id:\d+}/unfollow' => [Module\Api\Mastodon\Accounts\Unfollow::class, [ R::POST]],
+ '/accounts/{id:\d+}/block' => [Module\Api\Mastodon\Accounts\Block::class, [ R::POST]],
+ '/accounts/{id:\d+}/unblock' => [Module\Api\Mastodon\Accounts\Unblock::class, [ R::POST]],
+ '/accounts/{id:\d+}/mute' => [Module\Api\Mastodon\Accounts\Mute::class, [ R::POST]],
+ '/accounts/{id:\d+}/unmute' => [Module\Api\Mastodon\Accounts\Unmute::class, [ R::POST]],
'/accounts/{id:\d+}/pin' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported
'/accounts/{id:\d+}/unpin' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported
'/accounts/{id:\d+}/note' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // @todo
'/apps/verify_credentials' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo
'/blocks' => [Module\Api\Mastodon\Blocks::class, [R::GET ]],
'/bookmarks' => [Module\Api\Mastodon\Bookmarks::class, [R::GET ]],
- '/conversations' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented
- '/conversations/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], // not implemented
- '/conversations/{id:\d+}/read' => [Module\Api\Mastodon\Unimplemented::class, [R::POST ]], // not implemented
+ '/conversations' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo
+ '/conversations/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], // @todo
+ '/conversations/{id:\d+}/read' => [Module\Api\Mastodon\Unimplemented::class, [R::POST ]], // @todo
'/custom_emojis' => [Module\Api\Mastodon\CustomEmojis::class, [R::GET ]],
'/domain_blocks' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST, R::DELETE]], // not supported
'/directory' => [Module\Api\Mastodon\Directory::class, [R::GET ]],
'/statuses/{id:\d+}/unpin' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // @todo
'/suggestions' => [Module\Api\Mastodon\Suggestions::class, [R::GET ]],
'/suggestions/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], // not implemented
+ '/timelines/direct' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo
'/timelines/home' => [Module\Api\Mastodon\Timelines\Home::class, [R::GET ]],
'/timelines/list/{id:\d+}' => [Module\Api\Mastodon\Timelines\ListTimeline::class, [R::GET ]],
'/timelines/public' => [Module\Api\Mastodon\Timelines\PublicTimeline::class, [R::GET ]],