107 lines
3.9 KiB
Common Lisp
107 lines
3.9 KiB
Common Lisp
;;;; df-bug-bot.lisp
|
|
|
|
(in-package #:df-bug-bot)
|
|
|
|
(declaim (inline reinitizalize-random-state post-poll-p get-random-bug))
|
|
|
|
(defvar *csv-file* #P"df-bugs.csv"
|
|
"path to our CSV file")
|
|
(defvar *parsed-csv* nil
|
|
"a list that contains our parsed CSV")
|
|
(defvar *poll-chances* 0.25
|
|
"the chances for posting a status with a poll")
|
|
(defvar *csv-regex* "(?m)^[0-9]{7,}.+,(open|reopened|unable to reproduce|not fixable|suspended|won't fix),.+$"
|
|
"regex used for pre-processing the downloaded CSV file")
|
|
(defvar *recent-ids* nil
|
|
"a list containing the 5 most recent bug ids")
|
|
(defvar *bug-tracker-url* "https://dwarffortressbugtracker.com/csv_export.php"
|
|
"link to the DF bug tracker CSV export")
|
|
|
|
(defun reinitizalize-random-state ()
|
|
"reinitializes the random state with a new one"
|
|
(setf *random-state* (make-random-state t)))
|
|
|
|
(defun post-poll-p ()
|
|
"checks if we should post a poll"
|
|
(>= *poll-chances* (random 1.0)))
|
|
|
|
(defun parse-csv (csv)
|
|
"transforms the CSV to ensure that it meets our specs"
|
|
;; remove all lines that arent actual bugs
|
|
(ppcre:all-matches-as-strings *csv-regex*
|
|
;; remove all non-terminated strings (to appease cl-csv
|
|
(ppcre:regex-replace-all "(?m)\".+$" csv "")))
|
|
|
|
(defun refresh-csv-file ()
|
|
"downloads the bug CSV file and writes it to *CSV-FILE*"
|
|
(let ((csv-string (drakma:http-request *bug-tracker-url*)))
|
|
(str:to-file *csv-file*
|
|
(str:join (string #\newline)
|
|
(parse-csv csv-string)))
|
|
(setf *parsed-csv* (cl-csv:read-csv *csv-file*))))
|
|
|
|
(defun get-random-bug ()
|
|
"gets a random bug from the list"
|
|
(nth (random (length *parsed-csv*)) *parsed-csv*))
|
|
|
|
(defun generate-post ()
|
|
"fetches a random bug and generates a post with it"
|
|
(loop :with bug := (get-random-bug)
|
|
|
|
;; loops until we meet the following criteria:
|
|
;; 1- the bug hasnt been posted recently (last 5 posts)
|
|
;; 2- the bug text itself exists
|
|
;; 3- the bug text isnt an empty string
|
|
:until (and (not (member (nth 0 bug) *recent-ids* :test #'string=))
|
|
(nth 11 bug)
|
|
(not (str:emptyp (nth 11 bug))))
|
|
:do (setf bug (get-random-bug))
|
|
|
|
;; when we finally have a new bug we push the
|
|
;; bug id into the recent-id list and make sure that
|
|
;; we only have the most recent ids cached.
|
|
;; then we redo the random state just for kicks
|
|
;; before returning the text for the post
|
|
:finally
|
|
(push (nth 0 bug) *recent-ids*)
|
|
(setf *recent-ids* (subseq *recent-ids* 0 (min (length *recent-ids*) 5)))
|
|
(reinitizalize-random-state)
|
|
(return (format nil "~A: ~A" (nth 0 bug) (nth 11 bug)))))
|
|
|
|
(defun main ()
|
|
"main binary entry point"
|
|
|
|
(reinitizalize-random-state)
|
|
|
|
;; download our csv file if it doesnt exist
|
|
;; if its already downloaded go ahead and parse it
|
|
(if (uiop:file-exists-p *csv-file*)
|
|
(setf *parsed-csv* (cl-csv:read-csv *csv-file*))
|
|
(refresh-csv-file))
|
|
|
|
(handler-case
|
|
(with-user-abort
|
|
(run-bot ((make-instance 'mastodon-bot :config-file "config.file")
|
|
:with-websocket nil)
|
|
|
|
;; after every 4 days (and 13 min) refresh the bug list
|
|
;; this should hopefully not overlap with the posting,
|
|
;; because that might mess things up
|
|
(after-every ((time-to-seconds 4 :days 13 :minutes) :seconds :async t)
|
|
(refresh-csv-file))
|
|
|
|
;; post a random bug every hour
|
|
(after-every (1 :hour :run-immediately t)
|
|
(let ((poll? (post-poll-p)))
|
|
(post (generate-post)
|
|
:poll-options (when poll? '("Bug?" "Feature?" "Normal dwarven behavior?"))
|
|
:poll-timeout (when poll? 3600))))))
|
|
|
|
(error (e)
|
|
(format t "~A~%" e)
|
|
(uiop:quit))
|
|
|
|
(user-abort ()
|
|
(uiop:quit))))
|
|
|