]> git.mxchange.org Git - friendica.git/commitdiff
API: Account actions
authorMichael <heluecht@pirati.ca>
Fri, 14 May 2021 21:51:32 +0000 (21:51 +0000)
committerMichael <heluecht@pirati.ca>
Fri, 14 May 2021 21:51:32 +0000 (21:51 +0000)
13 files changed:
doc/API-Mastodon.md
src/Factory/Api/Mastodon/Account.php
src/Factory/Api/Mastodon/Relationship.php
src/Model/Contact.php
src/Module/Api/Mastodon/Accounts/Block.php [new file with mode: 0644]
src/Module/Api/Mastodon/Accounts/Follow.php [new file with mode: 0644]
src/Module/Api/Mastodon/Accounts/Mute.php [new file with mode: 0644]
src/Module/Api/Mastodon/Accounts/Unblock.php [new file with mode: 0644]
src/Module/Api/Mastodon/Accounts/Unfollow.php [new file with mode: 0644]
src/Module/Api/Mastodon/Accounts/Unmute.php [new file with mode: 0644]
src/Module/Api/Mastodon/Favourited.php
src/Object/Api/Mastodon/Relationship.php
static/routes.config.php

index 4ec2ce00545079e5355f951257d68907ddd3bda3..0e1610365cc0ee6957a1ba4e705f8ee6af24d7d9 100644 (file)
@@ -13,17 +13,18 @@ Authentication is the same as described in [Using the APIs](help/api#Authenticat
 
 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
 
index 40f55c6c3b8cd9430fa678cebfdda6fc1a329be2..28335a0e8e270bd3b4922ffcb3dbde5f0e4643bc 100644 (file)
@@ -44,9 +44,9 @@ class Account extends BaseFactory
        {
                parent::__construct($logger);
 
-               $this->baseUrl = $baseURL;
+               $this->baseUrl      = $baseURL;
                $this->profileField = $profileField;
-               $this->mstdnField = $mstdnField;
+               $this->mstdnField   = $mstdnField;
        }
 
        /**
@@ -61,7 +61,7 @@ class Account extends BaseFactory
                $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 = [];
index 1b1cc0f33a8b3c02aeb44518e56aaf636964c566..a1d94a33235cedbd3d925bade36342a0824c6723 100644 (file)
@@ -37,13 +37,33 @@ class Relationship extends BaseFactory
                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']));
        }
 
        /**
index f75cffadacd08ed5e5a2edbdca4c03c6f69a1589..89325e0939fedcd860b0d8d21c27e2849386f590 100644 (file)
@@ -2409,6 +2409,50 @@ class Contact
                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.
@@ -2554,7 +2598,7 @@ class Contact
                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']]);
@@ -2563,7 +2607,7 @@ class Contact
                }
        }
 
-       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']]);
diff --git a/src/Module/Api/Mastodon/Accounts/Block.php b/src/Module/Api/Mastodon/Accounts/Block.php
new file mode 100644 (file)
index 0000000..5043452
--- /dev/null
@@ -0,0 +1,47 @@
+<?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());
+       }
+}
diff --git a/src/Module/Api/Mastodon/Accounts/Follow.php b/src/Module/Api/Mastodon/Accounts/Follow.php
new file mode 100644 (file)
index 0000000..921fac6
--- /dev/null
@@ -0,0 +1,47 @@
+<?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());
+       }
+}
diff --git a/src/Module/Api/Mastodon/Accounts/Mute.php b/src/Module/Api/Mastodon/Accounts/Mute.php
new file mode 100644 (file)
index 0000000..6dda625
--- /dev/null
@@ -0,0 +1,47 @@
+<?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());
+       }
+}
diff --git a/src/Module/Api/Mastodon/Accounts/Unblock.php b/src/Module/Api/Mastodon/Accounts/Unblock.php
new file mode 100644 (file)
index 0000000..5199745
--- /dev/null
@@ -0,0 +1,47 @@
+<?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());
+       }
+}
diff --git a/src/Module/Api/Mastodon/Accounts/Unfollow.php b/src/Module/Api/Mastodon/Accounts/Unfollow.php
new file mode 100644 (file)
index 0000000..ef681e6
--- /dev/null
@@ -0,0 +1,47 @@
+<?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());
+       }
+}
diff --git a/src/Module/Api/Mastodon/Accounts/Unmute.php b/src/Module/Api/Mastodon/Accounts/Unmute.php
new file mode 100644 (file)
index 0000000..c870da0
--- /dev/null
@@ -0,0 +1,47 @@
+<?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());
+       }
+}
index f46fb7b073cc80ca6802cd3e1f42d1c7217f26d5..53bd82aab6d1c6bfd15c0b34c9f854bcc2bde066 100644 (file)
@@ -52,7 +52,7 @@ class Favourited extends BaseApi
 
                $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]);
index 16a4a2d3084f800b1c73cbfe832f3f01cf7b8dd8..463eb97e04c5df2fa14e615d5743129af0dcaba4 100644 (file)
@@ -37,42 +37,61 @@ class Relationship extends BaseDataTransferObject
        /** @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;
        }
index 3a19ff37972bb93c669e7ac01de53ad09bbb68ce..7c33d56745773032e18d5847e788ad02f0b9f045 100644 (file)
@@ -64,12 +64,12 @@ return [
                        '/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
@@ -90,9 +90,9 @@ return [
                        '/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         ]],
@@ -142,6 +142,7 @@ return [
                        '/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         ]],