Multiple meeting lobbies and beta-gated features (3.5.2)

- /meeting add-lobby, remove-lobby, lobbies: up to 10 lobbies per server, capped in the database
- Deleted voice channels are dropped from the lobbies
- Beta features: registered only in the beta server and usable only with the Beta Tester role (BETA_GUILD_ID, BETA_ROLE_ID); fails closed
- /beta (bot owner only) shows how beta access is set up

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
FernandoJVideiraandClaude Sonnet 5.5 committed 2026-10-23 00:38:48 +01:00
1 parent 136377d81a
commit a364435bab
15 files changed
+789 -52

No files matched your search

+86
View File
@@ -0,0 +1,86 @@
// Package betacmd is the /beta command: it shows how beta access is set up.
// It exists only in the beta server (see commands.BetaCommands), is hidden
// from everyone but administrators in Discord's command picker, and only the
// bot's owner can actually run it (the owner is looked up from the Discord
// application, so there's nothing to configure).
package betacmd
import (
"sync"
"velox-bot/internal/beta"
"github.com/bwmarrin/discordgo"
)
var (
noDM = false
noPerms = int64(0) // hides the command from everyone but administrators
ownerIDs sync.Map // user ID -> struct{}, filled once from the application
loadOnce sync.Mutex
loaded bool
)
var Beta = &discordgo.ApplicationCommand{
Name: "beta",
Description: "Show how beta access is set up (bot owner only)",
DMPermission: &noDM,
DefaultMemberPermissions: &noPerms,
}
// isOwner reports whether the user owns the bot's Discord application (or is
// on its team). If the application can't be read, nobody is the owner.
func isOwner(s *discordgo.Session, userID string) bool {
if userID == "" {
return false
}
loadOnce.Lock()
if !loaded {
app, err := s.Application("@me")
if err == nil && app != nil {
if app.Owner != nil {
ownerIDs.Store(app.Owner.ID, struct{}{})
}
if app.Team != nil {
ownerIDs.Store(app.Team.OwnerID, struct{}{})
for _, m := range app.Team.Members {
if m != nil && m.User != nil {
ownerIDs.Store(m.User.ID, struct{}{})
}
}
}
loaded = true
}
}
loadOnce.Unlock()
_, ok := ownerIDs.Load(userID)
return ok
}
func Handler(s *discordgo.Session, i *discordgo.InteractionCreate) {
var userID string
if i.Member != nil && i.Member.User != nil {
userID = i.Member.User.ID
}
var msg string
st := beta.StatusOf(i.GuildID, i.Member)
switch {
case !isOwner(s, userID):
msg = "Only the bot's owner can use this command."
case !st.InBetaGuild:
msg = "Beta features aren't available here."
case !st.RoleConfigured:
msg = "No Beta Tester role is set up, so beta features are locked for everyone."
case st.HasRole:
msg = "You're a beta tester: beta features are unlocked for you in this server."
default:
msg = "You don't have the Beta Tester role, so beta features are locked for you."
}
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: msg,
Flags: discordgo.MessageFlagsEphemeral,
AllowedMentions: &discordgo.MessageAllowedMentions{},
},
})
}