Compare commits
5 Commits
96db0131ad
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
67d92eba01
|
|||
|
330925edce
|
|||
|
40508c883e
|
|||
|
0764bba221
|
|||
|
a749a6b6aa
|
120
README.md
Normal file
120
README.md
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# Hugo Mastodon Comments
|
||||||
|
|
||||||
|
A Hugo theme component that displays Mastodon comments on your blog posts. When you share a blog post on Mastodon, replies to that toot are automatically fetched and displayed as comments on your site.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. You publish a blog post and share it on Mastodon
|
||||||
|
2. The script searches your Mastodon timeline for toots containing links to your blog
|
||||||
|
3. When a matching toot is found, it fetches all replies (descendants)
|
||||||
|
4. Comments are displayed on your blog with author info, avatars, and engagement stats
|
||||||
|
5. Results are cached to minimize API calls
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### 1. Copy Files
|
||||||
|
|
||||||
|
Copy the `static/comments/` directory to your Hugo site's static folder or use this as a theme component.
|
||||||
|
|
||||||
|
### 2. Configure Backend
|
||||||
|
|
||||||
|
Create `static/comments/config.php` from the sample:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
$config = [
|
||||||
|
'mastodon-instance' => 'https://mastodon.social',
|
||||||
|
'user-id' => 123456, // Your Mastodon user ID
|
||||||
|
'search-url' => 'https?://yourdomain.com',
|
||||||
|
'cache_toots' => 300, // Cache toots list for 5 minutes
|
||||||
|
'cache_comments' => 300, // Cache comments per toot for 5 minutes
|
||||||
|
'debug' => false
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
To find your Mastodon user ID, check your profile API: `https://mastodon.social/api/v1/accounts/lookup?acct=username`
|
||||||
|
|
||||||
|
### 3. Configure Hugo
|
||||||
|
|
||||||
|
Add to your `config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[params.mastodoncomments]
|
||||||
|
user = "@username@mastodon.social"
|
||||||
|
regex = "yourdomain.com"
|
||||||
|
contact = "https://mastodon.social/@username"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Add to Template
|
||||||
|
|
||||||
|
Include the partial in your blog post layout:
|
||||||
|
|
||||||
|
```html
|
||||||
|
{{ partial "comments.html" . }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Display Comments
|
||||||
|
|
||||||
|
Comments are loaded automatically via JavaScript when visitors view a blog post. The script:
|
||||||
|
- Extracts the current page's permalink
|
||||||
|
- Queries the PHP backend to find matching Mastodon toots
|
||||||
|
- Displays replies as comments with full formatting
|
||||||
|
|
||||||
|
### Optional: Direct Toot URL (Recommended for Performance)
|
||||||
|
|
||||||
|
For better performance, you can specify the Mastodon toot URL directly in your post's front matter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: "My Post Title"
|
||||||
|
mastodon_toot_url: "https://mastodon.social/@username/113456789012345678"
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- Skips the entire toot search phase
|
||||||
|
- Reduces API calls significantly
|
||||||
|
- Faster page loads
|
||||||
|
- Works across different Mastodon instances
|
||||||
|
- Ensures the correct toot is always used
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- For any post where you know the Mastodon toot URL
|
||||||
|
- Especially recommended for popular or archived posts
|
||||||
|
- When you've shared the post on a different Mastodon instance
|
||||||
|
|
||||||
|
If no `mastodon_toot_url` is provided, the system automatically falls back to searching for toots containing the post URL.
|
||||||
|
|
||||||
|
### Force Cache Refresh
|
||||||
|
|
||||||
|
To manually clear and rebuild the cache:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://yourdomain.com/comments/getcomments.php?force_refresh=1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cache Configuration
|
||||||
|
|
||||||
|
Adjust cache times based on your needs:
|
||||||
|
- **Lower values (60-300s)**: Faster updates, more API calls
|
||||||
|
- **Higher values (1800-3600s)**: Reduced load, slower updates
|
||||||
|
|
||||||
|
For most blogs, 300-1800 seconds is appropriate.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- PHP 7.0+ with `file_get_contents` enabled
|
||||||
|
- Write permissions for cache files in the comments directory
|
||||||
|
- Network access to your Mastodon instance API
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
- PHP backend: AGPL-3.0-or-later
|
||||||
|
- JavaScript: AGPL-3.0-or-later
|
||||||
|
- Configuration samples: CC0-1.0
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
Based on work by Björn Schießle.
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
SPDX-FileCopyrightText: 2019 Björn Schießle
|
SPDX-FileCopyrightText: 2019 Björn Schießle
|
||||||
-->
|
-->
|
||||||
{{ if ne .Params.page true }}
|
{{ if ne .Params.page true }}
|
||||||
|
<hr />
|
||||||
<div class="comments-container">
|
<div class="comments-container">
|
||||||
<h5>Comments</h5>
|
<h5>Comments</h5>
|
||||||
<noscript>
|
<noscript>
|
||||||
@@ -29,3 +30,4 @@
|
|||||||
<script>var MastodonUser="{{ .Site.Params.mastodoncomments.user }}"</script>
|
<script>var MastodonUser="{{ .Site.Params.mastodoncomments.user }}"</script>
|
||||||
<script>var BlogRegex="{{ .Site.Params.mastodoncomments.regex }}"</script>
|
<script>var BlogRegex="{{ .Site.Params.mastodoncomments.regex }}"</script>
|
||||||
<script>var CommentsContact="{{ .Site.Params.mastodoncomments.contact }}"</script>
|
<script>var CommentsContact="{{ .Site.Params.mastodoncomments.contact }}"</script>
|
||||||
|
<script>var MastodonTootUrl="{{ .Params.mastodon_toot_url }}"</script>
|
||||||
|
|||||||
@@ -4,6 +4,18 @@
|
|||||||
SPDX-FileCopyrightText: 2019 Björn Schießle
|
SPDX-FileCopyrightText: 2019 Björn Schießle
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Debug logging helper
|
||||||
|
var debugEnabled = false;
|
||||||
|
function debugLog(message, data) {
|
||||||
|
if (debugEnabled) {
|
||||||
|
if (data !== undefined) {
|
||||||
|
console.log('[Mastodon Comments] ' + message, data);
|
||||||
|
} else {
|
||||||
|
console.log('[Mastodon Comments] ' + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
// check if we show a blog post or not. Regex is defined in site-wide config
|
// check if we show a blog post or not. Regex is defined in site-wide config
|
||||||
@@ -14,20 +26,45 @@ $(document).ready(function() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugLog('Searching for comments for: ' + RelPermalink);
|
||||||
|
|
||||||
|
// Check if a direct Mastodon toot URL is provided
|
||||||
|
var ajaxData = { search: RelPermalink };
|
||||||
|
if (MastodonTootUrl && MastodonTootUrl !== "") {
|
||||||
|
// Pass the full URL to PHP so it can extract instance and ID
|
||||||
|
ajaxData.toot_url = MastodonTootUrl;
|
||||||
|
debugLog('Using predefined Mastodon toot URL: ' + MastodonTootUrl);
|
||||||
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/comments/getcomments.php",
|
url: "/comments/getcomments.php",
|
||||||
type: "get",
|
type: "get",
|
||||||
data: {
|
data: ajaxData,
|
||||||
search : RelPermalink
|
|
||||||
},
|
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
// Enable debug logging if backend has debug enabled
|
||||||
|
if (data.debug === true) {
|
||||||
|
debugEnabled = true;
|
||||||
|
debugLog('Debug mode enabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLog('Received data from backend', data);
|
||||||
|
|
||||||
var stats = data.stats;
|
var stats = data.stats;
|
||||||
var root = data.stats.root;
|
var root = data.stats.root;
|
||||||
|
|
||||||
|
debugLog('Stats - Favs: ' + stats.favs + ', Reblogs: ' + stats.reblogs + ', Replies: ' + stats.replies);
|
||||||
|
debugLog('Root toot ID: ' + root);
|
||||||
|
|
||||||
$("#like-count-container").append('<div id="mastodon-like-count"><a href="' + stats.url + '"><span title="Likes"><i class="fa fa-star"></i>' + stats.favs + '</span></a></div></div>');
|
$("#like-count-container").append('<div id="mastodon-like-count"><a href="' + stats.url + '"><span title="Likes"><i class="fa fa-star"></i>' + stats.favs + '</span></a></div></div>');
|
||||||
$("#reblog-count-container").append('<div id="mastodon-reblog-count"><a href="' + stats.url + '"><span title="Reblogs"><i class="fa fa-retweet"></i>' + stats.reblogs + '</span></a></div></div>');
|
$("#reblog-count-container").append('<div id="mastodon-reblog-count"><a href="' + stats.url + '"><span title="Reblogs"><i class="fa fa-retweet"></i>' + stats.reblogs + '</span></a></div></div>');
|
||||||
$("#reply-count-container").append('<div id="mastodon-reply-count"><a href="' + stats.url + '"><span title="Comments"><i class="fa fa-comments"></i>' + stats.replies + '</span></a></div></div>');
|
$("#reply-count-container").append('<div id="mastodon-reply-count"><a href="' + stats.url + '"><span title="Comments"><i class="fa fa-comments"></i>' + stats.replies + '</span></a></div></div>');
|
||||||
|
|
||||||
var comments = data.comments;
|
var comments = data.comments;
|
||||||
|
var commentCount = Object.keys(comments).length;
|
||||||
|
debugLog('Processing ' + commentCount + ' comments');
|
||||||
|
|
||||||
$.each(comments, function(key, value) {
|
$.each(comments, function(key, value) {
|
||||||
|
debugLog('Adding comment: ' + key, value);
|
||||||
var timestamp = Date.parse(value.date);
|
var timestamp = Date.parse(value.date);
|
||||||
var date = new Date(timestamp);
|
var date = new Date(timestamp);
|
||||||
var comment = "<div class='comment' id='" + key + "'>";
|
var comment = "<div class='comment' id='" + key + "'>";
|
||||||
@@ -39,19 +76,28 @@ $(document).ready(function() {
|
|||||||
var parentComment = document.getElementById(value.reply_to);
|
var parentComment = document.getElementById(value.reply_to);
|
||||||
if (value.reply_to === root || parentComment === null) {
|
if (value.reply_to === root || parentComment === null) {
|
||||||
$("#comments").append(comment);
|
$("#comments").append(comment);
|
||||||
|
debugLog(' → Added as top-level comment');
|
||||||
} else {
|
} else {
|
||||||
var selector = '#'+value.reply_to;
|
var selector = '#'+value.reply_to;
|
||||||
$(selector).append(comment);
|
$(selector).append(comment);
|
||||||
|
debugLog(' → Added as reply to: ' + value.reply_to);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parseInt(root) > 0) {
|
if (parseInt(root) > 0) {
|
||||||
$("#reference").append("<a href='" + MastodonUser + "/statuses/" + root + "'>Join the discussion on Mastodon!</a>");
|
$("#reference").append("<a href='" + MastodonUser + "/statuses/" + root + "'>Join the discussion on Mastodon!</a>");
|
||||||
|
debugLog('Added Mastodon discussion link for root: ' + root);
|
||||||
} else {
|
} else {
|
||||||
$("#comments").empty();
|
$("#comments").empty();
|
||||||
$("#statistics").empty();
|
$("#statistics").empty();
|
||||||
$("#reference").append("Comments are handled by my <a href='" + MastodonUser + "'>Mastodon account</a>. Sadly this article wasn't published at Mastodon. Feel free to <a href='" + CommentsContact + "'>send me a mail</a> if you want to share your thoughts regarding this topic.");
|
$("#reference").append("Comments are handled by my <a href='" + MastodonUser + "'>Mastodon account</a>. Sadly this article wasn't published at Mastodon. Feel free to <a href='" + CommentsContact + "'>send me a mail</a> if you want to share your thoughts regarding this topic.");
|
||||||
|
debugLog('No Mastodon toot found for this article');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error('[Mastodon Comments] AJAX Error:', status, error);
|
||||||
|
console.error('[Mastodon Comments] Response:', xhr.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,13 +10,30 @@ $instance = $config['mastodon-instance'];
|
|||||||
$uid = $config['user-id'];
|
$uid = $config['user-id'];
|
||||||
$searchurl = $config['search-url'];
|
$searchurl = $config['search-url'];
|
||||||
$search = isset($_GET['search']) ? strtolower($_GET['search']) : '';
|
$search = isset($_GET['search']) ? strtolower($_GET['search']) : '';
|
||||||
|
$toot_url = isset($_GET['toot_url']) ? $_GET['toot_url'] : null;
|
||||||
|
$force_refresh = isset($_GET['force_refresh']) && $_GET['force_refresh'] == '1';
|
||||||
$debug_on = $config['debug'];
|
$debug_on = $config['debug'];
|
||||||
|
|
||||||
|
debug("Request parameters - search: $search, toot_url: " . ($toot_url ? $toot_url : "not provided") . ", force_refresh: " . ($force_refresh ? "yes" : "no"));
|
||||||
/* cache files */
|
/* cache files */
|
||||||
$ctt = $config['cache_toots'];
|
$ctt = $config['cache_toots'];
|
||||||
$dbt = "cache-toots.json";
|
$dbt = "cache-toots.json";
|
||||||
$ctc = $config['cache_comments'];
|
$ctc = $config['cache_comments'];
|
||||||
$dbc = "cache-comments_%id.json";
|
$dbc = "cache-comments_%id.json";
|
||||||
|
|
||||||
|
/* Force cache refresh if requested */
|
||||||
|
if ($force_refresh) {
|
||||||
|
debug("Force refresh requested - clearing toot cache");
|
||||||
|
if (file_exists($dbt)) {
|
||||||
|
unlink($dbt);
|
||||||
|
}
|
||||||
|
// Also clear all comment cache files
|
||||||
|
foreach (glob("cache-comments_*.json") as $cache_file) {
|
||||||
|
unlink($cache_file);
|
||||||
|
debug("Deleted cache file: $cache_file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Exit if search empty */
|
/* Exit if search empty */
|
||||||
if (empty($search)) {
|
if (empty($search)) {
|
||||||
debug("No proper search given");
|
debug("No proper search given");
|
||||||
@@ -31,6 +48,19 @@ function debug($data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse Mastodon toot URL to extract instance and toot ID */
|
||||||
|
function parseTootUrl($url) {
|
||||||
|
// Expected format: https://instance.example/@username/1234567890
|
||||||
|
// or: https://instance.example/users/username/statuses/1234567890
|
||||||
|
if (preg_match('|^(https?://[^/]+)/.*/(\d+)$|i', $url, $matches)) {
|
||||||
|
return [
|
||||||
|
'instance' => $matches[1],
|
||||||
|
'id' => $matches[2]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/* CACHE FUNCTIONS */
|
/* CACHE FUNCTIONS */
|
||||||
/* write data to file */
|
/* write data to file */
|
||||||
function write_db($db, $data, $id) {
|
function write_db($db, $data, $id) {
|
||||||
@@ -40,8 +70,8 @@ function write_db($db, $data, $id) {
|
|||||||
}
|
}
|
||||||
$file['toots'] = $data;
|
$file['toots'] = $data;
|
||||||
$file['timestamp'] = time();
|
$file['timestamp'] = time();
|
||||||
// encode and write file
|
// encode and write file (no pretty print for performance)
|
||||||
$encoded = json_encode($file, JSON_PRETTY_PRINT);
|
$encoded = json_encode($file);
|
||||||
file_put_contents($db, $encoded, LOCK_EX);
|
file_put_contents($db, $encoded, LOCK_EX);
|
||||||
}
|
}
|
||||||
/* delete file */
|
/* delete file */
|
||||||
@@ -81,22 +111,25 @@ function read_db($db, &$data, $cachetime, &$cachebreak, $id) {
|
|||||||
|
|
||||||
/* TOOT FUNCTIONS */
|
/* TOOT FUNCTIONS */
|
||||||
function collectToots($instance, $uid, $min_id, $searchurl) {
|
function collectToots($instance, $uid, $min_id, $searchurl) {
|
||||||
$raw = file_get_contents("$instance/api/v1/accounts/$uid/statuses?exclude_reblogs=true&exclude_replies=true&limit=50&min_id=$min_id");
|
$raw = @file_get_contents("$instance/api/v1/accounts/$uid/statuses?exclude_reblogs=true&exclude_replies=true&limit=50&min_id=$min_id");
|
||||||
|
if ($raw === false) {
|
||||||
|
debug("Failed to fetch toots from API");
|
||||||
|
return array();
|
||||||
|
}
|
||||||
$json_complete = json_decode($raw, true);
|
$json_complete = json_decode($raw, true);
|
||||||
$json = array();
|
$json = array();
|
||||||
foreach ($json_complete as $toot) {
|
foreach ($json_complete as $toot) {
|
||||||
$json[] = array('id' => $toot['id'], 'date' => $toot['created_at'] ,'url' => analyzeToot($instance, $toot['id'], $searchurl));
|
$url = analyzeToot($toot['content'], $toot['id'], $searchurl);
|
||||||
|
$json[] = array('id' => $toot['id'], 'date' => $toot['created_at'] ,'url' => $url);
|
||||||
}
|
}
|
||||||
return($json);
|
return($json);
|
||||||
}
|
}
|
||||||
/* Find out if a toot contains the searched URL */
|
/* Find out if a toot contains the searched URL */
|
||||||
function analyzeToot($instance, $id, $searchurl) {
|
function analyzeToot($content, $id, $searchurl) {
|
||||||
debug("Searching for $searchurl in $id");
|
debug("Searching for $searchurl in $id");
|
||||||
$raw = file_get_contents("$instance/api/v1/statuses/$id");
|
|
||||||
$json = json_decode($raw, true);
|
|
||||||
|
|
||||||
// search for $searchurl inside of <a> tags, until (and excluding) a "
|
// search for $searchurl inside of <a> tags, until (and excluding) a "
|
||||||
preg_match("|$searchurl.+?(?=\")|i", $json['content'], $matches);
|
preg_match("|$searchurl.+?(?=\")|i", $content, $matches);
|
||||||
|
|
||||||
if(!empty($matches)) {
|
if(!empty($matches)) {
|
||||||
return(strtolower($matches[0])); // take first match inside toot
|
return(strtolower($matches[0])); // take first match inside toot
|
||||||
@@ -123,33 +156,29 @@ function filterComments($descendants, $root, &$result) {
|
|||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
/* get /context of toot */
|
/* get /context of toot and extract stats - combined to reduce API calls */
|
||||||
function tootContext($instance, $id, &$result) {
|
function tootContextAndStats($instance, $id, &$result) {
|
||||||
$raw = file_get_contents("$instance/api/v1/statuses/$id/context");
|
debug("Fetching context and stats for ID $id");
|
||||||
$json = json_decode($raw, true);
|
|
||||||
filterComments($json['descendants'], $id, $result);
|
// Fetch context (descendants/replies)
|
||||||
|
$raw_context = @file_get_contents("$instance/api/v1/statuses/$id/context");
|
||||||
|
if ($raw_context !== false) {
|
||||||
|
$json_context = json_decode($raw_context, true);
|
||||||
|
filterComments($json_context['descendants'], $id, $result);
|
||||||
|
} else {
|
||||||
|
debug("Failed to fetch context for $id");
|
||||||
}
|
}
|
||||||
/* extract stats info from toot */
|
|
||||||
function filterStats($stats) {
|
// Fetch stats
|
||||||
$result = [
|
$raw_stats = @file_get_contents("$instance/api/v1/statuses/$id");
|
||||||
'reblogs' => (int)$stats['reblogs_count'],
|
if ($raw_stats !== false) {
|
||||||
'favs' => (int)$stats['favourites_count'],
|
$json_stats = json_decode($raw_stats, true);
|
||||||
'replies' => (int)$stats['replies_count'],
|
$result['stats']['reblogs'] = (int)$json_stats['reblogs_count'];
|
||||||
'url' => $stats['url']
|
$result['stats']['favs'] = (int)$json_stats['favourites_count'];
|
||||||
];
|
$result['stats']['replies'] = (int)$json_stats['replies_count'];
|
||||||
return $result;
|
$result['stats']['url'] = $json_stats['url'];
|
||||||
}
|
} else {
|
||||||
/* for toot, extract interesting statistics */
|
debug("Failed to fetch stats for $id");
|
||||||
function tootStats($instance, $id, &$result) {
|
|
||||||
debug("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'];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,9 +186,38 @@ function tootStats($instance, $id, &$result) {
|
|||||||
* START PROGRAM
|
* START PROGRAM
|
||||||
***************/
|
***************/
|
||||||
|
|
||||||
|
// create empty $result template
|
||||||
|
$result_empty = ['comments' => [], 'stats' => ['reblogs' => 0, 'favs' => 0, 'replies' => 0, 'url' => '', 'root' => 0]];
|
||||||
|
$result = $result_empty;
|
||||||
|
|
||||||
|
/* If toot_url is provided, skip the toot search entirely */
|
||||||
|
$toot_instance = $instance; // Default to config instance
|
||||||
|
$id = null; // Initialize
|
||||||
|
|
||||||
|
if ($toot_url) {
|
||||||
|
$parsed = parseTootUrl($toot_url);
|
||||||
|
if ($parsed) {
|
||||||
|
$id = $parsed['id'];
|
||||||
|
$toot_instance = $parsed['instance'];
|
||||||
|
debug("Toot URL provided: $toot_url");
|
||||||
|
debug("Extracted - Instance: $toot_instance, ID: $id");
|
||||||
|
|
||||||
|
// When using a custom instance, include instance hash in cache filename to avoid collisions
|
||||||
|
if ($toot_instance !== $instance) {
|
||||||
|
$instance_hash = md5($toot_instance);
|
||||||
|
$dbc = "cache-comments_%id-{$instance_hash}.json";
|
||||||
|
debug("Using instance-specific cache file for non-default instance");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug("Failed to parse toot URL: $toot_url");
|
||||||
|
$toot_url = null; // Fall back to search
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$toot_url) {
|
||||||
/* check whether the cached file containing all toots is older than max. cache time */
|
/* check whether the cached file containing all toots is older than max. cache time */
|
||||||
// this at the same time loads the cached DB, either way
|
// this at the same time loads the cached DB, either way
|
||||||
$cachebreak = false;
|
$cachebreak = $force_refresh; // Force cache break if requested
|
||||||
read_db($dbt, $toots, $ctt, $cachebreak, false);
|
read_db($dbt, $toots, $ctt, $cachebreak, false);
|
||||||
|
|
||||||
if ($cachebreak) {
|
if ($cachebreak) {
|
||||||
@@ -197,24 +255,23 @@ if ($cachebreak) {
|
|||||||
debug("Toots cache is up-to-date");
|
debug("Toots cache is up-to-date");
|
||||||
}
|
}
|
||||||
|
|
||||||
// create empty $result
|
|
||||||
$result_empty = ['comments' => [], 'stats' => ['reblogs' => 0, 'favs' => 0, 'replies' => 0, 'url' => '', 'root' => 0]];
|
|
||||||
$result = $result_empty;
|
|
||||||
|
|
||||||
/* check if URL from $search exists in $toots */
|
/* check if URL from $search exists in $toots */
|
||||||
$id = array_keys(
|
$found_id = null;
|
||||||
array_filter(
|
foreach ($toots as $toot) {
|
||||||
array_column($toots, 'url'),
|
if (!empty($toot['url']) && strpos($toot['url'], $search) !== false) {
|
||||||
function ($value) use ($search) {
|
$found_id = $toot['id']; // will keep the oldest (last in array)
|
||||||
return (strpos($value, $search) !== false);
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
);
|
|
||||||
if (empty($id)) {
|
if ($found_id === null) {
|
||||||
debug("Blog URL \"$search\" has not been found");
|
debug("Blog URL \"$search\" has not been found");
|
||||||
} else {
|
} else {
|
||||||
// if multiple toots with the searched URL exist, take the oldest one (largest array index)
|
$id = $found_id;
|
||||||
$id = $toots[end($id)]['id'];
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process comments if we have a toot ID (either from URL or search) */
|
||||||
|
if ($id) {
|
||||||
|
|
||||||
/* read cached comments, or reload new comments if cached data too old */
|
/* read cached comments, or reload new comments if cached data too old */
|
||||||
$cachebreak = false;
|
$cachebreak = false;
|
||||||
@@ -228,9 +285,8 @@ if (empty($id)) {
|
|||||||
$result = $result_empty;
|
$result = $result_empty;
|
||||||
read_db($dbc, $result, $ctc, $cachebreak, $id);
|
read_db($dbc, $result, $ctc, $cachebreak, $id);
|
||||||
/* Extract comments and stats from toot */
|
/* Extract comments and stats from toot */
|
||||||
tootContext($instance, $id, $result);
|
tootContextAndStats($toot_instance, $id, $result);
|
||||||
tootStats($instance, $id, $result);
|
// Always count replies manually for accuracy
|
||||||
// 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']['replies'] = count($result['comments']);
|
||||||
$result['stats']['root'] = $id;
|
$result['stats']['root'] = $id;
|
||||||
|
|
||||||
@@ -247,6 +303,9 @@ header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
|||||||
// headers to tell that result is JSON
|
// headers to tell that result is JSON
|
||||||
header('Content-type: application/json');
|
header('Content-type: application/json');
|
||||||
|
|
||||||
|
// add debug flag to result for JavaScript console logging
|
||||||
|
$result['debug'] = $debug_on;
|
||||||
|
|
||||||
// actually output result as JSON, to be digested by getcomments.js
|
// actually output result as JSON, to be digested by getcomments.js
|
||||||
echo json_encode($result);
|
echo json_encode($result);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user