diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx
index 24595a3..c35095f 100644
--- a/src/app/about/page.tsx
+++ b/src/app/about/page.tsx
@@ -181,7 +181,9 @@ export default function AboutPage() {
Once per day you can open the Lucky Dip lottery. Pick a box — most are empty, but a few hold cash prizes.
- Winnings are added directly to your balance.
+ Winnings are added directly to your balance. So head over and check out our
+
+ Lucky Dip page now or you can always find the link on our home page.
diff --git a/src/lib/mastodon.ts b/src/lib/mastodon.ts
index a25de64..2ab0a2c 100644
--- a/src/lib/mastodon.ts
+++ b/src/lib/mastodon.ts
@@ -73,10 +73,11 @@ export async function getPostsPerHour(tag: string): Promise {
* Strategy:
* - Paginate until we have at least one post older than 1 hour (a complete picture),
* OR we exhaust the timeline, OR we hit MAX_PAGES_PER_HASHTAG.
- * - If the oldest fetched post is >= 1 hour old: postsPerHour = count of posts in the
- * last hour (direct measurement over a full window).
- * - If all fetched posts are within the last hour (hit page limit or timeline exhausted
- * with a narrow window): extrapolate — postsPerHour = count / (coveredHours).
+ * - Oldest post >= 1 hour old: count posts in the last hour directly (full window).
+ * - Hit the page cap (burst): more posts exist beyond what we fetched — extrapolate from
+ * the covered span (postsPerHour = count / coveredHours).
+ * - Timeline exhausted (sparse): these are all the posts that exist — use the raw count.
+ * Extrapolating would artificially inflate a tag with 3 posts clustered in 10 minutes.
*/
export async function getPostsData(
tag: string,
@@ -89,6 +90,7 @@ export async function getPostsData(
let allPosts: MastodonPost[] = []
let maxId: string | undefined
+ let hitPageCap = false
for (let page = 0; page < maxPages; page++) {
const { posts, nextMaxId } = await fetchPage(tag, maxId, postLimit)
@@ -104,6 +106,9 @@ export async function getPostsData(
if (oldestInBatch < cutoff) break
maxId = nextMaxId
+
+ // Mark if we completed the final allowed page without breaking
+ if (page === maxPages - 1) hitPageCap = true
}
if (allPosts.length === 0) return { postsPerHour: 0, relatedTags: [], hasAnyPosts: false }
@@ -116,11 +121,15 @@ export async function getPostsData(
if (oldestMs < cutoff) {
// We reached (or passed) the 1-hour horizon — count posts within the last hour directly
postsPerHour = allPosts.filter((p) => new Date(p.created_at).getTime() >= cutoff).length
- } else {
- // All posts are within the last hour (burst scenario or very sparse tag).
+ } else if (hitPageCap) {
+ // Hit the page limit — more posts likely exist beyond what we fetched (burst scenario).
// Extrapolate from the covered span. Minimum 1-minute span to avoid divide-by-zero.
const coveredMs = Math.max(newestMs - oldestMs, 60_000)
postsPerHour = allPosts.length / (coveredMs / ONE_HOUR_MS)
+ } else {
+ // Timeline exhausted — these are all the posts that exist within the last hour.
+ // Use the raw count directly; extrapolating would inflate a sparse tag.
+ postsPerHour = allPosts.length
}
// Count co-occurring tags from the API tags object (authoritative for membership)