Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #lang racket
- (require json)
- (require net/http-client)
- (require net/url)
- (require net/rfc6455)
- (define config (string->jsexpr (file->string "config.json")))
- (define (get-gateway-url)
- (define-values (http-response http-headers input)
- (http-sendrecv "discordapp.com" "/api/gateway" #:ssl? #t #:version "1.1" #:method "GET"))
- (combine-url/relative (string->url (hash-ref (read-json input) 'url)) "/"))
- (define gateway-url (get-gateway-url))
- (define ws (ws-connect gateway-url))
- (define last-seq null)
- (define heartbeat-thread
- (thread
- (lambda ()
- (let loop [(interval null)]
- (cond [(null? interval) (sleep 0.1)
- (loop (thread-receive))]
- [(number? interval) (sleep (/ interval 1000))
- (println "Sending heartbeat!")
- (ws-send! ws (jsexpr->string (hasheq 'op 1
- 'd last-seq)))
- (loop (let [(msg (thread-try-receive))]
- (if msg (loop msg) (loop interval))))])))))
- (define (send-message-with-file! content file-path channel-id)
- (println (string-append "https://www.rateless.net/~dormo/emotes/" (path->string file-path)))
- (http-sendrecv "discordapp.com"
- (string-append "/api/channels/" channel-id "/messages")
- #:ssl? #t
- #:version "1.1"
- #:method "POST"
- #:headers (list (string-append "Authorization: Bot " (hash-ref config 'token))
- "User-Agent: RatelessBot (https://rateless.net, v0.1)"
- "Content-Type: application/json")
- #:data (jsexpr->string (hasheq 'content content
- 'embed (hasheq 'image (hasheq 'url (string-append "https://www.rateless.net/~dormo/emotes/" (path->string file-path))))
- 'nonce (number->string (current-inexact-milliseconds))
- 'tts #f))))
- (define (send-message! content channel-id)
- (http-sendrecv "discordapp.com"
- (string-append "/api/channels/" channel-id "/messages")
- #:ssl? #t
- #:version "1.1"
- #:method "POST"
- #:headers (list (string-append "Authorization: Bot " (hash-ref config 'token))
- "User-Agent: RatelessBot (https://rateless.net, v0.1)"
- "Content-Type: application/json")
- #:data (jsexpr->string (hasheq 'content content
- 'nonce (number->string (current-inexact-milliseconds))
- 'tts #f))))
- (define (handle-event res)
- (cond [(string=? "MESSAGE_CREATE" (hash-ref res 't))
- (cond [(string=? "." (substring (hash-ref (hash-ref res 'd) 'content) 0 1))
- (let* [(channel-id (hash-ref (hash-ref res 'd) 'channel_id))
- (emote-name (substring (hash-ref (hash-ref res 'd) 'content) 1 (string-length (hash-ref (hash-ref res 'd) 'content))))
- (files (filter (lambda (x) (string=? (path->string (path-replace-extension x "")) emote-name)) (directory-list "emotes/")))]
- (if (< 0 (length files))
- (send-message-with-file! (substring (hash-ref (hash-ref res 'd) 'content) 1 (string-length (hash-ref (hash-ref res 'd) 'content)))
- (first files)
- channel-id)
- (send-message! "That emote doesn't exist" channel-id)))])]))
- (define (route res)
- (set! last-seq (hash-ref res 's))
- (cond [(= 10 (hash-ref res 'op)) (ws-send! ws
- (jsexpr->string (hasheq 'op 2
- 'd (hasheq 'token (hash-ref config 'token)
- 'properties (hasheq)
- 'compress #f
- 'large_threshold 250))))
- (thread-send heartbeat-thread (hash-ref (hash-ref res 'd) 'heartbeat_interval))]
- [(= 0 (hash-ref res 'op)) (set! last-seq (hash-ref res 's))
- (handle-event res)]))
- (define ws-listener-thread
- (thread
- (lambda ()
- (let loop ()
- (let ([m (ws-recv ws)])
- (cond [(not (eof-object? m)) (route (string->jsexpr m))]))
- (sleep 0.1) (loop)))))
- ; TODO:
- ; 1. If a client does not receive a heartbeat ack between its attempts at sending heartbeats,
- ; it should immediately terminate the connection with a non-1000 close code,
- ; reconnect, and attempt to resume.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement