190 lines
6.5 KiB
PHP
190 lines
6.5 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org>
|
||
|
|
*
|
||
|
|
* @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 <http://www.gnu.org/licenses/>.
|
||
|
|
*
|
||
|
|
*/
|
||
|
|
|
||
|
|
require_once 'Mastodon_api.php';
|
||
|
|
require_once '../../mastodon.feed/config.php';
|
||
|
|
|
||
|
|
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 [];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// headers for not caching the results
|
||
|
|
header('Cache-Control: no-cache, must-revalidate');
|
||
|
|
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);
|