Documentation
¶
Index ¶
- Constants
- Variables
- func CleanMinecraftFormatting(s string) string
- func DiscordMarkdownToMiniMessage(content string) string
- func FormatText(input string) c.Component
- func IsVanillaDeathMessage(playerName, message string) bool
- func RegisterCommands(p *proxy.Proxy, store *ConfigStore, bot *DiscordBot)
- func RenderConfig(cfg *Config) string
- func SanitizeForMinecraft(s string) string
- type ChatMessagesSettings
- type CompanionHandler
- type CompanionMessage
- type Config
- type ConfigStore
- func (cs *ConfigStore) Get() *Config
- func (cs *ConfigStore) IsAdmin(userID string, memberRoles []string) bool
- func (cs *ConfigStore) IsCommandExcluded(cmd string) bool
- func (cs *ConfigStore) IsServerExcluded(serverName string) bool
- func (cs *ConfigStore) Load() (*Config, error)
- func (cs *ConfigStore) WasUpgraded() bool
- type DiscordBot
- func (b *DiscordBot) IsShuttingDown() bool
- func (b *DiscordBot) RecordPlayer(playerID string)
- func (b *DiscordBot) SendAdvancementMessage(playerName, title, description string)
- func (b *DiscordBot) SendChatMessage(player proxy.Player, serverName, message string)
- func (b *DiscordBot) SendDeathMessage(deathMsg string)
- func (b *DiscordBot) SendJoinMessage(player proxy.Player, serverName string)
- func (b *DiscordBot) SendLeaveMessage(player proxy.Player, serverName string)
- func (b *DiscordBot) SendProxyStart()
- func (b *DiscordBot) SendProxyStop()
- func (b *DiscordBot) SendServerStatusChange(serverName string, isOnline bool)
- func (b *DiscordBot) SendServerSwitchMessage(player proxy.Player, fromServer, toServer string)
- func (b *DiscordBot) SetShuttingDown(val bool)
- func (b *DiscordBot) Start(ctx context.Context) error
- func (b *DiscordBot) Stop()
- type DiscordSettings
- type PlayerTracker
- type ServerMonitor
- type WebhookSettings
Constants ¶
const CurrentConfigVersion = 2
CurrentConfigVersion defines the latest configuration schema version.
Variables ¶
var Plugin = proxy.Plugin{ Name: "Discord4Gate", Init: func(ctx context.Context, p *proxy.Proxy) error { log := logr.FromContextOrDiscard(ctx) configPath := filepath.Join("config", "discord4gate.toml") store := NewConfigStore(configPath) if _, err := store.Load(); err != nil { log.Error(err, "Failed to load discord4gate configuration") return err } if store.WasUpgraded() { log.Info("Configuration automatically upgraded to latest schema!", "version", CurrentConfigVersion, "backup", configPath+".bak") } bot := NewDiscordBot(p, store, log) _ = bot.Start(ctx) companion := NewCompanionHandler(bot, log) monitor := NewServerMonitor(p, store, bot, log) monitor.Start() event.Subscribe(p.Event(), 0, func(e *proxy.PlayerChatEvent) { if !e.Allowed() { return } msg := e.Message() if store.IsCommandExcluded(msg) { return } srvName := "Server" if cs := e.Player().CurrentServer(); cs != nil { srvName = cs.Server().ServerInfo().Name() } if store.IsServerExcluded(srvName) { return } log.Info("Relaying in-game chat to Discord", "player", e.Player().Username(), "server", srvName, "message", msg) bot.SendChatMessage(e.Player(), srvName, msg) }) event.Subscribe(p.Event(), 0, func(e *proxy.ServerConnectedEvent) { targetSrvName := e.Server().ServerInfo().Name() if store.IsServerExcluded(targetSrvName) { return } bot.RecordPlayer(e.Player().ID().String()) if e.PreviousServer() == nil { log.Info("Player joined network", "player", e.Player().Username(), "server", targetSrvName) bot.SendJoinMessage(e.Player(), targetSrvName) } else { prevName := e.PreviousServer().ServerInfo().Name() if !strings.EqualFold(prevName, targetSrvName) { log.Info("Player switched server", "player", e.Player().Username(), "from", prevName, "to", targetSrvName) bot.SendServerSwitchMessage(e.Player(), prevName, targetSrvName) } } }) event.Subscribe(p.Event(), 0, func(e *proxy.DisconnectEvent) { if bot.IsShuttingDown() { return } srvName := "Server" if cs := e.Player().CurrentServer(); cs != nil { srvName = cs.Server().ServerInfo().Name() } if store.IsServerExcluded(srvName) { return } log.Info("Player left network", "player", e.Player().Username(), "server", srvName) bot.SendLeaveMessage(e.Player(), srvName) }) event.Subscribe(p.Event(), 0, func(e *proxy.PluginMessageEvent) { companion.HandlePluginMessage(e) }) event.Subscribe(p.Event(), 0, func(e *proxy.PreShutdownEvent) { log.Info("Proxy pre-shutdown: notifying Discord channel...") bot.SetShuttingDown(true) bot.SendProxyStop() }) event.Subscribe(p.Event(), 0, func(e *proxy.ShutdownEvent) { log.Info("Proxy shutdown: disconnecting Discord bot...") monitor.Stop() bot.Stop() }) RegisterCommands(p, store, bot) log.Info("Discord4Gate loaded successfully! (chat bridge, presence, slash commands active)") return nil }, }
Plugin is the Discord4Gate Discord <> Minecraft chat and event bridge plugin.
Functions ¶
func CleanMinecraftFormatting ¶
CleanMinecraftFormatting strips Minecraft color tags when sending text to Discord.
func DiscordMarkdownToMiniMessage ¶
DiscordMarkdownToMiniMessage converts Discord markdown & emojis into Minecraft MiniMessage tags.
func FormatText ¶
FormatText parses MiniMessage, RGB, Legacy color tags, and Click/Hover tags into a Minecraft Component.
func IsVanillaDeathMessage ¶
IsVanillaDeathMessage checks if a chat message looks like a vanilla death announcement.
func RegisterCommands ¶
func RegisterCommands(p *proxy.Proxy, store *ConfigStore, bot *DiscordBot)
RegisterCommands registers /discord and /discord4gate commands in Gate.
func RenderConfig ¶
RenderConfig formats the entire configuration with all comments, sections, and values.
func SanitizeForMinecraft ¶
SanitizeForMinecraft converts 4-byte UTF-8 runes (emojis > U+FFFF) to safe shortcodes and filters out invalid control characters to prevent Java Netty DecoderExceptions.
Types ¶
type ChatMessagesSettings ¶
type ChatMessagesSettings struct {
Message string `toml:"message"`
MessageType string `toml:"message_type"`
MessageEmbedColor string `toml:"message_embed_color"`
JoinMessage string `toml:"join_message"`
JoinMessageType string `toml:"join_message_type"`
JoinMessageEmbedColor string `toml:"join_message_embed_color"`
LeaveMessage string `toml:"leave_message"`
LeaveMessageType string `toml:"leave_message_type"`
LeaveMessageEmbedColor string `toml:"leave_message_embed_color"`
ServerSwitchMessage string `toml:"server_switch_message"`
ServerSwitchMessageType string `toml:"server_switch_message_type"`
ServerSwitchMessageEmbedColor string `toml:"server_switch_message_embed_color"`
ProxyStartMessage string `toml:"proxy_start_message"`
ProxyStartMessageType string `toml:"proxy_start_message_type"`
ProxyStartMessageEmbedColor string `toml:"proxy_start_message_embed_color"`
ProxyStopMessage string `toml:"proxy_stop_message"`
ProxyStopMessageType string `toml:"proxy_stop_message_type"`
ProxyStopMessageEmbedColor string `toml:"proxy_stop_message_embed_color"`
ServerStartMessage string `toml:"server_start_message"`
ServerStartMessageType string `toml:"server_start_message_type"`
ServerStartMessageEmbedColor string `toml:"server_start_message_embed_color"`
ServerStopMessage string `toml:"server_stop_message"`
ServerStopMessageType string `toml:"server_stop_message_type"`
ServerStopMessageEmbedColor string `toml:"server_stop_message_embed_color"`
DeathMessage string `toml:"death_message"`
DeathMessageType string `toml:"death_message_type"`
DeathMessageEmbedColor string `toml:"death_message_embed_color"`
AdvancementMessage string `toml:"advancement_message"`
AdvancementMessageType string `toml:"advancement_message_type"`
AdvancementMessageEmbedColor string `toml:"advancement_message_embed_color"`
}
type CompanionHandler ¶
type CompanionHandler struct {
// contains filtered or unexported fields
}
CompanionHandler handles plugin message packets from backend servers (optional companion integration).
func NewCompanionHandler ¶
func NewCompanionHandler(bot *DiscordBot, log logr.Logger) *CompanionHandler
NewCompanionHandler creates a new CompanionHandler instance.
func (*CompanionHandler) HandlePluginMessage ¶
func (h *CompanionHandler) HandlePluginMessage(e *proxy.PluginMessageEvent)
HandlePluginMessage processes incoming messages on discord4gate:main and compatible channels.
type CompanionMessage ¶
type CompanionMessage struct {
Type string `json:"type"` // "advancement", "death", "mspt", "chat"
Server string `json:"server,omitempty"` // server name
Player string `json:"player,omitempty"` // player username
Title string `json:"title,omitempty"` // advancement title
Description string `json:"description,omitempty"` // advancement description
Message string `json:"message,omitempty"` // death message or custom chat
MSPT float64 `json:"mspt,omitempty"` // MSPT load
TPS float64 `json:"tps,omitempty"` // TPS
}
CompanionMessage represents the JSON payload from optional backend mods/plugins.
type Config ¶
type Config struct {
ConfigVersion int `toml:"config_version"`
ExcludeServers []string `toml:"exclude_servers"`
ExcludedCommands []string `toml:"excluded_commands"`
PingInterval int `toml:"ping_interval"`
DiscordInvite string `toml:"discord_invite"`
Discord DiscordSettings `toml:"discord"`
WebhookRoot WebhookSettings `toml:"webhook"`
ChatRoot ChatMessagesSettings `toml:"chat"`
}
type ConfigStore ¶
type ConfigStore struct {
// contains filtered or unexported fields
}
func NewConfigStore ¶
func NewConfigStore(path string) *ConfigStore
func (*ConfigStore) Get ¶
func (cs *ConfigStore) Get() *Config
func (*ConfigStore) IsAdmin ¶
func (cs *ConfigStore) IsAdmin(userID string, memberRoles []string) bool
func (*ConfigStore) IsCommandExcluded ¶
func (cs *ConfigStore) IsCommandExcluded(cmd string) bool
func (*ConfigStore) IsServerExcluded ¶
func (cs *ConfigStore) IsServerExcluded(serverName string) bool
func (*ConfigStore) Load ¶
func (cs *ConfigStore) Load() (*Config, error)
func (*ConfigStore) WasUpgraded ¶
func (cs *ConfigStore) WasUpgraded() bool
type DiscordBot ¶
type DiscordBot struct {
// contains filtered or unexported fields
}
DiscordBot manages the connection to Discord, slash commands, chat dispatch, and presence.
func NewDiscordBot ¶
func NewDiscordBot(p *proxy.Proxy, store *ConfigStore, log logr.Logger) *DiscordBot
NewDiscordBot creates a new DiscordBot instance.
func (*DiscordBot) IsShuttingDown ¶
func (b *DiscordBot) IsShuttingDown() bool
func (*DiscordBot) RecordPlayer ¶
func (b *DiscordBot) RecordPlayer(playerID string)
func (*DiscordBot) SendAdvancementMessage ¶
func (b *DiscordBot) SendAdvancementMessage(playerName, title, description string)
SendAdvancementMessage dispatches advancement announcement to Discord.
func (*DiscordBot) SendChatMessage ¶
func (b *DiscordBot) SendChatMessage(player proxy.Player, serverName, message string)
SendChatMessage sends player in-game chat to Discord.
func (*DiscordBot) SendDeathMessage ¶
func (b *DiscordBot) SendDeathMessage(deathMsg string)
SendDeathMessage dispatches death announcement to Discord.
func (*DiscordBot) SendJoinMessage ¶
func (b *DiscordBot) SendJoinMessage(player proxy.Player, serverName string)
SendJoinMessage announces player connection to Discord.
func (*DiscordBot) SendLeaveMessage ¶
func (b *DiscordBot) SendLeaveMessage(player proxy.Player, serverName string)
SendLeaveMessage announces player disconnection to Discord.
func (*DiscordBot) SendProxyStart ¶
func (b *DiscordBot) SendProxyStart()
SendProxyStart sends the proxy start message.
func (*DiscordBot) SendProxyStop ¶
func (b *DiscordBot) SendProxyStop()
SendProxyStop sends the proxy stop message, updates topic to offline, and updates voice channels in parallel.
func (*DiscordBot) SendServerStatusChange ¶
func (b *DiscordBot) SendServerStatusChange(serverName string, isOnline bool)
SendServerStatusChange alerts when a backend server goes offline or boots back up.
func (*DiscordBot) SendServerSwitchMessage ¶
func (b *DiscordBot) SendServerSwitchMessage(player proxy.Player, fromServer, toServer string)
SendServerSwitchMessage announces player server transfer to Discord.
func (*DiscordBot) SetShuttingDown ¶
func (b *DiscordBot) SetShuttingDown(val bool)
func (*DiscordBot) Start ¶
func (b *DiscordBot) Start(ctx context.Context) error
Start connects to Discord and registers commands asynchronously (0s proxy startup lag).
func (*DiscordBot) Stop ¶
func (b *DiscordBot) Stop()
Stop gracefully disconnects the bot synchronously.
type DiscordSettings ¶
type DiscordSettings struct {
Token string `toml:"token"`
Channel string `toml:"channel"`
AdminIDs []string `toml:"admin_ids"`
MaxPlayers int `toml:"max_players"`
NameFormat string `toml:"name_format"`
EnableHoverTooltips bool `toml:"enable_hover_tooltips"`
ShowBotMessages bool `toml:"show_bot_messages"`
ShowAttachmentsIngame bool `toml:"show_attachments_ingame"`
ShowActivity bool `toml:"show_activity"`
ActivityType string `toml:"activity_type"`
ActivityText string `toml:"activity_text"`
EnableMentions bool `toml:"enable_mentions"`
EnableEveryoneAndHere bool `toml:"enable_everyone_and_here"`
UpdateChannelTopicInterval int `toml:"update_channel_topic_interval"`
Topic string `toml:"topic"`
VoicePlayerCountChannelID string `toml:"voice_player_count_channel_id"`
VoicePlayerCountFormat string `toml:"voice_player_count_format"`
VoicePlayerCountOfflineFormat string `toml:"voice_player_count_offline_format"`
VoiceStatusChannelID string `toml:"voice_status_channel_id"`
VoiceStatusOnlineFormat string `toml:"voice_status_online_format"`
VoiceStatusOfflineFormat string `toml:"voice_status_offline_format"`
Webhook WebhookSettings `toml:"webhook"`
Chat ChatMessagesSettings `toml:"chat"`
}
type PlayerTracker ¶
type PlayerTracker struct {
// contains filtered or unexported fields
}
PlayerTracker maintains a persistent list of unique players who have ever joined.
func NewPlayerTracker ¶
func NewPlayerTracker(path string) *PlayerTracker
NewPlayerTracker creates and initializes a PlayerTracker from disk.
func (*PlayerTracker) Count ¶
func (pt *PlayerTracker) Count() int
Count returns the total number of unique players who have ever joined.
func (*PlayerTracker) Record ¶
func (pt *PlayerTracker) Record(playerID string) bool
Record adds a player identifier and returns true if it was newly recorded.
type ServerMonitor ¶
type ServerMonitor struct {
// contains filtered or unexported fields
}
ServerMonitor watches backend server reachability and announces starts/stops to Discord.
func NewServerMonitor ¶
func NewServerMonitor(p *proxy.Proxy, store *ConfigStore, bot *DiscordBot, log logr.Logger) *ServerMonitor
NewServerMonitor creates a new ServerMonitor instance.
func (*ServerMonitor) Start ¶
func (sm *ServerMonitor) Start()
Start begins periodic background server polling independently of init context.