#lang racket/base (require racket/unix-socket json racket/list racket/os) (provide connect-discord update-presence) (define ipc-out #f) (define (get-socket-path) (define base-dirs (list (getenv "XDG_RUNTIME_DIR") (getenv "TMPDIR") "/tmp" (build-path "/run/user" (number->string (getpid))))) (for/first ([dir base-dirs] #:when dir [i (in-range 10)] #:do [(define p (build-path dir (format "discord-ipc-~a" i)))] #:when (file-exists? p)) p)) (define (write-packet opcode payload) (when ipc-out (define json-bytes (string->bytes/utf-8 (jsexpr->string payload))) (define len (bytes-length json-bytes)) (write-bytes (integer->integer-bytes opcode 4 #f #f) ipc-out) ; Opcode (write-bytes (integer->integer-bytes len 4 #f #f) ipc-out) ; Length (write-bytes json-bytes ipc-out) (flush-output ipc-out))) (define (connect-discord client-id) (define path (get-socket-path)) (if path (with-handlers ([exn:fail? (lambda (e) (printf "Discord connection failed.\n"))]) (define-values (in out) (unix-socket-connect path)) (set! ipc-out out) (write-packet 0 (hash 'v 1 'client_id client-id)) (printf "Connected to Discord at ~a\n" path)) (printf "Discord not found.\n"))) (define (update-presence details state #:start [start-timestamp #f] #:small-text [small-text #f]) (define activity (hash 'details details 'state state 'assets (hash 'large_image "racket-logo-ezgif_com-svg-to-png-converter" 'large_text "DrRacket" 'small_image (if small-text "lambda" #f) 'small_text small-text) 'timestamps (if start-timestamp (hash 'start start-timestamp) #f))) (write-packet 1 (hash 'cmd "SET_ACTIVITY" 'nonce (number->string (current-milliseconds)) 'args (hash 'pid (getpid) 'activity activity))))