Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 4124c0c

Browse files
committed
Proof of concept of Discord Bot integration
1 parent 94010a5 commit 4124c0c

File tree

5 files changed

+90
-0
lines changed

5 files changed

+90
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules
22
/gems
33
/auto_generated
4+
.env
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseAi
4+
module Discord
5+
class BotController < ::ApplicationController
6+
requires_plugin ::DiscourseAi::PLUGIN_NAME
7+
8+
skip_before_action :verify_authenticity_token
9+
10+
def interactions
11+
# Request signature verification
12+
begin
13+
verify_request!
14+
rescue Ed25519::VerifyError
15+
return head :unauthorized
16+
end
17+
18+
body = JSON.parse(request.body.read)
19+
20+
if body["type"] == 1
21+
# Respond to Discord PING request
22+
render json: { type: 1 }
23+
else
24+
response = { type: 5, data: { content: "Searching..." } }
25+
hijack { render json: response }
26+
27+
pp request.headers
28+
pp body
29+
30+
# Respond to /commands
31+
persona = DiscourseAi::AiBot::Personas::DiscourseHelper
32+
bot =
33+
DiscourseAi::AiBot::Bot.as(
34+
Discourse.system_user,
35+
persona: persona.new,
36+
model: "custom:6",
37+
)
38+
39+
query = body["data"]["options"].first["value"]
40+
reply = ""
41+
reply =
42+
bot.reply({ conversation_context: [{ type: :user, content: query }] }) { |a, b, c| nil }
43+
44+
pp reply.last.first
45+
46+
discord_reply = reply.last.first
47+
48+
api_endpoint =
49+
"https://discord.com/api/webhooks/#{SiteSetting.ai_discord_app_id}/#{body["token"]}"
50+
51+
conn = Faraday.new { |f| f.adapter FinalDestination::FaradayAdapter }
52+
response =
53+
conn.post(
54+
api_endpoint,
55+
{ content: discord_reply }.to_json,
56+
{ "Content-Type" => "application/json" },
57+
)
58+
59+
pp response
60+
end
61+
end
62+
63+
private
64+
65+
def verify_request!
66+
signature = request.headers["X-Signature-Ed25519"]
67+
timestamp = request.headers["X-Signature-Timestamp"]
68+
verify_key.verify([signature].pack("H*"), "#{timestamp}#{request.raw_post}")
69+
end
70+
71+
def verify_key
72+
# TODO remove this gem dependency
73+
Ed25519::VerifyKey.new([SiteSetting.ai_discord_app_public_key].pack("H*")).freeze
74+
end
75+
end
76+
end
77+
end

config/routes.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
get "quick-search" => "embeddings#quick_search"
1616
end
1717

18+
scope module: :discord, path: "/discord", defaults: { format: :json } do
19+
post "interactions" => "bot#interactions"
20+
end
21+
1822
scope module: :ai_bot, path: "/ai-bot", defaults: { format: :json } do
1923
get "bot-username" => "bot#show_bot_username"
2024
get "post/:post_id/show-debug-info" => "bot#show_debug_info"

config/settings.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,10 @@ discourse_ai:
431431
hidden: true
432432
type: list
433433
list_type: compact
434+
435+
ai_discord_app_id:
436+
default: ""
437+
client: false
438+
ai_discord_app_public_key:
439+
default: ""
440+
client: false

plugin.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
gem "tokenizers", "0.4.4"
1212
gem "tiktoken_ruby", "0.0.9"
13+
gem "ed25519", "1.2.4" #TODO remove this as existing ssl gem should handle this
1314

1415
enabled_site_setting :discourse_ai_enabled
1516

0 commit comments

Comments
 (0)