Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a discord adapter #223

Merged
merged 23 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion config/config.sample.edn
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
:google {:api {:key ""}
:custom {:search {:engine {:id ""}}}
:options {:safe "high"}}
:discord {:token ""}
bensontrinh marked this conversation as resolved.
Show resolved Hide resolved
bensontrinh marked this conversation as resolved.
Show resolved Hide resolved
:db {:url "postgresql://localhost:5432/yetibot"
:table {:prefix "yetibot_"}},
:history {:disabled "false"},
Expand Down Expand Up @@ -41,7 +42,9 @@
:host "yetibot-mattermost.herokuapp.com"
:token "h1111111111111111111111111"
:secure "true" ;; true by default
}},
}
:mydiscord {:type "discord"
:token ""}},
:ssh {:groups
[{:user "",
:servers [{:host "", :name ""} {:host "", :name ""}],
Expand Down
4 changes: 3 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,12 @@
; chat protocols
[irclj "0.5.0-alpha4"]
;; use this fork which uses javax.websockets for compatability with Yetibot
[stylefruits/gniazdo-jsr356 "1.0.0"]
;; gniazdo 1.2.2 needed for discljord
[stylefruits/gniazdo "1.2.2"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to update this for discord to work properly. I'm thinking this will end up breaking other things since this uses jetty's Websocket

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like your local yb is running just fine? I'll do some more testing but this upgrade might be good to go.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah locally it is fine. I didn't test any other connectors though.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to include this dep in the the yetibot/yetibotproject.clj as well to get it to run properly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed slack still works with this upgrade.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sick. Thanks for checking that out.

[slack-rtm "0.1.7" :exclusions [[stylefruits/gniazdo]]]
[org.julienxx/clj-slack "0.6.3"]
[mattermost-clj "4.0.3"]
[com.github.discljord/discljord "1.3.1"]

; javascript evaluation
[evaljs "0.1.2"]
Expand Down
5 changes: 4 additions & 1 deletion src/yetibot/core/adapters.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[yetibot.core.adapters.slack :as slack]
[yetibot.core.adapters.mattermost :as mattermost]
[yetibot.core.adapters.web :as web]
[yetibot.core.adapters.discord :as discord]
[taoensso.timbre :as log :refer [info debug warn]]
[clojure.stacktrace :refer [print-stack-trace]]
[yetibot.core.adapters.adapter :as a]
Expand All @@ -14,7 +15,8 @@
(s/def ::adapter (s/or :web ::web/config
:slack ::slack/config
:irc ::irc/config
:mattermost ::mattermost/config))
:mattermost ::mattermost/config
:discord ::discord/config))

(s/def ::config (s/map-of keyword? ::adapter))

Expand Down Expand Up @@ -45,6 +47,7 @@
:slack (slack/make-slack config)
:irc (irc/make-irc config)
:mattermost (mattermost/make-mattermost config)
:discord (discord/make-discord config)
(throw (ex-info (str "Unknown adapter type " (:type config)) config))))

