Issue when compiling

Asked by At

I keep getting the following error, its most likely something obvious but i'm rather new to this. Any help would be appreciated. I'm quite sure there error is in this section of the code.

(b/defcomponent client {:bounce/deps #{config/value}}
  (log/info "Connecting to Discord")
  (let [token (get-in config/value [:discord :token])
        client (if token
                 (.. (ClientBuilder.)
                     (withToken token)
                     login)
                 (throw (Error. "Discord token not found, please set {:discord {:token \"...\"}} in `config/config.edn`.")))]

Had a look through where i believe the error is, cant seem to figure it out though.

(ns snowball.discord
  (:require [clojure.string :as str]
            [clojure.core.async :as a]
            [bounce.system :as b]
            [taoensso.timbre :as log]
            [camel-snake-kebab.core :as csk]
            [snowball.config :as config]
            [snowball.util :as util])
  (:import [sx.blah.discord.api ClientBuilder]
           [sx.blah.discord.util.audio AudioPlayer]
           [sx.blah.discord.handle.audio IAudioReceiver]
           [sx.blah.discord.api.events IListener]))

(defn event->keyword [c]
  (-> (str c)
      (str/split #"\.")
      (last)
      (str/replace #"Event.*$" "")
      (csk/->kebab-case-keyword)))

(defmulti handle-event! (fn [c] (event->keyword c)))
(defmethod handle-event! :default [_] nil)

(declare client)

(defn ready? []
  (some-> client .isReady))

(defn poll-until-ready []
  (let [poll-ms (get-in config/value [:discord :poll-ms])]
    (log/info "Connected, waiting until ready")
    (util/poll-while poll-ms (complement ready?) #(log/info "Not ready, sleeping for" (str poll-ms "ms")))
    (log/info "Ready")))

(defn channels []
  (some-> client .getVoiceChannels seq))

(defn channel-users [channel]
  (some-> channel .getConnectedUsers seq))

(defn current-channel []
  (some-> client .getConnectedVoiceChannels seq first))

(defn ->name [entity]
  (some-> entity .getName))

(defn ->id [entity]
  (some-> entity .getLongID))

(defn leave! [channel]
  (when channel
    (log/info "Leaving" (->name channel))
    (.leave channel)))

(defn join! [channel]
  (when channel
    (log/info "Joining" (->name channel))
    (.join channel)))

(defn bot? [user]
  (some-> user .isBot))

(defn muted? [user]
  (when user
    (let [voice-state (first (.. user getVoiceStates values))]
      (or (.isMuted voice-state)
          (.isSelfMuted voice-state)
          (.isSuppressed voice-state)))))

(defn can-speak? [user]
  (not (or (bot? user) (muted? user))))

(defn has-speaking-users? [channel]
  (->> (channel-users channel)
       (filter can-speak?)
       (seq)
       (boolean)))

(defn default-guild []
  (some-> client .getGuilds seq first))

(defn guild-users []
  (some-> (default-guild) .getUsers))

(defn guild-text-channels []
  (some-> (default-guild) .getChannels))

(defn guild-voice-channels []
  (some-> (default-guild) .getVoiceChannels))

(defn move-user-to-voice-channel [user channel]
  (when (and user channel)
    (try
      (.moveToVoiceChannel user channel)
      (catch Exception e
        (log/warn "Tried to move a user to a voice channel that isn't connected to voice already")))))

(defn play! [audio]
  (when audio
    (when-let [guild (default-guild)]
      (doto (AudioPlayer/getAudioPlayerForGuild guild)
        (.clear)
        (.queue audio)))))

(defn send! [channel-id message]
  (when-let [channel (.getChannelByID (default-guild) channel-id)]
    (log/info "Sending message to" channel-id "-" message)
    (.sendMessage channel message)))

(defmethod handle-event! :reconnect-success [_]
  (log/info "Reconnection detected, leaving any existing voice channels to avoid weird state")
  (poll-until-ready)
  (when-let [channel (current-channel)]
    (leave! channel)))

(defn audio-manager []
  (some-> (default-guild) .getAudioManager))

(defrecord AudioEvent [audio user])

(defn subscribe-audio! [f]
  (let [sub! (atom nil)
        closed?! (atom false)
        sub-chan (a/go-loop []
                   (when-not @closed?!
                     (a/<! (a/timeout (get-in config/value [:discord :poll-ms])))
                     (if-let [am (audio-manager)]
                       (try
                         (let [sub (reify IAudioReceiver
                                     (receive [_ audio user _ _]
                                       (try
                                         (when-not (bot? user)
                                           (f (AudioEvent. audio user)))
                                         (catch Exception e
                                           (log/error "Caught error while passing audio event to subscription handler" e)))))]
                           (reset! sub! sub)
                           (log/info "Audio manager exists, subscribing to audio")
                           (.subscribeReceiver am sub))
                         (catch Exception e
                           (log/error "Caught error while setting up audio subscription" e)))
                       (recur))))]

    (fn []
      (when-let [sub @sub!]
        (reset! closed?! true)
        (a/close! sub-chan)
        (.unsubscribeReceiver (audio-manager) sub)))))

(b/defcomponent client {:bounce/deps #{config/value}}
  (log/info "Connecting to Discord")
  (let [token (get-in config/value [:discord :token])
        client (if token
                 (.. (ClientBuilder.)
                     (withToken token)
                     login)
                 (throw (Error. "Discord token not found, please set {:discord {:token \"...\"}} in `config/config.edn`.")))]

    (.registerListener
      (.getDispatcher client)
      (reify IListener
        (handle [_ event]
          (handle-event! event))))

    (with-redefs [client client]
      (poll-until-ready))

    (b/with-stop client
      (log/info "Shutting down Discord connection")
      (.logout client))))

(b/defcomponent audio-chan {:bounce/deps #{client}}
  (log/info "Starting audio channel from subscription")
  (let [audio-chan (a/chan (a/sliding-buffer 100))
        sub (subscribe-audio!
              (fn [event]
                (a/go
                  (a/put! audio-chan event))))]
    (b/with-stop audio-chan
      (log/info "Closing audio channel and unsubscribing")
      (sub)
      (a/close! audio-chan))))

Below is the error i am getting

>     make
>     ./run.sh
>     19-05-25 19:05:45 localhost INFO [snowball.main:6] - Starting components...
>     19-05-25 19:05:47 localhost INFO [snowball.config:16] - Loading base config from config.base.edn and user config from
> config/config.edn
>     19-05-25 19:05:47 localhost INFO [snowball.discord:150] - Connecting to Discord
>     Exception in thread "main" java.lang.ClassCastException: class clojure.lang.Symbol cannot be cast to class java.lang.String
> (clojure.lang.Symbol is in unnamed module of loader 'app';
> java.lang.String is in module java.base of loader 'bootstrap')
>             at snowball.discord$eval12963$start_client__12964.invoke(discord.clj:152)
>             at clojure.lang.AFn.applyToHelper(AFn.java:152)
>             at clojure.lang.AFn.applyTo(AFn.java:144)
>             at clojure.core$apply.invokeStatic(core.clj:665)
>             at clojure.core$apply.invoke(core.clj:660)
>             at bounce.system$start_system$fn__479$fn__480.invoke(system.clj:68)
>             at bounce.system$start_system$fn__479$fn__480$fn__481.invoke(system.clj:71)
>             at clojure.lang.AFn.applyToHelper(AFn.java:152)
>             at clojure.lang.AFn.applyTo(AFn.java:144)
>             at clojure.core$apply.invokeStatic(core.clj:665)
>             at clojure.core$with_bindings_STAR_.invokeStatic(core.clj:1973)
>             at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1973)
>             at clojure.lang.RestFn.invoke(RestFn.java:425)
>             at bounce.system$start_system$fn__479$fn__480.invoke(system.clj:69)
>             at bounce.system$start_system.invokeStatic(system.clj:80)
>             at bounce.system$start_system.invoke(system.clj:59)
>             at bounce.system$start_BANG_.invokeStatic(system.clj:122)
>             at bounce.system$start_BANG_.invoke(system.clj:112)
>             at snowball.main$_main.invokeStatic(main.clj:13)
>             at snowball.main$_main.invoke(main.clj:5)
>             at clojure.lang.AFn.applyToHelper(AFn.java:152)
>             at clojure.lang.AFn.applyTo(AFn.java:144)
>             at clojure.lang.Var.applyTo(Var.java:705)
>             at clojure.core$apply.invokeStatic(core.clj:665)
>             at clojure.main$main_opt.invokeStatic(main.clj:491)
>             at clojure.main$main_opt.invoke(main.clj:487)
>             at clojure.main$main.invokeStatic(main.clj:598)
>             at clojure.main$main.doInvoke(main.clj:561)
>             at clojure.lang.RestFn.applyTo(RestFn.java:137)
>             at clojure.lang.Var.applyTo(Var.java:705)
>             at clojure.main.main(main.java:37)
>     Makefile:12: recipe for target 'run' failed
>     make: *** [run] Error 1

1 Answers

0
Denis Fuenzalida On

From this bit:

>     19-05-25 19:05:47 localhost INFO [snowball.discord:150] - Connecting to Discord
>     Exception in thread "main" java.lang.ClassCastException: class clojure.lang.Symbol cannot be cast to class java.lang.String

It looks like the expression (get-in config/value [:discord :token]) in line 152 is returning a symbol (a clojure.lang.Symbol) instead of a String, and that is causing a ClassCastException to be thrown when building the Client class.

I guess what's going on is that you are reading the configuration from an external file or some other source and your token is not properly quoted, so it's being resolved as a Symbol ("a variable name") instead of a String.