2023-02-13 10:35:15 -06:00
|
|
|
;; Copyright 2023, Jaidyn Levesque <jadedctrl@posteo.at>
|
2023-01-08 18:51:36 -06:00
|
|
|
;;
|
|
|
|
;; 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/>.
|
|
|
|
;;
|
|
|
|
|
|
|
|
(import scheme
|
2023-02-08 10:31:56 -06:00
|
|
|
(chicken file) (chicken file posix) (chicken io) (chicken keyword) (chicken random)
|
2023-02-13 10:35:15 -06:00
|
|
|
(chicken pathname)
|
2023-01-10 22:59:57 -06:00
|
|
|
(chicken pretty-print) (chicken process-context)
|
2023-01-08 18:51:36 -06:00
|
|
|
(chicken process-context posix) (chicken string)
|
2023-02-13 10:35:15 -06:00
|
|
|
srfi-1 srfi-13 srfi-18 srfi-19 srfi-69 srfi-180
|
|
|
|
inotify
|
2023-02-08 10:31:56 -06:00
|
|
|
xattr
|
2023-01-08 18:51:36 -06:00
|
|
|
getopt-long)
|
|
|
|
|
|
|
|
|
2023-02-13 10:35:15 -06:00
|
|
|
;; Wrapper around `directory` that lists a dir's files as a relative path
|
|
|
|
(define (directory-rel #!optional (path "./"))
|
|
|
|
(let ([relative-parent (normalize-pathname (string-append path "/"))])
|
|
|
|
(map (lambda (leaf)
|
|
|
|
(string-append relative-parent leaf))
|
|
|
|
(directory path))))
|
|
|
|
|
|
|
|
|
|
|
|
;; Returns an inotify watch-descriptor according the given path
|
|
|
|
(define (path->wd path)
|
|
|
|
(car
|
|
|
|
(filter (lambda (wd)
|
|
|
|
(string=? (normalize-pathname (wd->path wd))
|
|
|
|
(normalize-pathname path)))
|
|
|
|
(wd-list))))
|
|
|
|
|
|
|
|
|
|
|
|
;; Attempt to remove an inotify watch; if it's already been removed, no sweat
|
|
|
|
;; (This happens sometimes when inotify automatically deletes a watch)
|
|
|
|
(define (attempt-remove-watch! watch)
|
|
|
|
(handle-exceptions exn
|
|
|
|
#t
|
|
|
|
(remove-watch! watch)))
|
|
|
|
|
|
|
|
|
2023-04-29 19:53:22 -05:00
|
|
|
;; Returns the path of a room's directory
|
2023-02-08 10:31:56 -06:00
|
|
|
(define (channel-directory-path conn channel)
|
2023-04-29 19:53:22 -05:00
|
|
|
(let ([dir (hash-table-ref conn 'directory)])
|
|
|
|
(if (and (string? dir) (string? channel))
|
|
|
|
(string-append dir "/" channel "/"))))
|
2023-02-08 10:31:56 -06:00
|
|
|
|
|
|
|
|
|
|
|
;; Returns the .users/ path of a channel
|
2023-02-08 11:26:01 -06:00
|
|
|
(define (channel-users-directory-path conn channel)
|
2023-02-08 10:31:56 -06:00
|
|
|
(string-append (channel-directory-path conn channel)
|
|
|
|
".users/"))
|
|
|
|
|
|
|
|
|
2023-02-08 11:26:01 -06:00
|
|
|
;; Main directory path of the given user
|
|
|
|
(define (channel-user-directory-path conn channel hostmask #!optional (state "all"))
|
|
|
|
(string-append (channel-users-directory-path conn channel)
|
|
|
|
state "/" (irc:hostmask-nick hostmask)))
|
|
|
|
|
|
|
|
|
|
|
|
;; Main directory path of the given user
|
|
|
|
(define (user-directory-path conn channel hostmask)
|
|
|
|
(string-append (channel-users-directory-path conn channel)
|
|
|
|
"all/" hostmask))
|
|
|
|
|
|
|
|
|
2023-02-08 10:31:56 -06:00
|
|
|
;; Tidies up a channel directory; removes `online` and `offline` user links, etc.
|
|
|
|
(define (cleanup-channel conn channel)
|
2023-02-08 11:26:01 -06:00
|
|
|
(let ([users-dir (channel-users-directory-path conn channel)])
|
|
|
|
(map
|
|
|
|
(lambda (state-dir)
|
|
|
|
(if (not (substring-index state-dir "/all"))
|
|
|
|
(map
|
|
|
|
(lambda (link)
|
|
|
|
(let ([link-path (string-append users-dir state-dir "/" link)])
|
|
|
|
(if (symbolic-link? link-path)
|
|
|
|
(delete-file link-path))))
|
|
|
|
(directory (string-append users-dir state-dir)))))
|
|
|
|
(directory users-dir))))
|
|
|
|
|
2023-02-08 10:31:56 -06:00
|
|
|
|
|
|
|
;; Creates a channel's file hierarchy, if need be
|
|
|
|
(define (make-channel conn channel)
|
2023-02-13 10:35:15 -06:00
|
|
|
(let* ([path (channel-directory-path conn channel)]
|
|
|
|
[subpath (lambda (leaf) (string-append path leaf))])
|
|
|
|
(create-directory (subpath ".in") #t)
|
|
|
|
(create-directory (subpath ".users/online") #t)
|
|
|
|
(create-directory (subpath ".users/offline") #t)
|
|
|
|
(create-directory (subpath ".users/all") #t)
|
2023-02-08 10:31:56 -06:00
|
|
|
(cleanup-channel conn channel)))
|
|
|
|
|
|
|
|
|
|
|
|
;; Creates a user's info files in the given channel, if need bee
|
|
|
|
(define (make-user conn channel hostmask)
|
2023-02-08 11:26:01 -06:00
|
|
|
(create-directory (user-directory-path conn channel hostmask) #t))
|
|
|
|
|
|
|
|
|
|
|
|
;; Disables a user-state (that is, removes a symlink from a .users directory
|
|
|
|
(define (user-disable-state conn channel hostmask state)
|
2023-02-13 10:35:15 -06:00
|
|
|
(let ([state-link
|
|
|
|
(create-directory (channel-user-directory-path conn channel hostmask state) #t)])
|
2023-02-08 11:26:01 -06:00
|
|
|
(if (or (file-exists? state-link)
|
|
|
|
(symbolic-link? state-link))
|
|
|
|
(delete-file state-link))))
|
|
|
|
|
|
|
|
|
|
|
|
;; Enables a user-state (that is, makes a symlink to a .users directory
|
|
|
|
(define (user-enable-state conn channel hostmask state)
|
2023-02-13 10:35:15 -06:00
|
|
|
(let ([state-link
|
|
|
|
(create-directory (channel-user-directory-path conn channel hostmask state) #t)])
|
2023-02-08 11:26:01 -06:00
|
|
|
(if (not (or (file-exists? state-link)
|
|
|
|
(symbolic-link? state-link)))
|
|
|
|
(create-symbolic-link (string-append "../all/" hostmask)
|
|
|
|
state-link))))
|
2023-02-08 10:31:56 -06:00
|
|
|
|
|
|
|
|
2023-02-08 11:26:01 -06:00
|
|
|
;; Ensures the enabled-state is enabled, and it's opposite (disabled-state) is not
|
|
|
|
(define (user-toggle-state conn channel hostmask enabled-state disabled-state)
|
|
|
|
(user-disable-state conn channel hostmask disabled-state)
|
|
|
|
(user-enable-state conn channel hostmask enabled-state))
|
2023-02-08 10:31:56 -06:00
|
|
|
|
|
|
|
|
2023-04-29 19:53:22 -05:00
|
|
|
|
2023-02-08 10:31:56 -06:00
|
|
|
|
|
|
|
|
|
|
|
;; Create a message file; putting metadata in xattrs, and text directly in the file
|
|
|
|
(define (make-message-file conn channel sender message)
|
2023-04-29 19:53:22 -05:00
|
|
|
(if (and message (string? message) channel (string? channel))
|
|
|
|
(let ([file (message-file-path conn channel)])
|
|
|
|
(call-with-output-file file
|
|
|
|
(lambda (out-port) (write-string message #f out-port)))
|
|
|
|
(set-xattr file "user.chat.sender" sender)
|
|
|
|
(set-xattr file "user.chat.date" (date->string (current-date) "~1T~2"))
|
|
|
|
(set-xattr file "user.chat.channel" channel)
|
|
|
|
(set-xattr file "user.chat.mime" "text/plain"))))
|
2023-02-08 10:31:56 -06:00
|
|
|
|
|
|
|
|
|
|
|
;; Sets a channel's .topic file
|
|
|
|
(define (set-channel-topic conn channel topic #!optional (username #f) (date #f))
|
|
|
|
(let ([topic-path (string-append (channel-directory-path conn channel)
|
|
|
|
".topic")])
|
|
|
|
(if (string? topic)
|
|
|
|
(call-with-output-file
|
|
|
|
topic-path
|
|
|
|
(lambda (out-port)
|
|
|
|
(write-string topic #f out-port))))
|
|
|
|
(if username
|
|
|
|
(set-xattr topic-path "user.chat.sender" (irc:hostmask-nick username)))))
|
2023-01-10 22:59:57 -06:00
|
|
|
|
|
|
|
|
2023-02-13 11:15:38 -06:00
|
|
|
;; Send message to an IRC channel
|
|
|
|
(define (send-message connection channel message)
|
|
|
|
(irc:write-cmd connection "PRIVMSG" channel message)
|
|
|
|
(make-message-file connection channel
|
|
|
|
(hash-table-ref connection 'nick)
|
|
|
|
message))
|
|
|
|
|
|
|
|
|
2023-01-10 22:59:57 -06:00
|
|
|
;; Hook function for irc:loop; handles all IRC commands
|
2023-01-08 18:51:36 -06:00
|
|
|
(define (on-command conn cmd params #!optional sender)
|
2023-01-10 22:59:57 -06:00
|
|
|
(cond
|
2023-01-15 20:46:23 -06:00
|
|
|
[(and (string=? cmd "PRIVMSG")
|
|
|
|
(string? sender)
|
|
|
|
(irc:hostmask? sender))
|
2023-01-14 14:25:32 -06:00
|
|
|
(let ([target (if (irc:user-is-self? conn (car params))
|
|
|
|
(irc:hostmask-nick sender)
|
|
|
|
(car params))])
|
2023-02-08 10:31:56 -06:00
|
|
|
(make-message-file conn target (irc:hostmask-nick sender) (last params)))]
|
2023-01-15 20:46:23 -06:00
|
|
|
|
|
|
|
[(or (string=? cmd "NOTICE")
|
|
|
|
(and (string=? cmd "PRIVMSG")
|
|
|
|
(or (string-null? sender) (not (irc:hostmask? sender)))))
|
2023-02-08 10:31:56 -06:00
|
|
|
(make-message-file conn ".server" "server" (last params))]
|
|
|
|
|
|
|
|
[(and (string=? cmd "JOIN") (irc:user-is-self? conn sender))
|
|
|
|
(make-channel conn (last params))]
|
2023-01-15 20:46:23 -06:00
|
|
|
|
2023-01-11 16:40:29 -06:00
|
|
|
[(string=? cmd "JOIN")
|
2023-02-08 10:31:56 -06:00
|
|
|
(make-user conn (last params) sender)]
|
2023-01-15 20:46:23 -06:00
|
|
|
|
2023-02-08 10:31:56 -06:00
|
|
|
;; [(string=? cmd "NICK")
|
|
|
|
;; (chatd-json-write conn
|
|
|
|
;; (compose-event-alist conn "user-info" #:user (last params)))])
|
|
|
|
))
|
2023-01-08 18:51:36 -06:00
|
|
|
|
|
|
|
|
2023-01-10 22:59:57 -06:00
|
|
|
;; Hook function for irc:loop; handles all IRC errors and replies
|
2023-01-08 18:51:36 -06:00
|
|
|
(define (on-reply conn reply params #!optional sender)
|
|
|
|
(cond
|
2023-02-08 10:31:56 -06:00
|
|
|
;; If topic set, output to a channel's .topic file
|
|
|
|
[(and (eq? reply RPL_TOPIC)
|
|
|
|
(irc:channel? (second params)))
|
|
|
|
(set-channel-topic conn (second params) (last params))]
|
|
|
|
|
|
|
|
[(and (eq? reply RPL_TOPICWHOTIME)
|
|
|
|
(irc:channel? (second params)))
|
|
|
|
(set-channel-topic conn (second params) #f (third params) (last params))]
|
|
|
|
|
|
|
|
;; We've got to add users, when they join the room!
|
|
|
|
[(or (and (irc:capability? conn 'userhost-in-names)
|
|
|
|
(eq? reply RPL_ENDOFNAMES))
|
|
|
|
(eq? reply RPL_ENDOFWHO))
|
|
|
|
(map (lambda (nick)
|
2023-02-08 11:26:01 -06:00
|
|
|
(let ([hostmask (irc:user-get conn nick 'hostmask)]
|
|
|
|
[channel (second params)])
|
|
|
|
(make-user conn channel hostmask)
|
|
|
|
(user-toggle-state conn channel hostmask "online" "offline")))
|
2023-02-08 10:31:56 -06:00
|
|
|
(irc:channel-users conn (second params)))]
|
|
|
|
|
|
|
|
[#t
|
|
|
|
(make-message-file conn ".server" "server" (last params))]))
|
2023-01-08 18:51:36 -06:00
|
|
|
|
|
|
|
|
2023-04-29 19:53:22 -05:00
|
|
|
(define (write-string-to-file file value)
|
|
|
|
(call-with-output-file file
|
|
|
|
(lambda (out-port)
|
|
|
|
(write-string value #f out-port))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-port-to-file path in-port)
|
|
|
|
(call-with-output-file path
|
|
|
|
(lambda (out-port)
|
|
|
|
(copy-port in-port out-port read-byte write-byte))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (write-byte-list-to-file path byte-list)
|
|
|
|
(call-with-output-file path
|
|
|
|
(lambda (out-port)
|
|
|
|
(map (lambda (byte)
|
|
|
|
(write-char byte out-port))
|
|
|
|
byte-list))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (read-file-to-string file)
|
|
|
|
(call-with-input-file file
|
|
|
|
(lambda (in-port)
|
|
|
|
(read-string #f in-port))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (directory-file-set! directory key value #!optional (xattr-alist '()))
|
|
|
|
(let ([path (subpath directory key)])
|
|
|
|
;; Write the xattrs (if applicable)
|
|
|
|
(map (lambda (xattr-cons)
|
|
|
|
(set-xattr path (symbol->string (car xattr-cons))
|
|
|
|
(cdr xattr-cons)))
|
|
|
|
xattr-alist)
|
|
|
|
|
|
|
|
;; Write the contents (value)
|
|
|
|
(cond [(string? value)
|
|
|
|
(write-string-to-file path value)]
|
|
|
|
[(input-port? value)
|
|
|
|
(write-port-to-file path value)]
|
|
|
|
[(list? value)
|
|
|
|
(write-byte-list-to-file path value)])))
|
|
|
|
|
|
|
|
|
|
|
|
(define (directory-file-get directory key)
|
|
|
|
(read-file-to-string (subpath directory key)))
|
|
|
|
|
|
|
|
|
|
|
|
;; Get the contents of the given file as a string, including the
|
|
|
|
(define (directory-file-get* directory key)
|
|
|
|
(let ([path (subpath directory key)])
|
|
|
|
(cons (directory-file-get directory key)
|
|
|
|
(map (lambda (xattr)
|
|
|
|
(cons (string->symbol xattr)
|
|
|
|
(get-xattr path xattr)))
|
|
|
|
(list-xattrs path)))))
|
|
|
|
|
|
|
|
|
|
|
|
;; Sets a channel's metadata value; that is, sets the contents of the file
|
|
|
|
;; /$channel/.meta/$key to $value.
|
|
|
|
(define (channel-metadata-set! root channel key value #!optional (xattr-alist '()))
|
|
|
|
(directory-file-set! (subpath root channel ".meta")
|
|
|
|
key value
|
|
|
|
xattr-alist))
|
|
|
|
|
|
|
|
|
|
|
|
;; Return a specific bit of metadata of a channel, as a string
|
|
|
|
(define (channel-metadata-get root channel key)
|
|
|
|
(directory-file-get (subpath root channel ".meta") key))
|
|
|
|
|
|
|
|
|
|
|
|
;; Return a cons-list of a channel's metadata, with the file-content followed by
|
|
|
|
;; an alist of the extended attributes
|
|
|
|
(define (channel-metadata-get* root channel key)
|
|
|
|
(directory-file-get* (subpath root channel ".meta") key))
|
|
|
|
|
|
|
|
|
|
|
|
;; Return a file path with the given parameters as elements of the path
|
|
|
|
;; E.g., "/etc/", "/systemd/user" "mom" => "/etc/systemd/user/mom"
|
|
|
|
(define (subpath . children)
|
|
|
|
(normalize-pathname
|
|
|
|
(reduce-right (lambda (a b)
|
|
|
|
(string-append a "/" b))
|
|
|
|
"" children)))
|
|
|
|
|
|
|
|
|
|
|
|
;; Returns the appropriate, non-colliding file path of a hypothetical message
|
|
|
|
(define (message-file-path parent date #!optional (suffix ""))
|
|
|
|
(let ([path
|
|
|
|
(string-append (channel-directory-path conn channel)
|
|
|
|
(date->string (current-date) "[~m-~d] ~H:~M:~S")
|
|
|
|
suffix)])
|
|
|
|
(if (file-exists? path)
|
|
|
|
(message-file-path conn channel
|
|
|
|
(number->string (+ (or (string->number suffix) 0) .1)))
|
|
|
|
path)))
|
|
|
|
|
|
|
|
(define (channel-add-message root channel contents #!optional (sender #f) (date #f))
|
|
|
|
(directory-file-set! (message-file-path (subpath root channel))))
|
2023-02-13 10:35:15 -06:00
|
|
|
|
|
|
|
|
|
|
|
;; Initialization for the input loop
|
2023-04-29 19:53:22 -05:00
|
|
|
(define (input-loop-init root-dir callbacks-alist)
|
|
|
|
(let ([join-callback (alist-ref 'join-channel callbacks-alist)])
|
2023-02-13 10:35:15 -06:00
|
|
|
(init!)
|
2023-04-29 19:53:22 -05:00
|
|
|
;; Start watching the chatdir (for new channel joins, etc)
|
|
|
|
(add-watch! root-dir
|
2023-02-13 10:35:15 -06:00
|
|
|
'(onlydir moved-to moved-from delete delete-self create))
|
|
|
|
|
2023-04-29 19:53:22 -05:00
|
|
|
;; Auto-join channels with all pre-existing channel directories
|
2023-02-13 10:35:15 -06:00
|
|
|
(map (lambda (path)
|
2023-04-29 19:53:22 -05:00
|
|
|
(let ([channel-dirname (pathname-file (pathname-directory (string-append path "/")))]
|
|
|
|
[join-callback (alist-ref 'join-channel callbacks-alist)])
|
|
|
|
(if join-callback
|
|
|
|
(apply join-callback (list channel-dirname)))
|
2023-02-13 10:35:15 -06:00
|
|
|
|
2023-04-29 19:53:22 -05:00
|
|
|
(add-watch! in-path '(moved-to close-write))
|
|
|
|
(print "Joined and watching: " in-path)))
|
|
|
|
(filter directory-exists? (directory-rel irc-dir)))))
|
2023-02-13 10:35:15 -06:00
|
|
|
|
|
|
|
|
|
|
|
;; Handles all inotify-watched file events from the top-level IRC-directory.
|
|
|
|
;; Mainly, checking for newly-joined or left channels.
|
2023-04-29 19:53:22 -05:00
|
|
|
(define (handle-main-dir-event callbacks-alist event)
|
|
|
|
(let ([flags (event-flags event)]
|
|
|
|
[leave-callback (alist-ref 'leave-channel callbacks-alist)]
|
|
|
|
[join-callback (alist-ref 'join-channel callbacks-alist)])
|
2023-02-13 10:35:15 -06:00
|
|
|
(cond
|
|
|
|
;; If a channel dir's been moved or removed, stop watching (ofc)
|
|
|
|
;; … Also quit that room! Heck them!
|
|
|
|
[(or (member 'moved-from flags)
|
|
|
|
(member 'delete flags)
|
|
|
|
(member 'delete-self flags))
|
|
|
|
(let* ([channel (event-name event)]
|
|
|
|
[channel-inpath (string-append (wd->path (event-wd event)) channel "/.in")]
|
|
|
|
[channel-wd (path->wd channel-inpath)])
|
|
|
|
(print "Remove watch for " channel-inpath "…")
|
|
|
|
(if (and channel-wd (member channel-wd (wd-list)))
|
|
|
|
(attempt-remove-watch! channel-wd))
|
2023-04-29 19:53:22 -05:00
|
|
|
(if leave-callback
|
|
|
|
(apply leave-callback (list channel))))]
|
2023-02-13 10:35:15 -06:00
|
|
|
|
|
|
|
;; If a dir's been created for a channel, maybe-join, then watch input!
|
|
|
|
[(or (member 'create flags)
|
|
|
|
(member 'moved-to flags))
|
2023-04-29 19:53:22 -05:00
|
|
|
(let* ([channel (event->pathname event)])
|
|
|
|
(print "Attempting to join channel " dirname "…")
|
|
|
|
(if join-callback
|
|
|
|
(apply join-callback (list path))))])))
|
2023-02-13 10:35:15 -06:00
|
|
|
|
2023-04-29 19:53:22 -05:00
|
|
|
|
|
|
|
(define (channel-joined root-dir channel)
|
|
|
|
(let* ([in-path (normalize-pathname (string-append root-dir "/" channel "/.in"))])
|
|
|
|
(add-watch! in-path '(moved-to close-write))
|
|
|
|
(print "Began watching input " in-path ".")))
|
2023-02-13 10:35:15 -06:00
|
|
|
|
|
|
|
|
|
|
|
;; Handles an inotify event that pertains to a channel's .in/ directory
|
2023-04-29 19:53:22 -05:00
|
|
|
(define (handle-channel-dir-event callbacks-alist event)
|
2023-02-13 10:35:15 -06:00
|
|
|
(let* ([event-dir (pathname-directory (event->pathname event))]
|
2023-02-13 11:15:38 -06:00
|
|
|
[dirname (pathname-file event-dir)]
|
2023-04-29 19:53:22 -05:00
|
|
|
[channel (pathname-file (pathname-directory event-dir))]
|
|
|
|
[send-message-callback (alist-ref 'send-message callbacks-alsit)])
|
2023-02-13 10:35:15 -06:00
|
|
|
(cond
|
2023-04-29 19:53:22 -05:00
|
|
|
;; If input is given to an `.in` dir… well, send that darn message!
|
|
|
|
;; What're you wating for?
|
2023-02-13 10:35:15 -06:00
|
|
|
[(and (string=? dirname ".in")
|
2023-04-29 19:53:22 -05:00
|
|
|
send-message-callback)
|
2023-02-13 11:15:38 -06:00
|
|
|
(map (lambda (message)
|
2023-04-29 19:53:22 -05:00
|
|
|
(apply send-message (list channel message)))
|
2023-02-13 11:15:38 -06:00
|
|
|
(with-input-from-file (event->pathname event)
|
|
|
|
read-lines))
|
2023-04-29 19:53:22 -05:00
|
|
|
(delete-file* (event->pathname event))])))
|
2023-02-13 10:35:15 -06:00
|
|
|
|
|
|
|
|
|
|
|
;; Handle a single inotify file event, as part of the input loop
|
2023-04-29 19:53:22 -05:00
|
|
|
(define (handle-file-event root-dir callbacks-alist event)
|
2023-02-13 10:35:15 -06:00
|
|
|
(if (not (member 'ignored (event-flags event)))
|
|
|
|
(let* ([flags (event-flags event)]
|
|
|
|
[wd-path (wd->path (event-wd event))]
|
2023-04-29 19:53:22 -05:00
|
|
|
[main-dir? (string=? wd-path root-dir)])
|
2023-02-13 10:35:15 -06:00
|
|
|
(if main-dir?
|
2023-04-29 19:53:22 -05:00
|
|
|
(handle-main-dir-event root-dir callbacks-alist event)
|
|
|
|
(handle-channel-dir-event root-dir callbacks-alist event)))))
|
2023-02-13 10:35:15 -06:00
|
|
|
|
|
|
|
|
|
|
|
;; The FS-backed input loop, to be run in a seperate thread (so as to not block)
|
|
|
|
;; This handles channel leaving/joining, and sending messages
|
2023-04-29 19:53:22 -05:00
|
|
|
;; Call-backs that should be provided:
|
|
|
|
;; (channel-joined channel)
|
|
|
|
;; (new-message channel text)
|
|
|
|
(define (input-loop root-dir callbacks-alist)
|
|
|
|
(map (lambda (event)
|
|
|
|
(handle-file-event root-dir callbacks-alist event))
|
2023-02-13 10:35:15 -06:00
|
|
|
(next-events!))
|
|
|
|
|
2023-04-29 19:53:22 -05:00
|
|
|
(input-loop root-dir callbacks-alist))
|