discussion of the nullerbot project.
 help / color / mirror / code / Atom feed
fb74901f4b979d0068133b641f93105aee0d2ec8 blob 4481 bytes (raw)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
 
(defpackage nullbot
  (:use #:cl
        #:cl-hash-util)
  (:local-nicknames
   (:jzon :com.inuoe.jzon)
   (:mapi :nullbot/matrix-api)
   (:sseq :split-sequence)
   (:dex :dexador))
  (:export
   #:start))
(in-package #:nullbot)

(defclass nullbot (mapi:matrix-bot) ())
(defparameter *bot* (make-instance 'nullbot
                                   :token (uiop:getenv "NULLBOT_TOKEN")
                                   :homeserver "matrix.nullring.xyz"))

(defparameter +feed-url+ "https://list.nullring.xyz/discussion/new.atom")
(defparameter +feed-room-id+ "!ShuXi5ohrPUtKHkrNO:matrix.nullring.xyz")
(defparameter +feed-cache-path+ #P"./nullbot_cache.sexp")
(defparameter +feed-sleep-minutes+ 1)
(defparameter +weather-vancouver+ )

(defparameter +prefix+ "$")

(defun get-temp (weather-station
    &aux
       (endpoint (format nil "https://api.weather.gc.ca/collections/swob-realtime/items?f=json&lang=en&url=C~A&sortby=-date_tm-value&limit=1&properties=date_tm-value,air_temp,air_temp-uom,air_temp-qa" weather-station))
       (data (jzon:parse (dex:get endpoint))))
  (hash-get (aref (gethash "features" data) 0) '("properties" "air_temp")))

(defun process-roommsg
    (content room-id sender
     &aux
       (msgtype (gethash "msgtype" content))
       (body (gethash "body" content))
       (split-body (sseq:split-sequence #\Space body))
       (command (car split-body)))
  (format t "processing msg~%")
  (when (and (> (length body) 0) (equal (aref (car split-body) 0) #\$))
    (cond
      ((string= command "$help")
       (mapi:sendmsg *bot* room-id "Unlike some other bots, I'm nice :3"))
      ((string= command "$weather")
       (mapi:sendmsg *bot* room-id (format nil "It's ~a degrees in Vancouver~%It's ~a degrees in Victoria" (get-temp "YVR") (get-temp "YYJ")))))))

(defmethod mapi:on-event
    ((obj nullbot) event room-id
     &aux
       (msgtype (gethash "type" event))
       (sender (gethash "sender" event)))
  (cond
    ((string= msgtype "m.room.message")
     (process-roommsg (gethash "content" event) room-id sender))))

(defun node-val (obj)
  (car (xmls:node-children obj)))

(defun node-attr (obj name)
  (second (assoc name (xmls:node-attrs obj) :test #'string=)))

;; TODO: make this into a generic f-n maybe and also make it not dumb
(defun get-node-by-name (obj name)
  (check-type obj xmls:node)
  (check-type name string)
  (loop for child in (xmls:node-children obj)
        when (and (xmls:node-p child) (string= name (xmls:node-name child)))
          return child))

(defun send-entry (entry)
  (mapi:sendmsg
   *bot*
   +feed-room-id+
   (format nil "New message on mailing list!~%Title: ~a~%From: ~a~%Link: ~a~%"
           (getf entry :title)
           (getf entry :author)
           (getf entry :link))))

(defun write-entries (entries)
  (with-open-file (str +feed-cache-path+
                       :direction :output
                       :if-does-not-exist :create
                       :if-exists :supersede)
    (format str "~s" entries)))

(defun feed-thread ()
  (loop while (bt2:with-lock-held ((mapi:lock *bot*)) (mapi:listening *bot*)) do
    (format t "Doing another poll~%")
    (let* ((feed-str (dex:get +feed-url+))
           (xmlobj (xmls:parse feed-str))
           (entries (loop for entry in (xmls:node-children xmlobj)
                          when (string= (xmls:node-name entry) "entry")
                            collect `(:id ,(node-val (get-node-by-name entry "id"))
                                      :title ,(node-val (get-node-by-name entry "title"))
                                      :author ,(node-val (node-val (get-node-by-name entry "author")))
                                      :link ,(node-attr (get-node-by-name entry "link") "href"))))
           (cached-entries))

      (if (uiop:file-exists-p +feed-cache-path+)
          (setf cached-entries (read-from-string (uiop:read-file-string +feed-cache-path+)))
          (write-entries entries))

      (when cached-entries
        (loop for entry in entries
              when (not (find (getf entry :id)
                              cached-entries
                              :test #'string=
                              :key (lambda (e) (getf e :id))))
                do (send-entry entry)))
      ;; update the cache with the new entries
      (write-entries entries))
    (sleep (* 60 +feed-sleep-minutes+))))

(defun start ()
  (bt2:make-thread #'feed-thread :name "nullbot polling thread")
  (mapi:start *bot*))
debug log:

solving fb74901 ...
found fb74901 in https://git.nullring.xyz/nullerbot.git

Code repositories for project(s) associated with this public inbox

	https://git.nullring.xyz/nullerbot.git
	https://git.nullring.xyz/nullerbot.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox