ircd.rkt 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #lang racket
  2. ;; single room IRC server
  3. (require "private/irc-functions.rkt"
  4. (prefix-in ws: "api.rkt")
  5. (prefix-in movie-night: "chat.rkt"))
  6. (provide (all-defined-out))
  7. (define movie-night-ws-url (make-parameter "wss://stream.ihatebeinga.live/ws"))
  8. (define users '())
  9. (define channel "#chats")
  10. ;; Main entry point
  11. ;; Returns the main server loop thread (for synchronizing) and
  12. ;; a function for killing the server.
  13. (define (serve #:port port-no #:hostname host)
  14. (define serve-cust (make-custodian))
  15. (parameterize ([current-custodian serve-cust])
  16. (define listener (tcp-listen port-no 5 #t host))
  17. (define (loop)
  18. (accept-and-handle listener)
  19. (loop))
  20. (define t (thread loop))
  21. (values t
  22. (lambda ()
  23. (custodian-shutdown-all serve-cust)))))
  24. ;; Accepting new clients
  25. (define (accept-and-handle listener)
  26. (define cust (make-custodian))
  27. (parameterize ([current-custodian cust])
  28. (define-values (in out) (tcp-accept listener))
  29. ;; once the ports are bound we spawn a new thread
  30. ;; in oreder to allow the main server loop to handler
  31. ;; other connections
  32. (thread
  33. (lambda ()
  34. (define user-conn (accept-irc-connection in out))
  35. (thread (lambda ()
  36. (handle-user-messages user-conn cust)))))))
  37. (define/contract (accept-irc-connection in out)
  38. (-> input-port? output-port? irc-connection?)
  39. (define fake-conn (irc-connection in out #f #f #f))
  40. (define nick
  41. (let loop ()
  42. (match (read-from-client fake-conn)
  43. [(irc-message _ "NICK" params)
  44. (car params)]
  45. [_ (loop)])))
  46. (log-info (format "~a connected" nick))
  47. (define user
  48. (let loop ()
  49. (match (read-from-client fake-conn)
  50. [(irc-message _ "USER" params)
  51. (car params)]
  52. [_ (loop)])))
  53. (file-stream-buffer-mode out 'line)
  54. (define conn (irc-connection in out nick user #f))
  55. (define ws-c
  56. (movie-night:make-connection
  57. (movie-night-ws-url)
  58. nick
  59. #:on-join (lambda (n) (on-join conn n))
  60. #:on-leave (lambda (n) (on-leave conn n))
  61. #:on-name-change (lambda (n1 n2) (on-name-change conn n1 n2))
  62. #:on-users (lambda (l) (set! users l) (notify-users conn))
  63. #:on-chat (lambda (from msg) (on-chat conn from msg))
  64. #:on-response (lambda (msg) (on-response conn msg))
  65. #:on-notify (lambda (msg) (on-response conn msg))))
  66. (set-irc-connection-ws-conn! conn ws-c)
  67. (welcome-user conn)
  68. conn)
  69. ;; Callbacks for the MoveNight chat api
  70. (define (notify-users conn)
  71. (send-to-client conn (irc-message
  72. ":lolcathost"
  73. RPL_NAMEREPLY
  74. (list (irc-connection-nick conn) "@" channel
  75. (format ":~a" (string-join users)))))
  76. (send-to-client conn (irc-message
  77. ":lolcathost"
  78. RPL_ENDOFNAMES
  79. (list (irc-connection-nick conn) channel ":End of /NAMES list."))))
  80. (define (on-chat conn from message)
  81. (unless (equal? from (irc-connection-nick conn))
  82. (send-to-client
  83. conn
  84. (irc-message (format "~a!~a@lolcathost" from from)
  85. "PRIVMSG"
  86. (list channel (format ":~a" message))))))
  87. (define (on-response conn message)
  88. (send-to-client
  89. conn
  90. (irc-message "OwO!SERVER@lolcathost"
  91. "NOTICE"
  92. (list channel
  93. (format ":!!! [ ~a ]" message)))))
  94. (define (on-join conn nick)
  95. (unless (equal? nick (irc-connection-nick conn))
  96. (send-to-client
  97. conn
  98. (irc-message (format "~a!~a@lolcathost" nick nick)
  99. "JOIN"
  100. (list channel)))))
  101. (define (on-leave conn nick)
  102. (unless (equal? nick (irc-connection-nick conn))
  103. (send-to-client
  104. conn
  105. (irc-message (format "~a!~a@lolcathost" nick nick)
  106. "PART"
  107. (list channel)))))
  108. (define (on-name-change conn old-nick new-nick)
  109. (unless (equal? old-nick (irc-connection-nick conn))
  110. (send-to-client
  111. conn
  112. (irc-message (format "~a!~a@lolcathost" old-nick old-nick)
  113. "NICK"
  114. (list new-nick)))))
  115. ;; The loop for handling commands from the client
  116. (define (handle-user-messages conn custodian)
  117. (define nick (irc-connection-nick conn))
  118. (match (read-from-client conn)
  119. [(irc-message _ "PING" params)
  120. (send-to-client conn (irc-message #f "PONG" params))]
  121. [(irc-message _ "NICK" params)
  122. ;; TODO: propagate this info along the WS
  123. (set-irc-connection-nick! conn (car params))]
  124. [(irc-message _ "JOIN" (list chan))
  125. #:when (equal? chan channel)
  126. (define c (irc-connection-ws-conn conn))
  127. (ws:send-join c (irc-connection-nick conn) "#00FFAA")
  128. (send-to-client conn (irc-message
  129. (format "~a!~a@lolcathost"
  130. (irc-connection-nick conn)
  131. (irc-connection-user conn))
  132. "JOIN"
  133. (list channel)))
  134. (send-to-client conn (irc-message
  135. ":lolcathost"
  136. RPL_TOPIC
  137. (list nick channel ":chatting hard")))
  138. (sleep 1.5) ;; is there a way around going to sleep? :-<
  139. (ws:send-users c)]
  140. [(irc-message _ "MODE" (cons chan _))
  141. #:when (equal? chan channel)
  142. (send-to-client conn (irc-message
  143. ":lolcathost"
  144. RPL_CHANNELMODEIS
  145. (list nick channel "+OwO")))]
  146. [(irc-message _ "LIST" _)
  147. (send-to-client conn (irc-message
  148. ":lolcathost"
  149. "002"
  150. (list nick " /list not implemented ")))
  151. ]
  152. [(irc-message _ "WHO" (list chan))
  153. #:when (equal? chan channel)
  154. (send-to-client conn (irc-message
  155. ":lolcathost"
  156. RPL_WHOREPLY
  157. (list nick channel
  158. (irc-connection-user conn)
  159. "lolcathost"
  160. "lolcathost"
  161. nick
  162. "H"
  163. ":0")))
  164. (send-to-client conn (irc-message
  165. ":lolcathost"
  166. RPL_ENDOFWHO
  167. (list nick channel ":End of /WHO list.")))]
  168. [(irc-message _ "PRIVMSG" (list chan msg))
  169. #:when (equal? chan channel)
  170. (send-ws-message conn msg)]
  171. [(or #f
  172. (? eof-object?)
  173. (irc-message _ "QUIT" _))
  174. ;; TODO remove from the user list
  175. ;; somehow attach this to a custodian?
  176. (custodian-shutdown-all custodian)]
  177. [(var msg)
  178. (log-warning (format "handle-user-message: unknown message: ~a" msg))])
  179. (handle-user-messages conn custodian))
  180. ;; Utils
  181. (define (send-ws-message conn msg)
  182. (ws:send-message (irc-connection-ws-conn conn) msg))
  183. (define (welcome-user conn)
  184. (define nick (irc-connection-nick conn))
  185. (define (notify-nick msg)
  186. (send-to-client conn
  187. (irc-message "lolcathost" "002" (list nick msg))))
  188. ;; "001" has to be a string, otherwise it's converted to 1
  189. (send-to-client conn (irc-message "lolcathost" "001" (list nick "OwO")))
  190. (notify-nick ":-----------------------------------------------------------------------------")
  191. (notify-nick ":If you encounter an error, try reconnecting!")
  192. (notify-nick ":This IRCd does not support many features, like user to user messages or channel lists.")
  193. (notify-nick ":[Feb 2020] NEW! features: support for HexChat, JOINs & PARTs.")
  194. (notify-nick ":A lot of things are broken, please submit an issues to ")
  195. (notify-nick ": --> <https://notabug.org/epi/movie-night-chat> <--")
  196. (notify-nick ":This pwogwam comes with ABSOWUTEWY NyO WAWWANTY!11oneone")
  197. (notify-nick ":This is fwee softwawe, and you awe wewcome to wedistwibute it")
  198. (notify-nick ":undew cewtain conditions; see the LICENSE file for details UwU :3")
  199. (notify-nick ":-----------------------------------------------------------------------------")
  200. (for ([x cofe])
  201. (notify-nick (string-append ":" x)))
  202. (notify-nick ":Welcome nyaa")
  203. (notify-nick (format ":Please join the channel ~a nyaa" channel)))
  204. (define cofe
  205. '(" ,. ,."
  206. " || ||"
  207. " ,''--''. ON THIS SERVER"
  208. " : (.)(.) : WE #cofe"
  209. " ,' `. "
  210. " : : "
  211. " : : hash tag IHBA gang"
  212. " -ctr- `._m____m_,' "))