mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 00:50:12 +00:00
Discord: Add option to provide server invite
When enabled, a temporary one-use invite is created and shown to the user on the account creation form.
This commit is contained in:
parent
0676b6c41f
commit
1f9af8df89
27
api.go
27
api.go
@ -2206,6 +2206,33 @@ func (app *appContext) DiscordVerifiedInvite(gc *gin.Context) {
|
||||
respondBool(200, ok, gc)
|
||||
}
|
||||
|
||||
// @Summary Returns a 10-minute, one-use Discord server invite
|
||||
// @Produce json
|
||||
// @Success 200 {object} DiscordInviteDTO
|
||||
// @Failure 400 {object} boolResponse
|
||||
// @Failure 401 {object} boolResponse
|
||||
// @Failure 500 {object} boolResponse
|
||||
// @Param invCode path string true "invite Code"
|
||||
// @Router /invite/{invCode}/discord/invite [get]
|
||||
// @tags Other
|
||||
func (app *appContext) DiscordServerInvite(gc *gin.Context) {
|
||||
if app.discord.inviteChannelName == "" {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
}
|
||||
code := gc.Param("invCode")
|
||||
if _, ok := app.storage.invites[code]; !ok {
|
||||
respondBool(401, false, gc)
|
||||
return
|
||||
}
|
||||
invURL, iconURL := app.discord.NewTempInvite(10*60, 1)
|
||||
if invURL == "" {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
gc.JSON(200, DiscordInviteDTO{invURL, iconURL})
|
||||
}
|
||||
|
||||
// @Summary Returns a list of matching users from a Discord guild, given a username (discriminator optional).
|
||||
// @Produce json
|
||||
// @Success 200 {object} DiscordUsersDTO
|
||||
|
@ -592,11 +592,29 @@
|
||||
"name": "Channel to monitor",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depens_true": "enabled",
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Only listen to commands in specified channel. Leave blank to monitor all."
|
||||
},
|
||||
"provide_invite": {
|
||||
"name": "Provide server invite",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Generate a one-time discord server invite for the account creation form. Required Bot permission \"Create instant invite\", you may need to re-add the bot to your server after."
|
||||
},
|
||||
"invite_channel": {
|
||||
"name": "Invite channel",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "provide_invite",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Channel to invite new users to."
|
||||
},
|
||||
"language": {
|
||||
"name": "Language",
|
||||
"required": false,
|
||||
|
@ -498,6 +498,11 @@ td.img-circle {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
span.img-circle.lg {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
span.shield.img-circle {
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
93
discord.go
93
discord.go
@ -8,16 +8,17 @@ import (
|
||||
)
|
||||
|
||||
type DiscordDaemon struct {
|
||||
Stopped bool
|
||||
ShutdownChannel chan string
|
||||
bot *dg.Session
|
||||
username string
|
||||
tokens []string
|
||||
verifiedTokens map[string]DiscordUser // Map of tokens to discord users.
|
||||
channelID, channelName string
|
||||
serverChannelName string
|
||||
users map[string]DiscordUser // Map of user IDs to users. Added to on first interaction, and loaded from app.storage.discord on start.
|
||||
app *appContext
|
||||
Stopped bool
|
||||
ShutdownChannel chan string
|
||||
bot *dg.Session
|
||||
username string
|
||||
tokens []string
|
||||
verifiedTokens map[string]DiscordUser // Map of tokens to discord users.
|
||||
channelID, channelName, inviteChannelID, inviteChannelName string
|
||||
guildID string
|
||||
serverChannelName, serverName string
|
||||
users map[string]DiscordUser // Map of user IDs to users. Added to on first interaction, and loaded from app.storage.discord on start.
|
||||
app *appContext
|
||||
}
|
||||
|
||||
func newDiscordDaemon(app *appContext) (*DiscordDaemon, error) {
|
||||
@ -71,7 +72,7 @@ func (d *DiscordDaemon) MustGetUser(channelID, userID, discrim, username string)
|
||||
|
||||
func (d *DiscordDaemon) run() {
|
||||
d.bot.AddHandler(d.messageHandler)
|
||||
d.bot.Identify.Intents = dg.IntentsGuildMessages | dg.IntentsDirectMessages | dg.IntentsGuildMembers
|
||||
d.bot.Identify.Intents = dg.IntentsGuildMessages | dg.IntentsDirectMessages | dg.IntentsGuildMembers | dg.IntentsGuildInvites
|
||||
if err := d.bot.Open(); err != nil {
|
||||
d.app.err.Printf("Discord: Failed to start daemon: %v", err)
|
||||
return
|
||||
@ -82,26 +83,92 @@ func (d *DiscordDaemon) run() {
|
||||
}
|
||||
d.username = d.bot.State.User.Username
|
||||
// Choose the last guild (server), for now we don't really support multiple anyway
|
||||
guild, err := d.bot.Guild(d.bot.State.Guilds[len(d.bot.State.Guilds)-1].ID)
|
||||
d.guildID = d.bot.State.Guilds[len(d.bot.State.Guilds)-1].ID
|
||||
guild, err := d.bot.Guild(d.guildID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to get guild: %v", err)
|
||||
}
|
||||
d.serverChannelName = guild.Name
|
||||
d.serverName = guild.Name
|
||||
if channel := d.app.config.Section("discord").Key("channel").String(); channel != "" {
|
||||
d.channelName = channel
|
||||
d.serverChannelName += "/" + channel
|
||||
}
|
||||
if d.app.config.Section("discord").Key("provide_invite").MustBool(false) {
|
||||
if invChannel := d.app.config.Section("discord").Key("invite_channel").String(); invChannel != "" {
|
||||
d.inviteChannelName = invChannel
|
||||
}
|
||||
}
|
||||
defer d.bot.Close()
|
||||
<-d.ShutdownChannel
|
||||
d.ShutdownChannel <- "Down"
|
||||
return
|
||||
}
|
||||
|
||||
// NewTempInvite creates an invite link, and returns the invite URL, as well as the URL for the server icon.
|
||||
func (d *DiscordDaemon) NewTempInvite(ageSeconds, maxUses int) (inviteURL, iconURL string) {
|
||||
var inv *dg.Invite
|
||||
var err error
|
||||
if d.inviteChannelName == "" {
|
||||
d.app.err.Println("Discord: Cannot create invite without channel specified in settings.")
|
||||
return
|
||||
}
|
||||
if d.inviteChannelID == "" {
|
||||
channels, err := d.bot.GuildChannels(d.guildID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Couldn't get channel list: %v", err)
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for _, channel := range channels {
|
||||
// channel, err := d.bot.Channel(ch.ID)
|
||||
// if err != nil {
|
||||
// d.app.err.Printf("Discord: Couldn't get channel: %v", err)
|
||||
// return
|
||||
// }
|
||||
if channel.Name == d.inviteChannelName {
|
||||
d.inviteChannelID = channel.ID
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
d.app.err.Printf("Discord: Couldn't find invite channel \"%s\"", d.inviteChannelName)
|
||||
return
|
||||
}
|
||||
}
|
||||
// channel, err := d.bot.Channel(d.inviteChannelID)
|
||||
// if err != nil {
|
||||
// d.app.err.Printf("Discord: Couldn't get invite channel: %v", err)
|
||||
// return
|
||||
// }
|
||||
inv, err = d.bot.ChannelInviteCreate(d.inviteChannelID, dg.Invite{
|
||||
// Guild: d.bot.State.Guilds[len(d.bot.State.Guilds)-1],
|
||||
// Channel: channel,
|
||||
// Inviter: d.bot.State.User,
|
||||
MaxAge: ageSeconds,
|
||||
MaxUses: maxUses,
|
||||
Temporary: false,
|
||||
})
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to create invite: %v", err)
|
||||
return
|
||||
}
|
||||
inviteURL = "https://discord.gg/" + inv.Code
|
||||
guild, err := d.bot.Guild(d.guildID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to get guild: %v", err)
|
||||
return
|
||||
}
|
||||
iconURL = guild.IconURL()
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the user(s) roughly corresponding to the username (if they are in the guild).
|
||||
// if no discriminator (#xxxx) is given in the username and there are multiple corresponding users, a list of all matching users is returned.
|
||||
func (d *DiscordDaemon) GetUsers(username string) []*dg.Member {
|
||||
members, err := d.bot.GuildMembers(
|
||||
d.bot.State.Guilds[len(d.bot.State.Guilds)-1].ID,
|
||||
d.guildID,
|
||||
"",
|
||||
1000,
|
||||
)
|
||||
|
@ -20,6 +20,8 @@
|
||||
window.discordEnabled = {{ .discordEnabled }};
|
||||
window.discordRequired = {{ .discordRequired }};
|
||||
window.discordPIN = "{{ .discordPIN }}";
|
||||
window.discordInviteLink = {{ .discordInviteLink }};
|
||||
window.discordServerName = "{{ .discordServerName }}";
|
||||
</script>
|
||||
<script src="js/form.js" type="module"></script>
|
||||
{{ end }}
|
||||
|
@ -43,6 +43,7 @@
|
||||
<span class="heading mb-1">{{ .strings.linkDiscord }}</span>
|
||||
<p class="content mb-1"> {{ .discordSendPINMessage }}</p>
|
||||
<h1 class="ac">{{ .discordPIN }}</h1>
|
||||
<a id="discord-invite"></a>
|
||||
<span class="button ~info !normal full-width center mt-1" id="discord-waiting">{{ .strings.success }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -276,3 +276,8 @@ type DiscordConnectUserDTO struct {
|
||||
JellyfinID string `json:"jf_id"`
|
||||
DiscordID string `json:"discord_id"`
|
||||
}
|
||||
|
||||
type DiscordInviteDTO struct {
|
||||
InviteURL string `json:"invite"`
|
||||
IconURL string `json:"icon"`
|
||||
}
|
||||
|
@ -123,6 +123,9 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
||||
}
|
||||
if discordEnabled {
|
||||
router.GET(p+"/invite/:invCode/discord/verified/:pin", app.DiscordVerifiedInvite)
|
||||
if app.config.Section("discord").Key("provide_invite").MustBool(false) {
|
||||
router.GET(p+"/invite/:invCode/discord/invite", app.DiscordServerInvite)
|
||||
}
|
||||
}
|
||||
}
|
||||
if *SWAGGER {
|
||||
|
22
ts/form.ts
22
ts/form.ts
@ -18,6 +18,8 @@ interface formWindow extends Window {
|
||||
discordRequired: boolean;
|
||||
discordPIN: string;
|
||||
discordStartCommand: string;
|
||||
discordInviteLink: boolean;
|
||||
discordServerName: string;
|
||||
userExpiryEnabled: boolean;
|
||||
userExpiryMonths: number;
|
||||
userExpiryDays: number;
|
||||
@ -88,10 +90,30 @@ if (window.telegramEnabled) {
|
||||
};
|
||||
}
|
||||
|
||||
interface DiscordInvite {
|
||||
invite: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
var discordVerified = false;
|
||||
if (window.discordEnabled) {
|
||||
window.discordModal = new Modal(document.getElementById("modal-discord"), window.discordRequired);
|
||||
const discordButton = document.getElementById("link-discord") as HTMLSpanElement;
|
||||
if (window.discordInviteLink) {
|
||||
_get("/invite/" + window.code + "/discord/invite", null, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status != 200) {
|
||||
return;
|
||||
}
|
||||
const inv = req.response as DiscordInvite;
|
||||
const link = document.getElementById("discord-invite") as HTMLAnchorElement;
|
||||
link.classList.add("subheading", "link-center");
|
||||
link.href = inv.invite;
|
||||
link.target = "_blank";
|
||||
link.innerHTML = `<span class="img-circle lg mr-1"><img class="img-circle" src="${inv.icon}" width="64" height="64"></span>${window.discordServerName}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
discordButton.onclick = () => {
|
||||
const waiting = document.getElementById("discord-waiting") as HTMLSpanElement;
|
||||
toggleLoader(waiting);
|
||||
|
2
views.go
2
views.go
@ -302,6 +302,8 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
|
||||
"command": `<code class="code">` + app.config.Section("discord").Key("start_command").MustString("!start") + `</code>`,
|
||||
"server_channel": app.discord.serverChannelName,
|
||||
}))
|
||||
data["discordServerName"] = app.discord.serverName
|
||||
data["discordInviteLink"] = app.discord.inviteChannelName != ""
|
||||
}
|
||||
|
||||
// if discordEnabled {
|
||||
|
Loading…
Reference in New Issue
Block a user