(defn ->registerable-adapter
Expand Down
196 changes: 196 additions & 0 deletions src/yetibot/core/adapters/discord.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
(ns yetibot.core.adapters.discord
(:require [clojure.spec.alpha :as s]
[clojure.core.async :refer [chan close!]]
[discljord.messaging :refer [get-guild-channels! start-connection! stop-connection! get-current-user! create-message! delete-message!]]
[discljord.connections :as discord-ws]
[yetibot.core.models.users :as users]
[discljord.events :refer [message-pump!]]
[taoensso.timbre :refer [info debug]]
[yetibot.core.adapters.adapter :as a]
[yetibot.core.handler :refer [handle-raw]]
[yetibot.core.chat :refer [base-chat-source chat-source *target* *adapter*]]))

(s/def ::type #{"discord"})
(s/def ::token string?)
(s/def ::config (s/keys :req-un [::type ::token]))

(defmulti handle-event
devth marked this conversation as resolved.
Show resolved Hide resolved
(fn [event-type event-data _conn yetibot-user]
event-type))

(defmethod handle-event :default
[event-type event-data _conn yetibot-user]
(debug "🎉 UNHANDLED EVENT 🎉")
(debug "Event type:" event-type)
(debug "Event data:" (pr-str event-data))
(debug "Author: " (pr-str (:author event-data)))
(debug "Channel ID: " (pr-str (:channel-id event-data))))

;; also has :message-reaction-remove
(defmethod handle-event :message-reaction-add
[event-type event-data _conn yetibot-user]
(debug "🎉 NEW REACTION 🎉")
(debug "Event type:" event-type)
(debug "Event data:" (pr-str event-data))
(debug "Channel ID: " (pr-str (:channel-id event-data)))
(debug "Emoji: " (pr-str (-> event-data :emoji :name)))
(debug "Thread Author ID: " (:message-author-id event-data))
(debug "Yetibot user: " (:id @yetibot-user))
(if (and
(= (-> event-data :emoji :name) "❌")
(= (:message-author-id event-data) (:id @yetibot-user)))
(do
(let [message-id (:message-id event-data)]
(debug "Trying to delete message with id:" message-id)
(if (delete-message! (:rest @_conn) (:channel-id event-data) message-id)
(debug "Successfully deleted message")
(debug "Failed to delete message)"))))

(do (let [emoji-name (-> event-data :emoji :name)]
(if (not= (:message-author-id event-data) (:id @yetibot-user))
(debug "You can only delete messages from Yetibot")
(debug "No handler for emoji: " emoji-name))))))
bensontrinh marked this conversation as resolved.
Show resolved Hide resolved


(defmethod handle-event :message-create
[event-type event-data _conn yetibot-user]
(debug "🎉 NEW MESSAGE 🎉")
(debug "Event type:" event-type)
(debug "Event data:" (pr-str event-data))
(debug "Author: " (pr-str (:author event-data)))
(debug "Author ID: " (-> event-data
:author
:id))
(debug "Author Username: " (-> event-data
:author
:username))
(debug "Channel ID: " (pr-str (:channel-id event-data)))
(debug "Yetibot user: " (:id @yetibot-user))
(if (= (:content event-data) "!disconnect")
bensontrinh marked this conversation as resolved.
Show resolved Hide resolved
(discord-ws/disconnect-bot! (:connection _conn))
(do
(debug "Handling Message")
(if (not= (:id @yetibot-user) (-> event-data
:author
:id))
(let [user-model (users/create-user
(-> event-data
:author
:username)
(event-data :author :id))
message (:content event-data)]
(debug "chat source: " (pr-str (chat-source (:channel-id event-data))))
(debug "running handle-raw")
(binding [*target* (:channel-id event-data)]
(handle-raw
(chat-source (:channel-id event-data))
user-model
:message
@yetibot-user
{:body message})))
(debug "Message from Yetibot => ignoring")))))



(defn start
"start the discord connection"
[adapter _conn config _connected? bot-id yetibot-user]
bensontrinh marked this conversation as resolved.
Show resolved Hide resolved
(debug "starting discord connection")

(binding [*adapter* adapter]
(let [event-channel (chan 100)
message-channel (discord-ws/connect-bot! (:token config) event-channel :intents #{:guilds :guild-messages :guild-message-reactions :direct-messages :direct-message-reactions})
rest-connection (start-connection! (:token config))]
(let [retcon {:event event-channel
:message message-channel
:rest rest-connection}]
(reset! _conn retcon)

(debug (pr-str _conn))

(reset! _connected? true)
(reset! bot-id {:id @(get-current-user! rest-connection)})
(reset! yetibot-user @(get-current-user! rest-connection))
(message-pump! event-channel (fn [event-type event-data] (handle-event event-type event-data _conn yetibot-user)))))))



(defn- channels [a]
(let [guild-channels (get-guild-channels!)]
(debug "Guild Channels: " (pr-str guild-channels))
(guild-channels)))

(defn- send-msg [adapter msg conn]
(debug "Trying to send message: " msg)
(debug "Target is: " *target*)
(debug "Adapter: " (pr-str adapter))
(debug "conn: " (pr-str conn))
(debug "rest: " (pr-str (:rest @conn)))
(create-message! (:rest @conn) *target* :content msg))

(defn stop
"stop the discord connection"
[adapter _conn]
(debug "Closing Discord" (a/uuid adapter))
(stop-connection! (:message @_conn))
(close! (:event @_conn)))

(defrecord Discord
[config
bot-id
conn
connected?
connection-last-active-timestamp
connection-latency
should-ping?
yetibot-user]

a/Adapter

(a/uuid [_] (:name config))

(a/platform-name [_] "Discord")

(a/channels [a] (channels a))

(a/send-paste [a msg] (send-msg a msg conn))

(a/send-msg [a msg] (send-msg a msg conn))

(a/join [_ channel]
(str
"Discord bots such as myself can't join channels on their own. Use "
"/invite from the channel you'd like me to join instead.✌️"))

(a/leave [_ channel]
(str
"Discord bots such as myself can't leave channels on their own. Use "
"/kick from the channel you'd like me to leave instead. 👊"))

(a/chat-source [_ channel] (chat-source channel))

(a/stop [adapter] (stop adapter conn))

(a/connected? [{:keys [connected?]}]
@connected?)

(a/connection-last-active-timestamp [_]
@connection-last-active-timestamp)

(a/connection-latency [_]
@connection-latency)

(a/start [adapter]
(start adapter conn config connected? bot-id yetibot-user)))
bensontrinh marked this conversation as resolved.
Show resolved Hide resolved

(defn make-discord
[config]
(map->Discord
{:config config
:bot-id (atom nil)
:conn (atom nil)
:connected? (atom false)
:connection-latency (atom nil)
:connection-last-active-timestamp (atom nil)
:yetibot-user (atom nil)
:should-ping? (atom false)}))