diff options
Diffstat (limited to 'reporter.lisp')
-rw-r--r-- | reporter.lisp | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/reporter.lisp b/reporter.lisp new file mode 100644 index 0000000..48ec0fa --- /dev/null +++ b/reporter.lisp @@ -0,0 +1,96 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NewsPhaser, Mastodon posts aggregator +;; Copyright (C) 2025 Gene Pasquet +;; +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(in-package :phaser) + +(defvar *timeline-page-size* 50 + "Maximum number of statuses retrieved from the timeline for each request") +(defvar *timeline-min-age* (* 24 3600) + "Minimum age of statuses before being considered for ranking") +(defvar *timeline-max-age* (* 48 3600) ; 48h + "Maximum age of statuses to be retrieved from timelines.") + +(defvar *timeline-min-timestamp* (- (get-universal-time) *timeline-max-age*)) +(defvar *timeline-max-timestamp* (- (get-universal-time) *timeline-min-age*)) + +(defvar *pop-favs-factor* 1) +(defvar *pop-boosts-factor* 1) +(defvar *pop-replies-factor* 1) +(defvar *max-requests* 20) + +(defvar *top-statuses-to-boost* 10) + +(defun outdated-statuses (page) + (remove-if + (lambda (status) + (> (tooter:created-at status) *timeline-min-timestamp*)) + page)) + +(defun last-page-p (page) + (or (null page) + (not (null (outdated-statuses page))))) + +(defun load-timeline-pages (min-id &optional (pages '()) (iter-num 0)) + "Load statuses from the timeline up to *TIMELINE-MAX-AGE* or MIN-ID." + (let* ((max-id (if (null pages) + nil + (tooter:id (car (last (car pages)))))) + (page (tooter:timeline *client* :home + :limit *timeline-page-size* + :max-id max-id + :min-id min-id)) + (statuses-and-page (if (null pages) + (list page) + (cons page pages)))) + (if (or (last-page-p page) (>= iter-num *max-requests*)) + (alexandria:flatten statuses-and-page) + (load-timeline-pages min-id statuses-and-page (1+ iter-num))))) + +(defun load-timeline () + (remove-if (lambda (status) + (let ((status-timestamp (tooter:created-at status))) + (or (< status-timestamp *timeline-min-timestamp*) + (> status-timestamp *timeline-max-timestamp*)))) + (load-timeline-pages nil))) + +(defun status-popularity (status) + (/ (+ (* (tooter:replies-count status) *pop-replies-factor*) + (* (tooter:favourites-count status) *pop-favs-factor*) + (* (tooter:reblogs-count status) *pop-boosts-factor*)) + (- (get-universal-time) (tooter:created-at status)))) + +(defun load-top-statuses () + (let* ((timeline (load-timeline)) + (ranked-statuses (sort (mapcar (lambda (status) + `(,(status-popularity status) . ,status)) + timeline) + '> :key 'car))) + (mapcar 'cdr (butlast ranked-statuses (- (length ranked-statuses) 10))))) + + +(defun summarize-top-statuses () + (let ((statuses (load-top-statuses))) + (mapcar (lambda (status) + (text-excerpt (tooter:content status))) + statuses))) + +(defun reblog-top-statuses () + (let ((top-statuses (load-top-statuses))) + (mapc (lambda (status) + (tooter:favourite *client* status)) + top-statuses))) |