Search code examples
common-lispircslacksbcl

Error connecting to Slack IRC gateway


I'm using the cl-irc library to connect to Slack, via the IRC gateway Slack provides.

However I'm getting the following error when I try to start the message loop with read-message-loop:

error while parsing arguments to DESTRUCTURING-BIND:
  invalid number of elements in
    ("duncan_bayne" "Welcome" "to" "Slack" "IRC" "Gateway"
     "server" "[email protected]")
  to satisfy lambda list
    (CL-IRC:NICKNAME CL-IRC::WELCOME-MESSAGE):
  exactly 2 expected, but 8 found
   [Condition of type SB-KERNEL::ARG-COUNT-ERROR]

... 

Backtrace:
  0: ((:METHOD CL-IRC::DEFAULT-HOOK (CL-IRC:IRC-RPL_WELCOME-MESSAGE)) #<CL-IRC:IRC-RPL_WELCOME-MESSAGE irc.tinyspeck.com RPL_WELCOME {1007FC6293}>) [fast-method]
  1: ((:METHOD CL-IRC::APPLY-TO-HOOKS (T)) #<CL-IRC:IRC-RPL_WELCOME-MESSAGE irc.tinyspeck.com RPL_WELCOME {1007FC6293}>) [fast-method]
  2: ((:METHOD CL-IRC:IRC-MESSAGE-EVENT (T CL-IRC:IRC-MESSAGE)) #<unavailable argument> #<CL-IRC:IRC-RPL_WELCOME-MESSAGE irc.tinyspeck.com RPL_WELCOME {1007FC6293}>) [fast-method]
  3: ((:METHOD CL-IRC:READ-MESSAGE (CL-IRC:CONNECTION)) #<CL-IRC:CONNECTION myob.irc.slack.com {10068E8ED3}>) [fast-method]
  4: ((:METHOD CL-IRC:READ-MESSAGE-LOOP (T)) #<CL-IRC:CONNECTION myob.irc.slack.com {10068E8ED3}>) [fast-method]
  5: (SB-INT:SIMPLE-EVAL-IN-LEXENV (CL-IRC:READ-MESSAGE-LOOP *CONN*) #<NULL-LEXENV>)
  6: (EVAL (CL-IRC:READ-MESSAGE-LOOP *CONN*))

While in the REPL I see:

UNHANDLED-EVENT:3672562852: RPL_MYINFO: irc.tinyspeck.com duncan_bayne "IRC-SLACK gateway"

I'm not sure what I'm doing wrong here; I'm fairly sure it's not my hooks, because the problem persists even if I disable them all.

Also, I can use the connection as expected - say, joining a channel and sending messages - provided I don't try to start the message loop.

At a guess, I'd say Slack is responding to connection with an unexpected message?


Solution

  • The fix as suggested by @jkilski is to modify cl-irc to accept the slightly unusual (but probably standards-compilant?) responses from Slack:

    (in-package #:cl-irc)
    (defmethod default-hook ((message irc-rpl_welcome-message))
      (with-slots
            (connection host user arguments)
          message
        (destructuring-bind
              (nickname &rest welcome-message)
            arguments
          (setf (user connection)
                (make-user connection
                           :nickname nickname
                           :hostname host
                           :username user)))))
    
    (in-package #:irc)
    (defmethod default-hook ((message irc-rpl_namreply-message))
      (let* ((connection (connection message)))
        (destructuring-bind
              (nick chan-visibility channel &optional names)
            (arguments message)
          (declare (ignore nick))
          (let ((channel (find-channel connection channel)))
            (setf (visibility channel)
                  (or (second (assoc chan-visibility
                                     '(("=" :public) ("*" :private) ("@" :secret))
                                     :test #'string=))
                      :unknown))
            (unless (has-mode-p channel 'namreply-in-progress)
              (add-mode channel 'namreply-in-progress
                        (make-instance 'list-value-mode :value-type :user)))
            (dolist (nickname (tokenize-string names))
              (let ((user (find-or-make-user connection
                                             (canonicalize-nickname connection
                                                                    nickname))))
                (unless (equal user (user connection))
                  (add-user connection user)
                  (add-user channel user))
                (set-mode channel 'namreply-in-progress user)
                (let* ((mode-char (getf (nick-prefixes connection)
                                        (elt nickname 0)))
                       (mode-name (when mode-char
                                    (mode-name-from-char connection
                                                         channel mode-char))))
                  (when mode-name
                    (if (has-mode-p channel mode-name)
                        (set-mode channel mode-name user)
                        (set-mode-value (add-mode channel mode-name
                                                  (make-mode connection
                                                             channel mode-name))
                                        user))))))))))
    

    I've applied to join the dev mailing list and will be submitting a patch shortly.