diff --git a/themes/hugo-mastodon-comments/.gitignore b/themes/hugo-mastodon-comments/.gitignore index 0ed663d..29e28fb 100644 --- a/themes/hugo-mastodon-comments/.gitignore +++ b/themes/hugo-mastodon-comments/.gitignore @@ -1 +1,2 @@ static/comments/config.php +static/comments/cache-*.json diff --git a/themes/hugo-mastodon-comments/static/comments/Mastodon_api.php b/themes/hugo-mastodon-comments/static/comments/Mastodon_api.php deleted file mode 100644 index b57e252..0000000 --- a/themes/hugo-mastodon-comments/static/comments/Mastodon_api.php +++ /dev/null @@ -1,975 +0,0 @@ - - * @copyright KwangSeon Yun - * @license https://raw.githubusercontent.com/yks118/Mastodon-api-php/master/LICENSE MIT License - * @link https://github.com/yks118/Mastodon-api-php - */ -class Mastodon_api { - private $mastodon_url = ''; - private $client_id = ''; - private $client_secret = ''; - - private $token = array(); - private $scopes = array(); - - public function __construct () {} - - public function __destruct () {} - - /** - * _post - * - * curl post - * - * @param string $url - * @param array $data - * - * @return array $response - */ - private function _post ($url,$data = array()) { - $parameters = array(); - $parameters[CURLOPT_POST] = 1; - - // set access_token - if (isset($this->token['access_token'])) { - $data['access_token'] = $this->token['access_token']; - } - - if (count($data)) { - $parameters[CURLOPT_POSTFIELDS] = preg_replace('/([(%5B)]{1})[0-9]+([(%5D)]{1})/','$1$2',http_build_query($data)); - } - - $url = $this->mastodon_url.$url; - $response = $this->get_content_curl($url,$parameters); - return $response; - } - - /** - * _get - * - * @param string $url - * @param array $data - * - * @return array $response - */ - private function _get ($url,$data = array()) { - $parameters = array(); - - // set authorization bearer - if (isset($this->token['access_token'])) { - $authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token']; - $parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization); - } - - $url = $this->mastodon_url.$url; - if (count($data)) { - $url .= '?' . http_build_query($data); - } - - $response = $this->get_content_curl($url,$parameters); - return $response; - } - - /** - * _patch - * - * @param string $url - * @param array $data - * - * @return array $parameters - */ - private function _patch ($url,$data = array()) { - $parameters = array(); - $parameters[CURLOPT_CUSTOMREQUEST] = 'PATCH'; - - // set authorization bearer - if (isset($this->token['access_token'])) { - $authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token']; - $parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization); - } - - if (count($data)) { - $parameters[CURLOPT_POSTFIELDS] = json_encode($data); - } - - $url = $this->mastodon_url.$url; - $response = $this->get_content_curl($url,$parameters); - return $response; - } - - /** - * _delete - * - * @param string $url - * - * @return array $response - */ - private function _delete ($url) { - $parameters = array(); - $parameters[CURLOPT_CUSTOMREQUEST] = 'DELETE'; - - // set authorization bearer - if (isset($this->token['access_token'])) { - $authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token']; - $parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization); - } - - $url = $this->mastodon_url.$url; - $response = $this->get_content_curl($url,$parameters); - return $response; - } - - /** - * get_content_curl - * - * @param string $url - * @param array $parameters - * - * @return array $data - */ - protected function get_content_curl ($url,$parameters = array()) { - $data = array(); - - // set CURLOPT_USERAGENT - if (!isset($parameters[CURLOPT_USERAGENT])) { - if (isset($_SERVER['HTTP_USER_AGENT'])) { - $parameters[CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT']; - } else { - // default IE11 - $parameters[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'; - } - } - - // check curl_init - if (function_exists('curl_init')) { - $ch = curl_init(); - - // url 설정 - curl_setopt($ch,CURLOPT_URL,$url); - - foreach ($parameters as $key => $value) { - curl_setopt($ch,$key,$value); - } - - // https - if (!isset($parameters[CURLOPT_SSL_VERIFYPEER])) { - curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); - } - if (!isset($parameters[CURLOPT_SSLVERSION])) { - curl_setopt($ch,CURLOPT_SSLVERSION,6); - } - - // no header - if (!isset($parameters[CURLOPT_HEADER])) { - curl_setopt($ch,CURLOPT_HEADER,0); - } - - // POST / GET (default : GET) - if (!isset($parameters[CURLOPT_POST]) && !isset($parameters[CURLOPT_CUSTOMREQUEST])) { - curl_setopt($ch,CURLOPT_POST,0); - } - - // response get php value - if (!isset($parameters[CURLOPT_RETURNTRANSFER])) { - curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); - } - - // HTTP2 - if (!isset($parameters[CURLOPT_HTTP_VERSION])) { - curl_setopt($ch,CURLOPT_HTTP_VERSION,3); - } - if (!isset($parameters[CURLINFO_HEADER_OUT])) { - curl_setopt($ch,CURLINFO_HEADER_OUT,TRUE); - } - - $data['html'] = json_decode(curl_exec($ch),true); - $data['response'] = curl_getinfo($ch); - - curl_close($ch); - } - - return $data; - } - - /** - * set_url - * - * @param string $path - */ - public function set_url ($path) { - $this->mastodon_url = $path; - } - - /** - * set_client - * - * @param string $id - * @param string $secret - */ - public function set_client ($id,$secret) { - $this->client_id = $id; - $this->client_secret = $secret; - } - - /** - * set_token - * - * @param string $token - * @param string $type - */ - public function set_token ($token,$type) { - $this->token['access_token'] = $token; - $this->token['token_type'] = $type; - } - - /** - * set_scopes - * - * @param array $scopes read / write / follow - */ - public function set_scopes ($scopes) { - $this->scopes = $scopes; - } - - /** - * create_app - * - * @param string $client_name - * @param array $scopes read / write / follow - * @param string $redirect_uris - * @param string $website - * - * @return array $response - * int $response['id'] - * string $response['redirect_uri'] - * string $response['client_id'] - * string $response['client_secret'] - */ - public function create_app ($client_name,$scopes = array(),$redirect_uris = '',$website = '') { - $parameters = array(); - - if (count($scopes) == 0) { - if (count($this->scopes) == 0) { - $scopes = array('read','write','follow'); - } else { - $scopes = $this->scopes; - } - } - - $parameters['client_name'] = $client_name; - $parameters['scopes'] = implode(' ',$scopes); - - if (empty($redirect_uris)) { - $parameters['redirect_uris'] = 'urn:ietf:wg:oauth:2.0:oob'; - } else { - $parameters['redirect_uris'] = $redirect_uris; - } - - if ($website) { - $parameters['website'] = $website; - } - - $response = $this->_post('/api/v2/apps',$parameters); - - if (isset($response['html']['client_id'])) { - $this->client_id = $response['html']['client_id']; - $this->client_secret = $response['html']['client_secret']; - } - - return $response; - } - - /** - * login - * - * @param string $id E-mail Address - * @param string $password Password - * - * @return array $response - * string $response['access_token'] - * string $response['token_type'] bearer - * string $response['scope'] read - * int $response['created_at'] time - */ - public function login ($id,$password) { - $parameters = array(); - $parameters['client_id'] = $this->client_id; - $parameters['client_secret'] = $this->client_secret; - $parameters['grant_type'] = 'password'; - $parameters['username'] = $id; - $parameters['password'] = $password; - - if (count($this->scopes) == 0) { - $parameters['scope'] = implode(' ',array('read','write','follow')); - } else { - $parameters['scope'] = implode(' ',$this->scopes); - } - - $response = $this->_post('/oauth/token',$parameters); - - if (isset($response['html']['access_token'])) { - $this->token['access_token'] = $response['html']['access_token']; - $this->token['token_type'] = $response['html']['token_type']; - } - - return $response; - } - - /** - * accounts - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - * int $response['id'] - * string $response['username'] - * string $response['acct'] - * string $response['display_name'] The name to display in the user's profile - * bool $response['locked'] - * string $response['created_at'] - * int $response['followers_count'] - * int $response['following_count'] - * int $response['statuses_count'] - * string $response['note'] A new biography for the user - * string $response['url'] - * string $response['avatar'] A base64 encoded image to display as the user's avatar - * string $response['avatar_static'] - * string $response['header'] A base64 encoded image to display as the user's header image - * string $response['header_static'] - */ - public function accounts ($id) { - $response = $this->_get('/api/v2/accounts/'.$id); - return $response; - } - - /** - * accounts_verify_credentials - * - * Getting the current user - * - * @return array $response - */ - public function accounts_verify_credentials () { - $response = $this->_get('/api/v2/accounts/verify_credentials'); - return $response; - } - - /** - * accounts_update_credentials - * - * Updating the current user - * - * @param array $parameters - * string $parameters['display_name'] The name to display in the user's profile - * string $parameters['note'] A new biography for the user - * string $parameters['avatar'] A base64 encoded image to display as the user's avatar - * string $parameters['header'] A base64 encoded image to display as the user's header image - * - * @return array $response - */ - public function accounts_update_credentials ($parameters) { - $response = $this->_patch('/api/v2/accounts/update_credentials',$parameters); - return $response; - } - - /** - * accounts_followers - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_followers ($id) { - $response = $this->_get('/api/v2/accounts/'.$id.'/followers'); - return $response; - } - - /** - * accounts_following - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_following ($id) { - $response = $this->_get('/api/v2/accounts/'.$id.'/following'); - return $response; - } - - /** - * accounts_statuses - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_statuses ($id) { - $response = $this->_get('/api/v2/accounts/'.$id.'/statuses'); - return $response; - } - - /** - * accounts_own_statuses, only own statuses - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_own_statuses ($id) { - $response = $this->_get('/api/v2/accounts/'.$id.'/statuses?exclude_replies=1'); - $result = []; - foreach ($response['html'] as $r) { - if(is_null($r['reblog'])) { - $result[] = $r; - } - } - - $response['html'] = $result; - - return $response; - } - - /** - * accounts_follow - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_follow ($id) { - $response = $this->_post('/api/v2/accounts/'.$id.'/follow'); - return $response; - } - - /** - * accounts_unfollow - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_unfollow ($id) { - $response = $this->_post('/api/v2/accounts/'.$id.'/unfollow'); - return $response; - } - - /** - * accounts_block - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_block ($id) { - $response = $this->_post('/api/v2/accounts/'.$id.'/block'); - return $response; - } - - /** - * accounts_unblock - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_unblock ($id) { - $response = $this->_post('/api/v2/accounts/'.$id.'/unblock'); - return $response; - } - - /** - * accounts_mute - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_mute ($id) { - $response = $this->_post('/api/v2/accounts/'.$id.'/mute'); - return $response; - } - - /** - * accounts_unmute - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function accounts_unmute ($id) { - $response = $this->_post('/api/v2/accounts/'.$id.'/unmute'); - return $response; - } - - /** - * accounts_relationships - * - * @see https://your-domain/web/accounts/:id - * - * @param array $parameters - * int $parameters['id'] - * - * @return array $response - * int $response['id'] - * bool $response['following'] - * bool $response['followed_by'] - * bool $response['blocking'] - * bool $response['muting'] - * bool $response['requested'] - */ - public function accounts_relationships ($parameters) { - $response = $this->_get('/api/v2/accounts/relationships',$parameters); - return $response; - } - - /** - * accounts_search - * - * @param array $parameters - * string $parameters['q'] - * int $parameters['limit'] default : 40 - * - * @return array $response - */ - public function accounts_search ($parameters) { - $response = $this->_get('/api/v2/accounts/search',$parameters); - return $response; - } - - /** - * blocks - * - * @return array $response - */ - public function blocks () { - $response = $this->_get('/api/v2/blocks'); - return $response; - } - - /** - * favourites - * - * @return array $response - */ - public function favourites () { - $response = $this->_get('/api/v2/favourites'); - return $response; - } - - /** - * follow_requests - * - * @return array $response - */ - public function follow_requests () { - $response = $this->_get('/api/v2/follow_requests'); - return $response; - } - - /** - * follow_requests_authorize - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * - * @return array $response - */ - public function follow_requests_authorize ($id) { - $response = $this->_post('/api/v2/follow_requests/authorize',array('id'=>$id)); - return $response; - } - - /** - * follow_requests_reject - * - * @see https://your-domain/web/accounts/:id - * - * @param int $id - * @return array $response - */ - public function follow_requests_reject ($id) { - $response = $this->_post('/api/v2/follow_requests/reject',array('id'=>$id)); - return $response; - } - - /** - * follows - * - * Following a remote user - * - * @param string $uri username@domain of the person you want to follow - * @return array $response - */ - public function follows ($uri) { - $response = $this->_post('/api/v2/follows',array('uri'=>$uri)); - return $response; - } - - /** - * instance - * - * Getting instance information - * - * @return array $response - * string $response['uri'] - * string $response['title'] - * string $response['description'] - * string $response['email'] - */ - public function instance () { - $response = $this->_get('/api/v2/instance'); - return $response; - } - - /** - * media - * - * Uploading a media attachment - * - * @param string $file_path local path / http path - * - * @return array $response - * int $response['id'] ID of the attachment - * string $response['type'] One of: "image", "video", "gifv" - * string $response['url'] URL of the locally hosted version of the image - * string $response['remote_url'] For remote images, the remote URL of the original image - * string $response['preview_url'] URL of the preview image - * string $response['text_url'] Shorter URL for the image, for insertion into text (only present on local images) - */ - public function media ($file_path) { - $url = $this->mastodon_url.'/api/v2/media'; - $parameters = $data = array(); - $parameters[CURLOPT_HTTPHEADER] = array('Content-Type'=>'multipart/form-data'); - $parameters[CURLOPT_POST] = true; - - // set access_token - if (isset($this->token['access_token'])) { - $parameters[CURLOPT_POSTFIELDS]['access_token'] = $this->token['access_token']; - } - - if (is_file($file_path)) { - $mime_type = mime_content_type($file_path); - - $cf = curl_file_create($file_path,$mime_type,'file'); - $parameters[CURLOPT_POSTFIELDS]['file'] = $cf; - } - - $response = $this->get_content_curl($url,$parameters); - return $response; - } - - /** - * mutes - * - * Fetching a user's mutes - * - * @return array $response - */ - public function mutes () { - $response = $this->_get('/api/v2/mutes'); - return $response; - } - - /** - * notifications - * - * @param int $id - * - * @return array $response - */ - public function notifications ($id = 0) { - $url = '/api/v2/notifications'; - - if ($id > 0) { - $url .= '/'.$id; - } - - $response = $this->_get($url); - return $response; - } - - /** - * notifications_clear - * - * Clearing notifications - * - * @return array $response - */ - public function notifications_clear () { - $response = $this->_post('/api/v2/notifications/clear'); - return $response; - } - - /** - * get_reports - * - * Fetching a user's reports - * - * @return array $response - */ - public function get_reports () { - $response = $this->_get('/api/v2/reports'); - return $response; - } - - /** - * post_reports - * - * Reporting a user - * - * @param array $parameters - * int $parameters['account_id'] The ID of the account to report - * int $parameters['status_ids'] The IDs of statuses to report (can be an array) - * string $parameters['comment'] A comment to associate with the report. - * - * @return array $response - */ - public function post_reports ($parameters) { - $response = $this->_post('/api/v2/reports',$parameters); - return $response; - } - - /** - * search - * - * Searching for content - * - * @param array $parameters - * string $parameters['q'] The search query - * string $parameters['resolve'] Whether to resolve non-local accounts - * - * @return array $response - */ - public function search ($parameters) { - $response = $this->_get('/api/v2/search',$parameters); - return $response; - } - - /** - * statuses - * - * Fetching a status - * - * @param int $id - * - * @return array $response - */ - public function statuses ($id) { - $response = $this->_get('/api/v2/statuses/'.$id); - return $response; - } - - /** - * statuses_context - * - * Getting status context - * - * @param int $id - * - * @return array $response - */ - public function statuses_context ($id) { - $response = $this->_get('/api/v2/statuses/'.$id.'/context'); - return $response; - } - - /** - * statuses_card - * - * Getting a card associated with a status - * - * @param int $id - * - * @return array $response - */ - public function statuses_card ($id) { - $response = $this->_get('/api/v2/statuses/'.$id.'/card'); - return $response; - } - - /** - * statuses_reblogged_by - * - * Getting who reblogged a status - * - * @param int $id - * - * @return array $response - */ - public function statuses_reblogged_by ($id) { - $response = $this->_get('/api/v2/statuses/'.$id.'/reblogged_by'); - return $response; - } - - /** - * statuses_favourited_by - * - * Getting who favourited a status - * - * @param int $id - * - * @return array $response - */ - public function statuses_favourited_by ($id) { - $response = $this->_get('/api/v2/statuses/'.$id.'/favourited_by'); - return $response; - } - - /** - * post_statuses - * - * @param array $parameters - * string $parameters['status'] The text of the status - * int $parameters['in_reply_to_id'] (optional): local ID of the status you want to reply to - * int $parameters['media_ids'] (optional): array of media IDs to attach to the status (maximum 4) - * string $parameters['sensitive'] (optional): set this to mark the media of the status as NSFW - * string $parameters['spoiler_text'] (optional): text to be shown as a warning before the actual content - * string $parameters['visibility'] (optional): either "direct", "private", "unlisted" or "public" - * - * @return array $response - */ - public function post_statuses ($parameters) { - $response = $this->_post('/api/v2/statuses',$parameters); - return $response; - } - - /** - * delete_statuses - * - * Deleting a status - * - * @param int $id - * - * @return array $response empty - */ - public function delete_statuses ($id) { - $response = $this->_delete('/api/v2/statuses/'.$id); - return $response; - } - - /** - * statuses_reblog - * - * Reblogging a status - * - * @param int $id - * - * @return array $response - */ - public function statuses_reblog ($id) { - $response = $this->_post('/api/v2/statuses/'.$id.'/reblog'); - return $response; - } - - /** - * statuses_unreblog - * - * Unreblogging a status - * - * @param int $id - * - * @return array $response - */ - public function statuses_unreblog ($id) { - $response = $this->_post('/api/v2/statuses/'.$id.'/unreblog'); - return $response; - } - - /** - * statuses_favourite - * - * Favouriting a status - * - * @param int $id - * - * @return array $response - */ - public function statuses_favourite ($id) { - $response = $this->_post('/api/v2/statuses/'.$id.'/favourite'); - return $response; - } - - /** - * statuses_unfavourite - * - * Unfavouriting a status - * - * @param int $id - * - * @return array $response - */ - public function statuses_unfavourite ($id) { - $response = $this->_post('/api/v2/statuses/'.$id.'/unfavourite'); - return $response; - } - - /** - * timelines_home - * - * @return array $response - */ - public function timelines_home () { - $response = $this->_get('/api/v2/timelines/home'); - return $response; - } - - /** - * timelines_public - * - * @param array $parameters - * bool $parameters['local'] Only return statuses originating from this instance - * - * @return array $response - */ - public function timelines_public ($parameters = array()) { - $response = $this->_get('/api/v2/timelines/public',$parameters); - return $response; - } - - /** - * timelines_tag - * - * @param string $hashtag - * @param array $parameters - * bool $parameters['local'] Only return statuses originating from this instance - * - * @return array $response - */ - public function timelines_tag ($hashtag,$parameters = array()) { - $response = $this->_get('/api/v2/timelines/tag/'.$hashtag,$parameters); - return $response; - } -} diff --git a/themes/hugo-mastodon-comments/static/comments/config.sample.php b/themes/hugo-mastodon-comments/static/comments/config.sample.php index 8cdc8f7..1b48359 100644 --- a/themes/hugo-mastodon-comments/static/comments/config.sample.php +++ b/themes/hugo-mastodon-comments/static/comments/config.sample.php @@ -1,7 +1,7 @@ 'https://mastodon.social', - 'user-id' => 549759, - 'threshold' => 300, - 'token' => '47598kdjhfgkdg894kejg834joejg43' + 'user-id' => 379833, + 'search-url' => 'https?://fsfe.org', // please use https?:// as schema + 'threshold' => 300 ]; diff --git a/themes/hugo-mastodon-comments/static/comments/getcomments.js b/themes/hugo-mastodon-comments/static/comments/getcomments.js index a7d5576..8df5fe7 100644 --- a/themes/hugo-mastodon-comments/static/comments/getcomments.js +++ b/themes/hugo-mastodon-comments/static/comments/getcomments.js @@ -38,11 +38,11 @@ $(document).ready(function() { } }); if (parseInt(root) > 0) { - $("#reference").append("Join the discussion on Mastodon!"); + $("#reference").append("Join the discussion on Mastodon!"); } else { $("#comments").empty(); $("#statistics").empty(); - $("#reference").append("Comments are handled by my Mastodon account. Sadly this article wasn't published at Mastodon. Feel free to send me a mail if you want to share your thoughts regarding this topic."); + $("#reference").append("Comments are handled by my Mastodon account. Sadly this article wasn't published at Mastodon. Feel free to send me a mail if you want to share your thoughts regarding this topic."); } } diff --git a/themes/hugo-mastodon-comments/static/comments/getcomments.php b/themes/hugo-mastodon-comments/static/comments/getcomments.php index 2743773..0eca6dc 100644 --- a/themes/hugo-mastodon-comments/static/comments/getcomments.php +++ b/themes/hugo-mastodon-comments/static/comments/getcomments.php @@ -1,183 +1,159 @@ - * - * @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 . - * - */ -require_once 'Mastodon_api.php'; -require_once '../../mastodon.feed/config.php'; +require_once 'config.php'; +$instance = $config['mastodon-instance']; +$uid = $config['user-id']; +$searchurl = $config['search-url']; +$search = isset($_GET['search']) ? $_GET['search'] : ''; +/* cache files */ +$dbt = "cache-toots.json"; -class CollectMastodonData { - - /** @var \Mastodon_api */ - private $api; - - /** @var string url of the mastodon instance */ - private $mastodonUrl = 'https://mastodon.social'; - - /** @var string token to authenticate at the mastodon instance */ - private $bearerToken; - - /** @var int keep cache at least 600 seconds = 10 minutes */ - private $threshold = 600; - - /** @var string uid on the mastodon instance */ - private $uid; - - /** @var array cached comments from previous searches */ - private $commentCache = []; - - private $cacheFile = 'myCommentsCache.json'; - - public function __construct($config) { - $this->mastodonUrl = $config['mastodon-instance']; - $this->bearerToken = $config['token']; - $this->uid = $config['user-id']; - - $this->api = new Mastodon_api(); - - $this->api->set_url($this->mastodonUrl); - $this->api->set_token($this->bearerToken, 'bearer'); - } - - private function filterComments($descendants, $root, &$result) { - foreach ($descendants as $d) { - $result['comments'][$d['id']] = [ - 'author' => [ - 'display_name' => $d['account']['display_name'] ? $d['account']['display_name'] : $d['account']['username'], - 'avatar' => $d['account']['avatar_static'], - 'url' => $d['account']['url'] - ], - 'toot' => $d['content'], - 'date' => $d['created_at'], - 'url' => $d['uri'], - 'reply_to' => $d['in_reply_to_id'], - 'root' => $root, - ]; - } - - return $result; - } - - private function filterStats($stats) { - $result = [ - 'reblogs' => (int)$stats['reblogs_count'], - 'favs' => (int)$stats['favourites_count'], - 'replies' => (int)$stats['replies_count'], - 'url' => $stats['url'] - ]; - return $result; - } - - private function filterSearchResults($searchResult) { - $result = []; - if (isset($searchResult['html']['statuses'])) { - foreach ($searchResult['html']['statuses'] as $status) { - if ($status['in_reply_to_id'] === null) { - $result[] = $status['id']; - } - } - } - - sort($result); - return $result; - } - - /** - * find all toots for a given blog post and return the corresponding IDs - * - * @param string $search - * @return array - */ - public function findToots($search) { - $result = $this->api->search(['q' => $search]); - return $this->filterSearchResults($result); - } - - public function getComments($id, &$result) { - $raw = file_get_contents("https://mastodon.social/api/v1/statuses/$id/context"); - $json = json_decode($raw, true); - $this->filterComments($json['descendants'], $id, $result); - } - - public function getStatistics($id, &$result) { - $raw = file_get_contents("https://mastodon.social/api/v1/statuses/$id"); - $json = json_decode($raw, true); - $newStats = $this->filterStats($json); - $result['stats']['reblogs'] += $newStats['reblogs']; - $result['stats']['favs'] += $newStats['favs']; - $result['stats']['replies'] += $newStats['replies']; - if (empty($result['stats']['url'])) { - $result['stats']['url'] = $newStats['url']; - } - } - - public function storeCollection($id, $comments) { - $timestamp = time(); - $comments['timestamp'] = $timestamp; - $this->commentCache[$id] = $comments; - file_put_contents($this->cacheFile, json_encode($this->commentCache)); - } - - public function getCachedCollection($search) { - if (file_exists($this->cacheFile)) { - $cachedComments = file_get_contents($this->cacheFile); - $cachedCommentsArray = json_decode($cachedComments, true); - if (is_array($cachedCommentsArray)) { - $this->commentCache = $cachedCommentsArray; - $currentTimestamp = time(); - if (isset($cachedCommentsArray[$search])) { - if ((int)$cachedCommentsArray[$search]['timestamp'] + $this->threshold > $currentTimestamp) { - unset($cachedCommentsArray[$search]['timestamp']); - return $cachedCommentsArray[$search]; - } - } - } - } - - return []; - } +/* MISC FUNCTIONS */ +function out($data) { + error_log("[getcomments.php] " . print_r($data, TRUE)); } +/* CACHE FUNCTIONS */ +/* write data to file */ +function write_db($db, $data) { + // encode and write file + $encoded = json_encode($data, JSON_PRETTY_PRINT); + file_put_contents($db, $encoded, LOCK_EX); +} +/* access data from file */ +function read_db($db, &$data) { + if (! file_exists($db)) { + touch($db); + } + $file = file_get_contents($db, true); + $data = json_decode($file, true); +} + +/* TOOT FUNCTIONS */ +function collectToots($instance, $uid, $min_id) { + $raw = file_get_contents("$instance/api/v1/accounts/$uid/statuses?exclude_reblogs=true&exclude_replies=true&limit=50&min_id=$min_id"); + $json = json_decode($raw, true); + return($json); +} + + +/* Collect all the toots */ +$toots = array(); +/* get id of latest cached toot, and set as $min_id */ +read_db($dbt, $toots); +if (!empty($toots['0']['id'])) { + $min_id_cached = $toots['0']['id']; + $min_id = $min_id_cached; +} else { + /* if cached toots do not exist, start from oldest toot */ + $min_id = "0"; +} + + +/* test whether there are new toots available */ +$min_id_new = "1"; +while ($min_id_new !== $min_id) { + $min_id_new = $min_id; + + $toots = array_merge(collectToots($instance, $uid, $min_id), $toots); + $min_id = $toots['0']['id']; +} + +/* if newer toot has been found, find new URLs */ +// TODO: only look up newly found toots +if ($min_id !== $min_id_cached) { + out("New toots found"); + $ids = array_column($toots, 'id'); + + /* Find out if a toot contains the searched URL */ + function analyzeToot($instance, $id, $searchurl) { + $raw = file_get_contents("$instance/api/v1/statuses/$id"); + $json = json_decode($raw, true); + + preg_match("|$searchurl.+?(?=\")|i", $json['content'], $matches); + + if(!empty($matches)) { + return(strtolower($matches[0])); + } else { + return(""); + } + } + + $toots = array(); + foreach ($ids as $id) { + $toots[] = array('id' => $id, 'url' => analyzeToot($instance, $id, $searchurl)); + } + + write_db($dbt, $toots); +} else { + out("No new toots found"); +} + +/* check if URL from $search exists in $toots */ +// if multiple exist, take the oldest one (highest array position) +$id = array_keys(array_column($toots, 'url'), strtolower($search)); +$id = $toots[end($id)]['id']; +// TODO: graceful exit if no toot exists + + +$id = "102955148581768112"; // TODO test +/* Extract comments and stats from toot */ $result = ['comments' => [], 'stats' => ['reblogs' => 0, 'favs' => 0, 'replies' => 0, 'url' => '', 'root' => 0]]; -$search = isset($_GET['search']) ? $_GET['search'] : ''; -$collector = new CollectMastodonData($config); -$ids = []; -if (!empty($search)) { - $oldCollection = $collector->getCachedCollection($search); - if (empty($oldCollection)) { - $ids = $collector->findToots($search); - $result['stats']['root'] = isset($ids[0]) ? $ids[0] : 0; - foreach ($ids as $id) { - // get comments - $newComments = $collector->getComments($id, $result); - // get statistics (likes, replies, boosts,...) - $collector->getStatistics($id, $result); - // FIXME: At the moment the API doesn't return the correct replies count so I count it manually - $result['stats']['replies'] = count($result['comments']); - } - $collector->storeCollection($search, $result); - } else { - $result = $oldCollection; - } +function filterComments($descendants, $root, &$result) { + foreach ($descendants as $d) { + $result['comments'][$d['id']] = [ + 'author' => [ + 'display_name' => $d['account']['display_name'] ? $d['account']['display_name'] : $d['account']['username'], + 'avatar' => $d['account']['avatar_static'], + 'url' => $d['account']['url'] + ], + 'toot' => $d['content'], + 'date' => $d['created_at'], + 'url' => $d['uri'], + 'reply_to' => $d['in_reply_to_id'], + 'root' => $root, + ]; + } + return $result; } +function tootContext($instance, $id, &$result) { + $raw = file_get_contents("$instance/api/v1/statuses/$id/context"); + $json = json_decode($raw, true); + filterComments($json['descendants'], $id, $result); +} + +function filterStats($stats) { + $result = [ + 'reblogs' => (int)$stats['reblogs_count'], + 'favs' => (int)$stats['favourites_count'], + 'replies' => (int)$stats['replies_count'], + 'url' => $stats['url'] + ]; + return $result; +} + +function tootStats($instance, $id, &$result) { + out("Checking ID $id"); + $raw = file_get_contents("$instance/api/v1/statuses/$id"); + $json = json_decode($raw, true); + $newStats = filterStats($json); + $result['stats']['reblogs'] += $newStats['reblogs']; + $result['stats']['favs'] += $newStats['favs']; + $result['stats']['replies'] += $newStats['replies']; + if (empty($result['stats']['url'])) { + $result['stats']['url'] = $newStats['url']; + } +} + +// FIXME: At the moment the API doesn't return the correct replies count so I count it manually +$result['stats']['replies'] = count($result['comments']); +$result['stats']['root'] = $id; +tootContext($instance, $id, $result); +tootStats($instance, $id, $result); + + // headers for not caching the results header('Cache-Control: no-cache, must-revalidate'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); @@ -185,5 +161,6 @@ header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // headers to tell that result is JSON header('Content-type: application/json'); -// send the result now echo json_encode($result); + +?> diff --git a/themes/hugo-mastodon-comments/static/comments/mastodon-comments.css b/themes/hugo-mastodon-comments/static/comments/mastodon-comments.css index 615ad72..0da2180 100644 --- a/themes/hugo-mastodon-comments/static/comments/mastodon-comments.css +++ b/themes/hugo-mastodon-comments/static/comments/mastodon-comments.css @@ -1,3 +1,8 @@ +.comments-container { + text-align: left; + padding-bottom: 15px; +} + .comments-container .comment .avatar { float: left; width: 50px; @@ -5,6 +10,7 @@ margin-left: 16px; margin-right: 16px; border-radius: 50%; + padding-top: 0; } .comments-container #reference {