Skip to content

Commit db47ede

Browse files
committed
Discord server invites
1 parent 79d21f2 commit db47ede

File tree

14 files changed

+964
-82
lines changed

14 files changed

+964
-82
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseGithubSponsors
4+
class DiscordController < ::ApplicationController
5+
requires_plugin DiscourseGithubSponsors::PLUGIN_NAME
6+
7+
before_action :ensure_logged_in
8+
before_action :ensure_sponsor, only: %i[status generate_invite]
9+
before_action :ensure_discord_configured, only: %i[status generate_invite]
10+
11+
def status
12+
discord_account = get_user_discord_account
13+
14+
unless discord_account
15+
render json: { has_discord_linked: false, on_server: false, discord_username: nil }
16+
return
17+
end
18+
19+
# Discord stores username in the info hash
20+
discord_username = discord_account.info["username"] || discord_account.info["name"]
21+
22+
# Check if user is on the Discord server
23+
api = DiscourseGithubSponsors::DiscordApi.new
24+
on_server = api.member_exists?(discord_username)
25+
26+
render json: {
27+
has_discord_linked: true,
28+
on_server: on_server,
29+
discord_username: discord_username,
30+
}
31+
rescue => e
32+
Rails.logger.error("Discord status check error: #{e.message}")
33+
render json: {
34+
error: "Failed to check Discord status",
35+
has_discord_linked: true,
36+
},
37+
status: 500
38+
end
39+
40+
def generate_invite
41+
discord_account = get_user_discord_account
42+
43+
unless discord_account
44+
render json: { error: I18n.t("github_sponsors.errors.discord_not_linked") }, status: 422
45+
return
46+
end
47+
48+
discord_username = discord_account.info["username"] || discord_account.info["name"]
49+
50+
# Check if user is already on server
51+
api = DiscourseGithubSponsors::DiscordApi.new
52+
if api.member_exists?(discord_username)
53+
render json: { error: I18n.t("github_sponsors.errors.already_on_server") }, status: 422
54+
return
55+
end
56+
57+
# Generate the invite
58+
invite_code = api.create_invite
59+
60+
# Send webhook notification
61+
github_account =
62+
UserAssociatedAccount.find_by(user_id: current_user.id, provider_name: "github")
63+
github_username = github_account&.info&.dig("nickname") || current_user.username
64+
65+
api.send_webhook_notification(
66+
"#{github_username} generated a Discord invite (Discord: #{discord_username})",
67+
)
68+
69+
render json: {
70+
invite_code: invite_code,
71+
invite_url: "https://discord.gg/#{invite_code}",
72+
expires_at: Time.now.to_i + SiteSetting.discord_invite_max_age,
73+
}
74+
rescue DiscourseGithubSponsors::DiscordApi::PermissionError => e
75+
Rails.logger.error("Discord permission error: #{e.message}")
76+
render json: { error: I18n.t("github_sponsors.errors.bot_permission") }, status: 500
77+
rescue DiscourseGithubSponsors::DiscordApi::RateLimitError => e
78+
Rails.logger.error("Discord rate limit: #{e.message}")
79+
render json: { error: I18n.t("github_sponsors.errors.rate_limited") }, status: 429
80+
rescue => e
81+
Rails.logger.error("Discord invite generation error: #{e.message}")
82+
Rails.logger.error(e.backtrace.join("\n"))
83+
render json: { error: I18n.t("github_sponsors.errors.api_error") }, status: 500
84+
end
85+
86+
private
87+
88+
def ensure_sponsor
89+
sponsor_group = Group.find_by(name: SiteSetting.github_sponsors_group_name)
90+
unless sponsor_group&.users&.include?(current_user)
91+
render json: { error: I18n.t("github_sponsors.errors.not_sponsor") }, status: 403
92+
end
93+
end
94+
95+
def ensure_discord_configured
96+
if SiteSetting.discord_server_guild_id.blank? || SiteSetting.discord_bot_token.blank?
97+
render json: { error: "Discord is not configured" }, status: 422
98+
end
99+
end
100+
101+
def get_user_discord_account
102+
UserAssociatedAccount
103+
.where(user_id: current_user.id)
104+
.where("LOWER(provider_name) = ?", "discord")
105+
.first
106+
end
107+
end
108+
end

0 commit comments

Comments
 (0)