Documentation
¶
Overview ¶
Package sikasa: bot.go Purpose: Defines the top-level Bot type, lifecycle (New, Start, Stop), intent helpers, and the registries that command/keyword builders write into.
Key Components:
- Bot: wraps *bot.Client (disgo) and owns the registries
- New(): constructs a Bot with sensible defaults; defers wiring until Start() so options can be appended fluently
- WithIntents(): fluent setter for gateway intents
- Start()/Stop(): lifecycle, opens the gateway and syncs slash commands
Dependencies:
- github.com/disgoorg/disgo: high-level Discord bot framework
- github.com/disgoorg/disgo/voice: voice connection manager + DAVE
- github.com/thomas-vilte/dave-go: pure-Go DAVE/E2EE backend
Note: discordgo has been retired in favour of disgo because the latter is the only major Go Discord library with native DAVE (E2EE) support, which Discord enforces in many voice regions (close code 4017 without it).
Package sikasa: buttonctx.go Purpose: Defines ButtonCtx, the per-interaction context passed to button click handlers registered via Bot.OnButton. Provides reply, update, and defer helpers that mirror MsgCtx / CmdCtx so handler code stays uniform.
Key Components:
- ButtonCtx: handler context for a single button interaction
- Reply: sends a fresh ephemeral or public response
- Update: edits the message that hosted the button
- Defer: acks the interaction without producing a visible reply
Dependencies:
- github.com/disgoorg/disgo/discord: message and component types
- github.com/disgoorg/disgo/handler: ComponentEvent (interaction wrapper)
- github.com/disgoorg/disgo/events: underlying event type
Note: Discord requires a response within 3 seconds or the interaction fails. Reply, Update, and Defer all satisfy that requirement; long-running work should call Defer first and then Followup later.
Package sikasa: buttons.go Purpose: Provides Bot.OnButton, a thin sugar over disgo's handler.Mux.ButtonComponent that hands the user a ButtonCtx instead of raw disgo types. Pattern strings are passed straight through to disgo, so customID lookup uses the same chi-style {var} syntax.
Key Components:
- ButtonHandler: signature for sikasa-style button handlers
- Bot.OnButton(): registers a button route on the underlying router
Note: OnButton must be called BEFORE Bot.Start(), because the disgo handler.Mux is constructed there and routes are registered on it during Start. Calling OnButton after Start would panic.
Package sikasa: cmdctx.go Purpose: Defines CmdCtx, the per-invocation context passed to slash command handlers. Bundles the disgo handler.CommandEvent and SlashCommandInteractionData with reply helpers for text, embeds, and media so handlers stay one-liners.
Key Components:
- CmdCtx: per-invocation handler context
- newCmdCtx(): internal constructor; called by command.go's router glue
- Reply / ReplyEmbed / ReplyFile / ReplyURL: response helpers
- String / Int / Bool / User / Channel / Attachment: option accessors
- Defer / Followup: long-running command pattern
Dependencies:
- github.com/disgoorg/disgo/discord: message, embed, and option types
- github.com/disgoorg/disgo/handler: CommandEvent, the response surface
Note: Reply must be called within 3 seconds of the interaction firing; otherwise call Defer() first and finish with Followup().
Package sikasa: command.go Purpose: Provides a fluent CommandBuilder for declaring slash commands alongside their handlers in one place. Builders translate to disgo's discord.SlashCommandCreate at Start() time, and their handlers are wired into the disgo handler.Router for dispatch.
Key Components:
- CommandBuilder: fluent type for one slash command
- Bot.Command(): entry point that returns a fresh builder
- StringArg/IntArg/BoolArg/UserArg/ChannelArg/AttachmentArg: option helpers
- CmdHandler: function signature for command handlers
Dependencies:
- github.com/disgoorg/disgo/discord: ApplicationCommandCreate types
- github.com/disgoorg/disgo/handler: router and CommandEvent
Package sikasa: dep_installer.go Purpose: Obsoleted and moved to install_deps.go
Package sikasa: embed.go Purpose: Provides a fluent EmbedBuilder so callers can compose Discord embeds without dropping into the raw discord.Embed struct. Pairs with MsgCtx.ReplyEmbed / SendEmbed and CmdCtx.ReplyEmbed.
Key Components:
- EmbedBuilder: immutable, chainable wrapper over discord.Embed
- NewEmbed(): constructor; preferred entry point
- Build(): returns the underlying discord.Embed for direct use
Note: Discord enforces hard limits on embed fields. Setters do NOT truncate; they pass through whatever you give them. The package-level Truncate helper is provided for callers that want explicit clamping.
Package sikasa: errors.go Purpose: Exports sentinel errors used across the package so callers can distinguish failure modes via errors.Is() instead of string matching.
Key Components:
- Voice errors: ErrBotNotStarted, ErrInvalidGuildID, ErrInvalidChannelID, ErrNotInVoice, ErrNoAudio, ErrNotPaused
- Bot lifecycle: ErrEmptyToken
- Prefix command errors: ErrUnknownCommand, ErrMissingArg, ErrInvalidArg
Note: Wrapped errors (fmt.Errorf("%w: ...", ErrXxx, ...)) preserve the sentinel chain, so errors.Is(err, sikasa.ErrXxx) works on both bare and wrapped values.
Package sikasa: group.go Purpose: Implements logical command grouping to support clean modularization of bot commands, prefix commands, keywords, and buttons.
Key Components:
- Group: a logical namespace and routing wrapper
- Bot.Group(): entry point for registering a group
Dependencies:
- None
Package sikasa: install_deps.go Purpose: Handles automatic detection and installation of system-level shared library dependencies (FFmpeg/libavformat/libavutil) and CLI binaries (ffmpeg, yt-dlp, bun) required for the music/remux pipelines.
Key Components:
- ensureDependencies(): Performs pre-flight check and downloads missing dependencies
- ResolveBinaryPath(): Finds absolute paths of required CLI executables
Dependencies:
- archive/zip: for extracting bun zip archives
- net/http: for downloading binaries
- os: to read files and check states
- os/exec: to execute installation commands
- regexp: to parse OS release information
- runtime: to detect host OS
Package sikasa: keyword.go Purpose: Provides a fluent KeywordBuilder for matching plain-message content against a set of substrings or a regular expression and firing a handler when a match occurs.
Key Components:
- KeywordBuilder: fluent type for one keyword/regex rule
- Bot.OnKeyword(): substring-based matcher (case-insensitive)
- Bot.OnRegex(): regex-based matcher (compiled once at registration)
- MsgHandler: function signature for keyword handlers
Dependencies:
- regexp: standard library regex engine
Note: Multiple keyword rules may match the same message; all matching rules fire in registration order.
Package sikasa: msgctx.go Purpose: Defines MsgCtx, the per-message context passed to keyword/regex handlers. Bundles the disgo MessageCreate event with reply helpers for text, embeds, and media.
Key Components:
- MsgCtx: per-message handler context
- newMsgCtx(): internal constructor; called by bot.go's keyword dispatcher
- Reply / Send / ReplyFile / ReplyURL: response helpers
- AuthorMention / Author / ChannelID / GuildID: shortcuts to common fields
Dependencies:
- github.com/disgoorg/disgo/discord: message and reference types
- github.com/disgoorg/disgo/events: MessageCreate event
Note: Reply uses Discord's inline reply (message reference) so the bot's reply links back to the user's message; Send posts to the channel without the reference link.
Package sikasa: native_remux_go.go Purpose: Pure Go WebM/Opus to Ogg/Opus remuxer using github.com/remko/go-mkvparse (Matroska/WebM parser) and mccoy.space/g/ogg (Ogg encoder). No CGo or FFmpeg shared libraries needed.
Package sikasa: prefix.go Purpose: Provides a fluent PrefixBuilder for declaring text-based prefix commands (e.g. "!play song.mp3"). Mirrors the bot.Command builder so users get a consistent feel across slash and prefix invocation paths.
Key Components:
- PrefixBuilder: fluent type for one prefix command
- Bot.OnPrefix(): entry point that returns a fresh builder
- Bot.SetPrefix(): sets the global trigger prefix (default: "")
- StringArg/IntArg/BoolArg: option helpers, hybrid with ctx.Rest()
- PrefixHandler: function signature for prefix command handlers
- dispatchPrefix: internal MessageCreate router
Note: Last StringArg consumes the entire remaining message tail. Quote-aware tokenization (e.g. "!echo \"hello world\"" as a single arg) is intentionally out of scope for the MVP; callers can fall back to ctx.Rest() if they need the raw string.
Package sikasa: prefixctx.go Purpose: Defines PrefixCtx, the per-invocation context passed to prefix command handlers. Embeds *MsgCtx to inherit Reply/Send/React helpers, and adds typed accessors (String/Int/Bool) plus raw-token escape hatches (Args/Arg/Rest) so handlers can mix builder validation with manual parsing.
Key Components:
- PrefixCtx: per-invocation handler context, wraps MsgCtx
- String/Int/Bool: typed access to declared builder args
- Args/Arg/Rest: raw-token access for free-form parsing
- Name(): the resolved command name (not the alias)
Note: Reply* methods come from the embedded *MsgCtx; the prefix dispatcher is the only construction site, so these accessors are guaranteed non-nil for the user.
Package sikasa: ratelimit.go Purpose: Provides an in-memory sliding window rate limiter for commands and keywords.
Package sikasa: songcache.go Purpose: Implements an asynchronous, sequential audio prefetcher and sliding-window cache manager for YouTube tracks. Supports persistent local caches, prioritizing active tracks, and evicting out-of-window cache files.
Key Components:
- getCachePath(): Computes the MD5 filename for cache files.
- prefetchTrack(): Downloads YouTube streams and remuxes them to Ogg files.
- prefetchWorker(): Background goroutine that processes prefetches sequentially.
- getNextPrefetchTrack(): Selects the next prioritized track that needs caching.
- notifyPrefetch(): Wakes up the background worker.
- triggerPrefetch(): Recalculates sliding window, triggers worker, and evicts expired cache files.
Dependencies:
- context: Handling cancellation.
- crypto/md5: Generating unique cache keys.
- os: Creating directories and managing files.
- os/exec: Running yt-dlp and ffmpeg.
- path/filepath: Building cross-platform paths.
Package sikasa: voice.go Purpose: High-level API for joining Discord voice channels and playing audio sources (local files, YouTube URLs) on top of disgo's voice manager.
Key Components:
- VoiceManager: factory accessed via bot.Voice(); holds per-guild contexts
- VoiceCtx: handle returned by Join(); supports PlayFile, PlayYouTube, Pause, Resume, Stop, Leave
- PlaybackState: enum for the current state machine
Dependencies:
- github.com/disgoorg/disgo/voice: Conn, OpusFrameProvider, SpeakingFlags
- voice_ffmpeg.go, voice_ogg.go, voice_youtube.go: pipeline pieces
- voice_provider.go: bridges the pipeline to disgo
Note: Pause/Resume now happen at the OpusFrameProvider layer. The provider returns voice.SilenceAudioFrame while paused, so disgo's AudioSender keeps ticking and Discord does not drop the speaking session.
Package sikasa: voice_check.go Purpose: Shared "is the user allowed to control this voice session" guard used by prefix commands tagged with .RequireSameVoice() and by anyone who needs the same check from a slash handler.
Key Components:
- Bot.checkSameVoice(): inspects the cached voice states for the guild and returns ErrNotSameChannel when the invoker is not co-located with the bot
Note: When the bot is not connected to any voice channel in the guild the check passes. This lets initial-join commands like "play" still work; the command itself decides whether to join. Use this helper only after the caller has already checked for a guild context (DMs have no voice state).
Package sikasa: voice_ffmpeg.go Purpose: Manages FFmpeg subprocesses that produce Ogg-Opus output suitable for direct streaming into a Discord voice connection.
Key Components:
- ffmpegProcess: wraps an *exec.Cmd plus its stdout pipe
- spawnTranscode(): decode any input format and re-encode to Opus
- spawnPassthrough(): stream-copy already-Opus input (zero CPU cost)
- spawnFromStdin(): accept piped input from another process (yt-dlp)
Dependencies:
- os/exec: subprocess lifecycle
Note: All spawn functions configure FFmpeg to write Ogg-Opus to stdout. Stderr is silenced to avoid noise; for debugging, route it to os.Stderr.
Package sikasa: voice_ogg.go Purpose: Parses an Ogg-Opus byte stream from FFmpeg's stdout into individual Opus frames suitable for direct injection into discordgo's OpusSend channel.
Key Components:
- oggPageParser: reads Ogg pages from an io.Reader and returns segments
- NextFrame(): returns the next Opus packet (one 20ms frame)
Dependencies:
- encoding/binary: little-endian header parsing
- io: EOF handling
Note: Ogg page format is fixed-27-byte header followed by a segment table. Segments < 255 bytes mark a packet boundary; consecutive 255-byte segments are concatenated until a sub-255 segment ends the packet. The first two pages of an Opus stream are headers (OpusHead, OpusTags) and are skipped.
Package sikasa: voice_provider.go Purpose: Bridges the FFmpeg-driven Ogg-Opus pipeline (library-agnostic) into disgo's voice.OpusFrameProvider interface. The provider is owned by a VoiceCtx and replaced on every PlayFile / PlayYouTube call.
Key Components:
- streamProvider: implements voice.OpusFrameProvider for one ffmpeg run
Dependencies:
- github.com/disgoorg/disgo/voice: SilenceAudioFrame, OpusFrameProvider
Note: disgo's internal AudioSender pulls a frame every 20ms. While paused we return SilenceAudioFrame so the sender keeps ticking and Discord does not drop the speaking session. On EOF we return io.EOF and the AudioSender stops calling us; the next PlayX swap re-arms a fresh provider.
Package sikasa: voice_queue.go Purpose: Per-guild playback queue. Owned by VoiceCtx so each guild session has its own track list and cursor; multi-guild concurrency falls out of the existing voices map[snowflake.ID]*VoiceCtx.
Key Components:
- TrackKind: enum distinguishing local files from streamed URLs
- Track: a single queue entry (kind + source descriptor)
- queue: slice + cursor; supports advance, rewind, jump, clear
Note: queue is not exported. Callers manipulate the queue through VoiceCtx methods (Enqueue, Next, Prev, Skip, Queue, ClearQueue), which serialize access through VoiceCtx.mu. The queue itself is therefore not internally locked.
Package sikasa: voice_recovery.go Purpose: Watches the slog stream for DAVE/voice errors that indicate the session has desynchronized ("no active epoch") and triggers a Reconnect() on the affected VoiceCtx. The watcher is layered on top of the user's own slog handler so log output is unchanged.
Key Components:
- recoveryHandler: slog.Handler that wraps another handler, sniffs error records, and dispatches recoveries
- installRecovery: installs the wrapper around the bot's logger
Note: Reconnect is rate-limited per guild (one attempt every 30s) so a flood of "no active epoch" errors does not stack reconnect attempts on top of each other.
Package sikasa: voice_state.go Purpose: Handles persistent saving and restoring of voice playback state to survive bot restarts and network reconnects.
Key Components:
- PersistedState: Struct serialized to JSON representing the queue and playback state
- Persist(): Saves the current VoiceCtx state to disk
- deleteState(): Removes the saved state file when leaving a channel
- recoveryWorker(): Periodically checks and recovers lost voice connections
- runRecovery(): Reads disk state files and resumes playback or triggers reconnects
Dependencies:
- encoding/json: Marshalling state to JSON format
- os: File and directory operations
- path/filepath: Building file paths
Package sikasa: voice_youtube.go Purpose: Handles YouTube playback stream spawning and search queries. Streams raw Opus audio using a simplified pipeline (yt-dlp -> ffmpeg copy), and uses the pure-Go ytm-go library for fast search metadata resolving.
Key Components:
- spawnYouTube(): Spawns yt-dlp (with optional Bun JS runtime) and ffmpeg to remux Opus
- SearchYouTube(): Performs search resolving natively via ytm-go InnerTube client
- IsHTTPURL(): Heuristic to check if query is a URL
- probeYouTubeEntries(): Extracts info for playlist expansions
Dependencies:
- context: Subprocess lifecycle cancellation
- os: Subprocess detection and file check
- os/exec: Subprocess execution
- path/filepath: Cross-platform paths
- github.com/dlcuy22/ytm-go: Natively resolves YouTube Music metadata
Package sikasa: ytsearch.go Purpose: Holds the per-bot YouTube search session map. Each session captures the candidate tracks shown to one user via an interactive picker and the metadata (invoker, expiry) needed to authorize a button click. Bound to the bot lifetime; sessions auto-expire after ytSearchTTL.
Key Components:
- ytSearchSession: per-picker state: candidates, invoker, expiry
- Bot.NewYTSearchSession: registers a fresh session and returns its id
- Bot.YTSearchSession: looks up an active session by id
- Bot.consumeYTSearch: atomic lookup-and-delete for click handlers
- BuildYTSearchEmbed: renders the picker embed and button row
Note: customID layout: "sikasa:ytsearch:<sessionID>:<idx>". The session id is a 12-char base32 random string; collisions are vanishingly small for the active window. Sessions live in memory only; restarting the bot invalidates outstanding pickers (acceptable trade since the parent message visually loses its interactive state on restart anyway).
Index ¶
- Constants
- Variables
- func IsHTTPURL(s string) bool
- func RemuxStream(reader io.Reader, outPath string) error
- func RemuxStreamGo(reader io.Reader, outPath string) error
- func RemuxStreamToWriter(reader io.Reader, writer io.Writer) error
- func RemuxStreamToWriterGo(reader io.Reader, writer io.Writer) error
- func ResolveBinaryPath(name string) string
- func Truncate(s string, n int) string
- type Bot
- func (b *Bot) Command(name, description string) *CommandBuilder
- func (b *Bot) ConsumeYTSearch(id string) *ytSearchSession
- func (b *Bot) Disgo() *bot.Client
- func (b *Bot) Group(name string, setup func(*Group)) *Bot
- func (b *Bot) NewYTSearchSession(invoker, guildID snowflake.ID, tracks []Track) string
- func (b *Bot) NewYTSearchSessionMode(invoker, guildID snowflake.ID, tracks []Track, mode YTSearchMode) string
- func (b *Bot) OnButton(pattern string, h ButtonHandler) *Bot
- func (b *Bot) OnKeyword(terms ...string) *KeywordBuilder
- func (b *Bot) OnPrefix(name, description string) *PrefixBuilder
- func (b *Bot) OnRegex(pattern string) *KeywordBuilder
- func (b *Bot) RemuxMode() string
- func (b *Bot) SetPrefix(p string) *Bot
- func (b *Bot) Shutdown()
- func (b *Bot) Start() error
- func (b *Bot) Stop() error
- func (b *Bot) Voice() *VoiceManager
- func (b *Bot) WithCache(dir string, maxAhead int) *Bot
- func (b *Bot) WithGuild(guildID string) *Bot
- func (b *Bot) WithIntents(intents gateway.Intents) *Bot
- func (b *Bot) WithJSRuntime(name string, path ...string) *Bot
- func (b *Bot) WithLogger(l *log.Logger) *Bot
- func (b *Bot) WithMusicLogInterval(d time.Duration) *Bot
- func (b *Bot) WithRemuxMode(mode string) *Bot
- func (b *Bot) WithSlog(l *slog.Logger) *Bot
- func (b *Bot) WithVerbose(levels ...slog.Level) *Bot
- func (b *Bot) WithoutCache() *Bot
- func (b *Bot) YTSearchSession(id string) *ytSearchSession
- type ButtonCtx
- func (c *ButtonCtx) Author() discord.User
- func (c *ButtonCtx) AuthorID() string
- func (c *ButtonCtx) Bot() *Bot
- func (c *ButtonCtx) ChannelID() string
- func (c *ButtonCtx) ClearComponents() error
- func (c *ButtonCtx) CustomID() string
- func (c *ButtonCtx) Data() discord.ButtonInteractionData
- func (c *ButtonCtx) Defer(ephemeral bool) error
- func (c *ButtonCtx) DeferUpdate() error
- func (c *ButtonCtx) Event() *handler.ComponentEvent
- func (c *ButtonCtx) GuildID() string
- func (c *ButtonCtx) Reply(text string) error
- func (c *ButtonCtx) ReplyEmbed(embed any) error
- func (c *ButtonCtx) ReplyPublic(text string) error
- func (c *ButtonCtx) Update(text string) error
- func (c *ButtonCtx) UpdateEmbed(embed any) error
- func (c *ButtonCtx) Var(name string) string
- func (c *ButtonCtx) Vars() map[string]string
- type ButtonHandler
- type CmdCtx
- func (c *CmdCtx) Attachment(name string) discord.Attachment
- func (c *CmdCtx) Author() discord.User
- func (c *CmdCtx) Bool(name string) bool
- func (c *CmdCtx) Bot() *Bot
- func (c *CmdCtx) Channel(name string) discord.ResolvedChannel
- func (c *CmdCtx) ChannelID() string
- func (c *CmdCtx) Data() discord.SlashCommandInteractionData
- func (c *CmdCtx) Defer(ephemeral bool) error
- func (c *CmdCtx) Event() *handler.CommandEvent
- func (c *CmdCtx) Followup(text string) error
- func (c *CmdCtx) GuildID() string
- func (c *CmdCtx) Int(name string) int64
- func (c *CmdCtx) Reply(text string) error
- func (c *CmdCtx) ReplyEmbed(embed discord.Embed) error
- func (c *CmdCtx) ReplyEphemeral(text string) error
- func (c *CmdCtx) ReplyFile(content, filePath string) error
- func (c *CmdCtx) ReplyURL(content, url, fileName string) error
- func (c *CmdCtx) String(name string) string
- func (c *CmdCtx) User(name string) discord.User
- type CmdHandler
- type ColorHandler
- type CommandBuilder
- func (c *CommandBuilder) AttachmentArg(name, description string, required bool) *CommandBuilder
- func (c *CommandBuilder) BoolArg(name, description string, required bool) *CommandBuilder
- func (c *CommandBuilder) ChannelArg(name, description string, required bool) *CommandBuilder
- func (c *CommandBuilder) Handle(h CmdHandler) *CommandBuilder
- func (c *CommandBuilder) IntArg(name, description string, required bool) *CommandBuilder
- func (c *CommandBuilder) StringArg(name, description string, required bool) *CommandBuilder
- func (c *CommandBuilder) UserArg(name, description string, required bool) *CommandBuilder
- type EmbedBuilder
- func (b *EmbedBuilder) Author(name, iconURL, url string) *EmbedBuilder
- func (b *EmbedBuilder) Build() discord.Embed
- func (b *EmbedBuilder) ClearFields() *EmbedBuilder
- func (b *EmbedBuilder) Color(c int) *EmbedBuilder
- func (b *EmbedBuilder) Description(s string) *EmbedBuilder
- func (b *EmbedBuilder) Field(name, value string, inline bool) *EmbedBuilder
- func (b *EmbedBuilder) Footer(text, iconURL string) *EmbedBuilder
- func (b *EmbedBuilder) Image(url string) *EmbedBuilder
- func (b *EmbedBuilder) Now() *EmbedBuilder
- func (b *EmbedBuilder) Thumbnail(url string) *EmbedBuilder
- func (b *EmbedBuilder) Timestamp(t time.Time) *EmbedBuilder
- func (b *EmbedBuilder) Title(s string) *EmbedBuilder
- func (b *EmbedBuilder) URL(u string) *EmbedBuilder
- type Group
- func (g *Group) Bot() *Bot
- func (g *Group) Command(name, description string) *CommandBuilder
- func (g *Group) Name() string
- func (g *Group) OnButton(pattern string, h ButtonHandler) *Group
- func (g *Group) OnKeyword(terms ...string) *KeywordBuilder
- func (g *Group) OnPrefix(name, description string) *PrefixBuilder
- func (g *Group) OnRegex(pattern string) *KeywordBuilder
- type Intents
- type KeywordBuilder
- type MsgCtx
- func (c *MsgCtx) Author() discord.User
- func (c *MsgCtx) AuthorMention() string
- func (c *MsgCtx) Bot() *Bot
- func (c *MsgCtx) ChannelID() string
- func (c *MsgCtx) Content() string
- func (c *MsgCtx) Event() *events.MessageCreate
- func (c *MsgCtx) GuildID() string
- func (c *MsgCtx) Message() discord.Message
- func (c *MsgCtx) NewEmbed() *EmbedBuilder
- func (c *MsgCtx) React(emoji string) error
- func (c *MsgCtx) Reply(text string) error
- func (c *MsgCtx) ReplyEmbed(embed any) error
- func (c *MsgCtx) ReplyFile(content, filePath string) error
- func (c *MsgCtx) ReplyURL(content, url, fileName string) error
- func (c *MsgCtx) Send(text string) error
- func (c *MsgCtx) SendEmbed(embed any) error
- func (c *MsgCtx) SendEmbedWithButtons(embed any, components ...discord.InteractiveComponent) error
- type MsgHandler
- type PersistedState
- type PlaybackState
- type PrefixBuilder
- func (p *PrefixBuilder) Aliases(names ...string) *PrefixBuilder
- func (p *PrefixBuilder) BoolArg(name, description string, required bool) *PrefixBuilder
- func (p *PrefixBuilder) Handle(h PrefixHandler) *PrefixBuilder
- func (p *PrefixBuilder) IntArg(name, description string, required bool) *PrefixBuilder
- func (p *PrefixBuilder) RequireSameVoice() *PrefixBuilder
- func (p *PrefixBuilder) StringArg(name, description string, required bool) *PrefixBuilder
- type PrefixCtx
- type PrefixHandler
- type RateLimitConfig
- type RemuxMode
- type Track
- type TrackKind
- type VoiceCtx
- func (v *VoiceCtx) Bot() *Bot
- func (v *VoiceCtx) ClearQueue()
- func (v *VoiceCtx) Cursor() int
- func (v *VoiceCtx) Disgo() voice.Conn
- func (v *VoiceCtx) Enqueue(t Track) (position int, started bool, err error)
- func (v *VoiceCtx) InsertNext(t Track) (position int, started bool, err error)
- func (v *VoiceCtx) InsertNextYouTube(url string) (firstPos, added int, started bool, err error)
- func (v *VoiceCtx) IsPlaying() bool
- func (v *VoiceCtx) JumpTo(index int) (Track, error)
- func (v *VoiceCtx) Leave() error
- func (v *VoiceCtx) Next() (Track, bool, error)
- func (v *VoiceCtx) Now() (Track, bool)
- func (v *VoiceCtx) Pause() error
- func (v *VoiceCtx) Persist()
- func (v *VoiceCtx) PlayFile(path string) (position int, started bool, err error)
- func (v *VoiceCtx) PlayYouTube(url string) (firstPos, added int, started bool, err error)
- func (v *VoiceCtx) Prev() (Track, error)
- func (v *VoiceCtx) Queue() []Track
- func (v *VoiceCtx) Reconnect() error
- func (v *VoiceCtx) Resume() error
- func (v *VoiceCtx) SetAnnounceChannel(channelID string) *VoiceCtx
- func (v *VoiceCtx) Shuffle() error
- func (v *VoiceCtx) Skip() (Track, bool, error)
- func (v *VoiceCtx) Source() string
- func (v *VoiceCtx) State() PlaybackState
- func (v *VoiceCtx) Stop() error
- func (v *VoiceCtx) WithJSRuntime(name string, path ...string) *VoiceCtx
- func (v *VoiceCtx) WithRemuxMode(mode string) *VoiceCtx
- type VoiceManager
- type YTSearchMode
Constants ¶
const ( IntentsAll = gateway.IntentsAll IntentsNonPrivileged = gateway.IntentsNonPrivileged IntentsPrivileged = gateway.IntentsPrivileged IntentsNone = gateway.IntentsNone )
Re-exported intent bundles. Pass these to WithIntents() to control which gateway events the bot subscribes to.
Note: disgo splits intents into privileged (members, presences, message content) and non-privileged groups. IntentsAll OR's both together so the bot subscribes to everything; this requires the privileged intents to be enabled in the Developer Portal.
const ( EmbedTitleMaxLen = 256 EmbedDescriptionMaxLen = 4096 EmbedFieldNameMaxLen = 256 EmbedFieldValueMaxLen = 1024 EmbedAuthorNameMaxLen = 256 EmbedFieldsMax = 25 )
Discord embed limits, surfaced as constants so callers can clamp values before hitting the API. https://discord.com/developers/docs/resources/message
Variables ¶
var ( // ErrBotNotStarted is returned by APIs that require a live gateway // connection (most voice operations) when invoked before Bot.Start(). ErrBotNotStarted = errors.New("sikasa: bot not started yet") // ErrInvalidGuildID is returned when a guild snowflake string fails // to parse. Wrapped with the offending value for context. ErrInvalidGuildID = errors.New("sikasa: invalid guild id") // ErrInvalidChannelID is returned when a channel snowflake string // fails to parse. Wrapped with the offending value for context. ErrInvalidChannelID = errors.New("sikasa: invalid channel id") // ErrNotInVoice is returned by voice operations that require an // active voice connection for the guild but find none. ErrNotInVoice = errors.New("sikasa: not connected to a voice channel") // ErrNoAudio is returned when a playback control method (Pause) // runs while nothing is playing. ErrNoAudio = errors.New("sikasa: no audio playing") // ErrNotPaused is returned by Resume() when the stream is not in // the paused state. ErrNotPaused = errors.New("sikasa: not paused") // ErrEmptyToken is returned by New() when the supplied token is "". ErrEmptyToken = errors.New("sikasa: empty token") // ErrUnknownCommand is the sentinel for prefix-dispatch lookups that // fail. The dispatcher renders it to the user as a reply. ErrUnknownCommand = errors.New("sikasa: unknown prefix command") // ErrMissingArg signals that a required builder argument was not // supplied in the user's message. ErrMissingArg = errors.New("sikasa: missing required argument") // ErrInvalidArg signals that a supplied argument failed type parsing // (e.g. IntArg given non-numeric text). ErrInvalidArg = errors.New("sikasa: invalid argument value") // ErrQueueEmpty is returned when a queue navigation method (Next, Prev, // Skip) has nothing more to advance to. ErrQueueEmpty = errors.New("sikasa: queue is empty") // ErrNoPrevious is returned by Prev() when the queue cursor is already // at the first track. ErrNoPrevious = errors.New("sikasa: no previous track") // ErrNotSameChannel is returned by guards that require the invoking user // to be in the same voice channel as the bot. ErrNotSameChannel = errors.New("sikasa: you must be in the same voice channel as the bot") )
Functions ¶
func IsHTTPURL ¶
IsHTTPURL is a cheap heuristic that decides whether a user-supplied string should be treated as a URL or a search query.
params:
s: the raw user input
returns:
bool: true when s looks like an HTTP(S) URL
func RemuxStreamGo ¶ added in v1.1.0
RemuxStreamGo reads WebM/Opus data from an io.Reader and writes Ogg/Opus to a local file using the pure-Go remuxer (go-mkvparse + mccoy.space/g/ogg).
func RemuxStreamToWriterGo ¶ added in v1.1.0
RemuxStreamToWriterGo reads WebM/Opus data from an io.Reader and writes Ogg/Opus to the provided io.Writer using the pure-Go remuxer.
func ResolveBinaryPath ¶
ResolveBinaryPath resolves the absolute path of a binary. It first checks the system PATH, and if not found, checks the local Sikasa bin directory.
params:
name: binary name (e.g. "yt-dlp", "ffmpeg", "bun")
returns:
string: resolved path or name if not found
Types ¶
type Bot ¶
type Bot struct {
// contains filtered or unexported fields
}
Bot is the high-level wrapper around disgo's bot.Client.
Key Fields:
- token: raw bot token; consumed only at Start()
- intents: gateway intents bitmask
- guildID: optional dev-guild for instant command sync; zero means global
- cmds: registered command builders, flushed to Discord on Start()
- kws: registered keyword matchers, evaluated on every MessageCreate
- prefix: global text prefix that triggers PrefixBuilder dispatch; empty string disables prefix routing entirely
- prefixes: registered prefix command builders
- prefixIndex: lookup table built at Start(); keys include both names and aliases (lower-cased)
- client: the live disgo client; nil until Start() succeeds
- voices: per-guild voice contexts, keyed by guild ID
- slog: structured logger handed to disgo (gateway, voice, REST)
Note: Not safe for concurrent registration; build all commands and keywords before calling Start(). After Start, the underlying client is goroutine-safe.
func New ¶
New constructs a Bot with the given token. The "Bot " prefix is added by disgo internally, so pass the raw token from the Developer Portal.
params:
token: the Discord bot token from the Developer Portal
returns:
*Bot: a configured Bot ready for command/keyword registration
error: reserved for future validation; currently always nil
func (*Bot) Command ¶
func (b *Bot) Command(name, description string) *CommandBuilder
Command registers a new slash command and returns its builder. Chain arg helpers and Handle() to finish the definition.
params:
name: the command name as it appears after the slash
description: the user-visible description
returns:
*CommandBuilder: builder for further configuration
func (*Bot) ConsumeYTSearch ¶
ConsumeYTSearch atomically looks up a session and removes it from the registry. Picker handlers should consume on success or cancel so the same button cannot fire twice.
params:
id: session id
returns:
*ytSearchSession: the session, or nil if missing/expired
func (*Bot) Disgo ¶
Disgo returns the underlying *bot.Client as an escape hatch for features the wrapper does not cover (sharding, manual REST calls, advanced events, etc). Returns nil before Start() has been called.
returns:
*bot.Client: the live disgo client, or nil if the bot has not started
func (*Bot) Group ¶
Group registers a sub-group of commands, keywords, and handlers. The setup function is called immediately with the new Group.
params:
name: the group's name
setup: registration callback
returns:
*Bot: the bot instance, for chaining
func (*Bot) NewYTSearchSession ¶
NewYTSearchSession registers a picker session and returns its id. Use the returned id to build button customIDs in the form "sikasa:ytsearch:<id>:<idx>".
params:
invoker: user who triggered the search; only this user may click
guildID: guild where the search was issued (must be non-zero)
tracks: candidate tracks to display
returns:
string: session id
NewYTSearchSession registers a picker session and returns its id. Use the returned id to build button customIDs in the form "sikasa:ytsearch:<id>:<idx>". Mode defaults to YTSearchModeEnqueue.
params:
invoker: user who triggered the search; only this user may click
guildID: guild where the search was issued (must be non-zero)
tracks: candidate tracks to display
returns:
string: session id
func (*Bot) NewYTSearchSessionMode ¶
func (b *Bot) NewYTSearchSessionMode(invoker, guildID snowflake.ID, tracks []Track, mode YTSearchMode) string
NewYTSearchSessionMode is NewYTSearchSession with an explicit hand-off mode. Use YTSearchModeInsertNext to wire a search picker into a "play next" prefix command without rebuilding the dispatcher.
params:
invoker, guildID, tracks: see NewYTSearchSession
mode: how the click handler should hand the choice off to voice
returns:
string: session id
func (*Bot) OnButton ¶
func (b *Bot) OnButton(pattern string, h ButtonHandler) *Bot
OnButton registers a handler for button clicks whose customID matches the given chi-style pattern (e.g. "/sikasa/ytsearch/{session}/{idx}"). Path variables are accessible from the ButtonCtx via Var(name) or Vars().
params:
pattern: customID pattern, supports {var} placeholders
h: handler invoked with a *ButtonCtx
returns:
*Bot: receiver, for chaining
Note: Must be called before Bot.Start(). Handlers registered after Start() will not be wired into the live router.
func (*Bot) OnKeyword ¶
func (b *Bot) OnKeyword(terms ...string) *KeywordBuilder
OnKeyword registers a substring-based keyword rule. Matching is case-insensitive and uses Contains semantics, so "hello" matches "hello world" and "Well, Hello!" alike.
params:
terms: one or more substrings; the rule fires if ANY are present
returns:
*KeywordBuilder: builder for attaching a handler via Reply()
func (*Bot) OnPrefix ¶
func (b *Bot) OnPrefix(name, description string) *PrefixBuilder
OnPrefix registers a new prefix command and returns its builder. Chain arg helpers and Handle() to finish the definition.
params:
name: the command name (without the prefix)
description: a short user-visible description
returns:
*PrefixBuilder: builder for further configuration
func (*Bot) OnRegex ¶
func (b *Bot) OnRegex(pattern string) *KeywordBuilder
OnRegex registers a regex-based message rule. The pattern is compiled once at registration; an invalid pattern panics here rather than later inside the message dispatcher.
params:
pattern: a Go regexp/RE2 pattern
returns:
*KeywordBuilder: builder for attaching a handler via Reply()
func (*Bot) RemuxMode ¶
RemuxMode returns the configured default remuxing mode for the bot.
returns:
string: the configured remuxing mode ("ffmpeg", "native", or "native-go")
func (*Bot) SetPrefix ¶
SetPrefix sets the global text prefix that triggers PrefixBuilder dispatch. An empty string disables prefix dispatch entirely (the default).
params:
p: the prefix string (e.g. "!", "?", "k!")
returns:
*Bot: receiver, for chaining
func (*Bot) Shutdown ¶
func (b *Bot) Shutdown()
Shutdown cleans up all active connections, stops background workers, and exits the process immediately. This prevents the process from getting stuck on terminal interruption (Ctrl-C).
func (*Bot) Start ¶
Start builds the disgo client, opens the gateway, syncs slash commands, and wires up keyword/message dispatchers. Returns once the gateway handshake is complete; events run in disgo-managed goroutines from there.
returns:
error: if client construction, gateway open, or command sync fails
func (*Bot) Stop ¶
Stop closes voice connections and the gateway. Always call this via defer after Start().
returns:
error: reserved for future error paths; currently always nil
func (*Bot) Voice ¶
func (b *Bot) Voice() *VoiceManager
Voice returns the bot's VoiceManager, the entry point for joining voice channels and starting playback.
returns:
*VoiceManager: helper for voice channel operations
func (*Bot) WithCache ¶
WithCache configures the sliding window audio prefetching cacher.
params:
dir: cache directory path (e.g. "sikasa-data/audiocache")
maxAhead: number of tracks to prefetch ahead of the current track
returns:
*Bot: receiver, for chaining
func (*Bot) WithGuild ¶
WithGuild scopes slash command registration to a single guild. Per-guild commands sync instantly, which is ideal during development. Leave unset for global commands (which can take up to an hour to propagate).
params:
guildID: the Discord guild snowflake; accepts the same string form
that the Developer Portal and Discord client display
returns:
*Bot: receiver, for chaining
func (*Bot) WithIntents ¶
WithIntents sets the gateway intents. Must be called before Start().
params:
intents: bitmask of gateway.Intent values
returns:
*Bot: receiver, for chaining
func (*Bot) WithJSRuntime ¶ added in v1.1.0
WithJSRuntime selects a JavaScript runtime for yt-dlp's signature decryption. Pass the runtime name and optionally its path. Supported names: "bun", "deno", "quickjs". When path is empty the runtime is auto-resolved via PATH.
Examples:
bot.WithJSRuntime("quickjs")
bot.WithJSRuntime("bun")
bot.WithJSRuntime("bun", "/usr/local/bin/bun")
bot.WithJSRuntime("deno")
Calling WithJSRuntime("") or omitting it entirely lets yt-dlp use its built-in default (no --js-runtimes flag).
params:
name: runtime name ("bun", "deno", "quickjs", or "" to disable)
path: optional absolute path to the runtime binary
returns:
*Bot: receiver, for chaining
func (*Bot) WithLogger ¶
WithLogger swaps the default logger. Pass nil to silence output.
params:
l: standard library logger
returns:
*Bot: receiver, for chaining
func (*Bot) WithMusicLogInterval ¶
WithMusicLogInterval tunes the logging interval for music process memory usage reports.
params:
d: reporting interval (e.g. 5s)
returns:
*Bot: receiver, for chaining
func (*Bot) WithRemuxMode ¶
WithRemuxMode configures the default remuxing strategy for new voice connections. Accepted values: "ffmpeg" (deprecated), "native" (deprecated), or "native-go" (default).
params:
mode: the remuxing mode
returns:
*Bot: receiver, for chaining
func (*Bot) WithSlog ¶
WithSlog sets the structured logger that gets handed to disgo (gateway, voice, REST). Use this to surface heartbeat warnings, voice state changes, and DAVE/E2EE handshake details. Pass nil to disable.
params:
l: a *slog.Logger; level controls verbosity
returns:
*Bot: receiver, for chaining
func (*Bot) WithVerbose ¶
WithVerbose enables structured logging on stderr for both the sikasa wrapper and the underlying disgo client. By default, it uses debug-level logging; this can be customized by passing a specific slog.Level.
params:
levels: optional slog.Level to override the default LevelDebug
returns:
*Bot: receiver, for chaining
func (*Bot) WithoutCache ¶
WithoutCache disables the audio prefetching cacher entirely.
returns:
*Bot: receiver, for chaining
func (*Bot) YTSearchSession ¶
YTSearchSession looks up an active session by id without removing it. Returns nil when the id is unknown or the session has expired.
params:
id: session id from NewYTSearchSession
returns:
*ytSearchSession: live session, or nil
type ButtonCtx ¶
type ButtonCtx struct {
// contains filtered or unexported fields
}
ButtonCtx is the context passed to a Bot.OnButton handler.
Key Fields:
- bot: parent Bot, exposed via Bot()
- event: the disgo *handler.ComponentEvent, exposed via Event()
- data: the button interaction data
- vars: path variables extracted from the customID pattern
- deferred: tracks whether the handler has called DeferUpdate, so subsequent Update/UpdateEmbed/ClearComponents go through the followup-style UpdateInteractionResponse instead of trying to respond a second time
Note: ButtonCtx values are constructed by the dispatcher; do not build them yourself. The embedded *handler.ComponentEvent is exposed as an escape hatch for advanced use (followups, file uploads, modals).
func (*ButtonCtx) AuthorID ¶
AuthorID returns the snowflake of the user who clicked the button. Convenient for permission checks against a stored invokerID.
func (*ButtonCtx) ClearComponents ¶
ClearComponents edits the message to drop every action row, leaving the content and embeds untouched. Use it when the buttons should disappear but the surrounding context stays visible.
returns:
error: from disgo
func (*ButtonCtx) Data ¶
func (c *ButtonCtx) Data() discord.ButtonInteractionData
Data returns the underlying button interaction data.
func (*ButtonCtx) Defer ¶
Defer acks the interaction without sending a visible reply. Use this when the handler will follow up later (more than 3 seconds out).
params:
ephemeral: when true, any later followup is only shown to the clicker
returns:
error: from disgo
func (*ButtonCtx) DeferUpdate ¶
DeferUpdate acks the click within Discord's 3 second window without visibly changing the message. Use it before doing slow work (yt-dlp probe, voice handshake, etc.) that would otherwise leave the user staring at "this interaction failed". After this call, Update / UpdateEmbed / ClearComponents transparently target the deferred response.
returns:
error: from disgo
func (*ButtonCtx) Event ¶
func (c *ButtonCtx) Event() *handler.ComponentEvent
Event returns the underlying *handler.ComponentEvent for advanced flows.
func (*ButtonCtx) Reply ¶
Reply sends a new message in response to the button click. The message is ephemeral (only visible to the clicker) by default, since most button flows already have visible context in the parent message.
params:
text: the message body
returns:
error: from disgo
func (*ButtonCtx) ReplyEmbed ¶
ReplyEmbed sends an ephemeral embed reply. Accepts a discord.Embed or a *EmbedBuilder; pass the builder directly without calling .Build().
params:
embed: a discord.Embed or *EmbedBuilder
returns:
error: from disgo or an unsupported-type error
func (*ButtonCtx) ReplyPublic ¶
ReplyPublic sends a non-ephemeral message in response to the click.
params:
text: the message body
returns:
error: from disgo
func (*ButtonCtx) Update ¶
Update edits the message that owns the clicked button. Useful for swapping a picker into a confirmation. Pass an empty string to clear the content; existing embeds and components are preserved unless cleared explicitly via Event().UpdateMessage with the appropriate ClearX calls. After DeferUpdate has been called, this method edits the deferred response instead of issuing a fresh one.
params:
text: new message body
returns:
error: from disgo
func (*ButtonCtx) UpdateEmbed ¶
UpdateEmbed edits the message to display the given embed and clears all component rows so the picker disappears once a choice is made. Accepts a discord.Embed or a *EmbedBuilder. After DeferUpdate has been called, the edit is dispatched against the deferred response.
params:
embed: a discord.Embed or *EmbedBuilder
returns:
error: from disgo or an unsupported-type error
type ButtonHandler ¶
ButtonHandler is the signature every button handler must satisfy.
type CmdCtx ¶
type CmdCtx struct {
// contains filtered or unexported fields
}
CmdCtx is the context passed to a slash command handler.
Key Fields:
- bot: the parent Bot, exposed via Bot()
- event: the disgo CommandEvent, exposed via Event()
- data: the parsed slash command interaction data
func (*CmdCtx) Attachment ¶
func (c *CmdCtx) Attachment(name string) discord.Attachment
Attachment returns the uploaded attachment for the named option, or the zero Attachment if missing.
func (*CmdCtx) Author ¶
Author returns the user who invoked the command, regardless of whether the invocation happened in a guild or in DMs.
func (*CmdCtx) Channel ¶
func (c *CmdCtx) Channel(name string) discord.ResolvedChannel
Channel returns the picked channel for the named option, or the zero ResolvedChannel if missing.
func (*CmdCtx) Data ¶
func (c *CmdCtx) Data() discord.SlashCommandInteractionData
Data returns the parsed slash command interaction data, useful when the option helpers below are insufficient (e.g. iterating Resolved entities).
func (*CmdCtx) Defer ¶
Defer acknowledges the interaction with a "Bot is thinking…" placeholder. Use this when the handler needs more than 3 seconds to produce a result. After deferring, finish the response with Followup or by editing the interaction response directly.
params:
ephemeral: if true, the eventual response is visible only to the invoker
returns:
error: from disgo
func (*CmdCtx) Event ¶
func (c *CmdCtx) Event() *handler.CommandEvent
Event returns the underlying disgo *handler.CommandEvent as an escape hatch for advanced response patterns (UpdateInteractionResponse, file followups, etc).
func (*CmdCtx) Followup ¶
Followup sends a follow-up message after a Defer. Discord allows follow-ups for up to 15 minutes after the original interaction.
params:
text: the follow-up body
returns:
error: from disgo
func (*CmdCtx) GuildID ¶
GuildID returns the guild snowflake as a string, or empty for DM invocations.
func (*CmdCtx) Reply ¶
Reply sends a plain-text response to the interaction. Must run within 3 seconds of the command firing; otherwise use Defer + Followup.
params:
text: the message body
returns:
error: from disgo
func (*CmdCtx) ReplyEmbed ¶
ReplyEmbed sends an embed response.
params:
embed: a fully-built Embed
returns:
error: from disgo
func (*CmdCtx) ReplyEphemeral ¶
ReplyEphemeral is like Reply but the response is visible only to the invoker.
params:
text: the message body
returns:
error: from disgo
func (*CmdCtx) ReplyFile ¶
ReplyFile sends a local file as a response. The file is opened, attached, and closed by Discord after upload.
params:
content: optional message body
filePath: path on disk to the file to upload
returns:
error: if the file cannot be opened or the response cannot be sent
func (*CmdCtx) ReplyURL ¶
ReplyURL streams a remote file directly into the response without writing to disk. Useful for image/media APIs.
params:
content: optional message body
url: remote file URL to fetch via HTTP GET
fileName: name shown to the recipient
returns:
error: if the fetch fails or the upstream returns non-200
type CmdHandler ¶
CmdHandler is the signature every slash command handler must satisfy. Returning an error logs it via the bot's logger; it does not propagate further, since by the time the handler runs the user has already been shown the bot's response.
type ColorHandler ¶
type ColorHandler struct {
// contains filtered or unexported fields
}
ColorHandler is a custom structured log handler that formats logs with ANSI colors.
func NewColorHandler ¶
func NewColorHandler(w io.Writer, lvl slog.Level) *ColorHandler
NewColorHandler constructs a ColorHandler that writes to w at the given level.
params:
w: the writer to write output to (e.g. os.Stderr)
lvl: the minimum logging level to display
returns:
*ColorHandler: initialized handler
func (*ColorHandler) Enabled ¶
Enabled reports whether the handler handles records at the given level.
params:
ctx: execution context
lvl: log level to check
returns:
bool: true if the level is enabled
func (*ColorHandler) Handle ¶
Handle processes the log record and prints it to the destination writer with ANSI colors.
params:
ctx: execution context
r: the log record containing message, level, attributes, and source
returns:
error: any writer write error
type CommandBuilder ¶
type CommandBuilder struct {
// contains filtered or unexported fields
}
CommandBuilder accumulates a slash command's definition and handler.
Key Fields:
- name, desc: surfaced to Discord
- opts: command options in the order added
- handler: invoked on InteractionApplicationCommand
Note: Builders are not thread-safe; finish configuration before Start().
func (*CommandBuilder) AttachmentArg ¶
func (c *CommandBuilder) AttachmentArg(name, description string, required bool) *CommandBuilder
AttachmentArg adds a file attachment option to the command. The uploaded file is resolvable via ctx.Attachment(name).
params:
name, description, required: see StringArg
returns:
*CommandBuilder: receiver, for chaining
func (*CommandBuilder) BoolArg ¶
func (c *CommandBuilder) BoolArg(name, description string, required bool) *CommandBuilder
BoolArg adds a boolean option to the command.
params:
name, description, required: see StringArg
returns:
*CommandBuilder: receiver, for chaining
func (*CommandBuilder) ChannelArg ¶
func (c *CommandBuilder) ChannelArg(name, description string, required bool) *CommandBuilder
ChannelArg adds a channel-picker option to the command.
params:
name, description, required: see StringArg
returns:
*CommandBuilder: receiver, for chaining
func (*CommandBuilder) Handle ¶
func (c *CommandBuilder) Handle(h CmdHandler) *CommandBuilder
Handle attaches the handler that runs when the command is invoked. This finalizes the builder.
params:
h: the handler invoked with a *CmdCtx
returns:
*CommandBuilder: receiver, for chaining
func (*CommandBuilder) IntArg ¶
func (c *CommandBuilder) IntArg(name, description string, required bool) *CommandBuilder
IntArg adds an integer option to the command.
params:
name, description, required: see StringArg
returns:
*CommandBuilder: receiver, for chaining
func (*CommandBuilder) StringArg ¶
func (c *CommandBuilder) StringArg(name, description string, required bool) *CommandBuilder
StringArg adds a string option to the command.
params:
name: option name shown in the command picker
description: option description
required: if true, Discord rejects invocations missing this arg
returns:
*CommandBuilder: receiver, for chaining
func (*CommandBuilder) UserArg ¶
func (c *CommandBuilder) UserArg(name, description string, required bool) *CommandBuilder
UserArg adds a user-picker option to the command. The picked user is resolvable via ctx.User(name).
params:
name, description, required: see StringArg
returns:
*CommandBuilder: receiver, for chaining
type EmbedBuilder ¶
type EmbedBuilder struct {
// contains filtered or unexported fields
}
EmbedBuilder is a fluent wrapper over discord.Embed. Methods mutate the receiver and return it, so chains compile to a single Embed value.
Note: Constructor functions return *EmbedBuilder so chains stay nil-safe even if zero-value method calls are accidentally inlined. Treat the builder as scoped to a single message; do not reuse a builder across concurrent goroutines.
func BuildYTSearchEmbed ¶
func BuildYTSearchEmbed(query, sessionID string, tracks []Track) (*EmbedBuilder, []discord.InteractiveComponent)
BuildYTSearchEmbed renders a picker embed plus a button ActionRow for the given candidate tracks. The customIDs encode the session id and a 0-based index, matching the route Bot.OnButton handlers should listen on.
params:
query: the original search string, shown in the embed title
sessionID: id returned by NewYTSearchSession
tracks: same slice passed into the session
returns:
*EmbedBuilder: ready to pass to ReplyEmbed/SendEmbed
[]discord.ButtonComponent: one button per track, plus a Cancel button
func NewEmbed ¶
func NewEmbed() *EmbedBuilder
NewEmbed returns a fresh builder. Identical to MsgCtx.NewEmbed() and CmdCtx.NewEmbed(); exposed at package level so callers can build embeds outside of a context.
func (*EmbedBuilder) Author ¶
func (b *EmbedBuilder) Author(name, iconURL, url string) *EmbedBuilder
Author sets the small author block at the top of the embed.
params:
name: display name (max 256 chars; required for the block to render)
iconURL: optional thumbnail beside the name; pass "" to omit
url: optional clickable link on the name; pass "" to omit
func (*EmbedBuilder) Build ¶
func (b *EmbedBuilder) Build() discord.Embed
Build returns the underlying discord.Embed. Most callers do not need this directly: ReplyEmbed / SendEmbed accept an EmbedBuilder via Build() internally. Exposed for callers that need to mix sikasa embeds with raw disgo APIs.
func (*EmbedBuilder) ClearFields ¶
func (b *EmbedBuilder) ClearFields() *EmbedBuilder
ClearFields removes any previously-added fields. Useful when reusing a builder template across multiple embeds.
func (*EmbedBuilder) Color ¶
func (b *EmbedBuilder) Color(c int) *EmbedBuilder
Color sets the left-hand stripe color. Accepts a hex int like 0x5865F2.
func (*EmbedBuilder) Description ¶
func (b *EmbedBuilder) Description(s string) *EmbedBuilder
Description sets the embed description (max 4096 chars).
func (*EmbedBuilder) Field ¶
func (b *EmbedBuilder) Field(name, value string, inline bool) *EmbedBuilder
Field appends a name/value field. Discord caps at 25 fields per embed; extra fields beyond the cap are silently dropped on the API side.
params:
name: field heading (max 256 chars)
value: field body (max 1024 chars)
inline: render side-by-side with adjacent inline fields
func (*EmbedBuilder) Footer ¶
func (b *EmbedBuilder) Footer(text, iconURL string) *EmbedBuilder
Footer sets the small footer text below the body.
params:
text: footer text (max 2048 chars; required for the block to render)
iconURL: optional icon beside the footer; pass "" to omit
func (*EmbedBuilder) Image ¶
func (b *EmbedBuilder) Image(url string) *EmbedBuilder
Image sets the large image rendered below the body.
func (*EmbedBuilder) Now ¶
func (b *EmbedBuilder) Now() *EmbedBuilder
Now sets the timestamp to the current wall-clock time.
func (*EmbedBuilder) Thumbnail ¶
func (b *EmbedBuilder) Thumbnail(url string) *EmbedBuilder
Thumbnail sets the small image rendered in the top-right corner.
func (*EmbedBuilder) Timestamp ¶
func (b *EmbedBuilder) Timestamp(t time.Time) *EmbedBuilder
Timestamp sets the small timestamp shown in the footer.
func (*EmbedBuilder) Title ¶
func (b *EmbedBuilder) Title(s string) *EmbedBuilder
Title sets the embed title (max 256 chars).
func (*EmbedBuilder) URL ¶
func (b *EmbedBuilder) URL(u string) *EmbedBuilder
URL makes the title clickable, pointing to the given URL.
type Group ¶
type Group struct {
// contains filtered or unexported fields
}
Group represents a logical namespace or category of bot commands, prefix commands, keywords, and buttons.
Key Fields:
- bot: the underlying Bot instance
- name: the group's name, useful for logging or debugging
func (*Group) Bot ¶
Bot returns the parent Bot instance of the Group.
returns:
*Bot: the underlying bot
func (*Group) Command ¶
func (g *Group) Command(name, description string) *CommandBuilder
Command registers a new slash command on the group's Bot and returns its builder.
params:
name: the slash command name
description: description of the slash command
returns:
*CommandBuilder: slash command builder
func (*Group) OnButton ¶
func (g *Group) OnButton(pattern string, h ButtonHandler) *Group
OnButton registers a button handler for a specific custom ID pattern on the group's Bot.
params:
pattern: path pattern for custom ID
h: handler for the button
returns:
*Group: the Group instance, for chaining
func (*Group) OnKeyword ¶
func (g *Group) OnKeyword(terms ...string) *KeywordBuilder
OnKeyword registers a substring-based keyword rule on the group's Bot.
params:
terms: keyword strings to match
returns:
*KeywordBuilder: keyword builder
func (*Group) OnPrefix ¶
func (g *Group) OnPrefix(name, description string) *PrefixBuilder
OnPrefix registers a prefix-based text command on the group's Bot.
params:
name: prefix command name
description: description of prefix command
returns:
*PrefixBuilder: prefix builder
func (*Group) OnRegex ¶
func (g *Group) OnRegex(pattern string) *KeywordBuilder
OnRegex registers a regex-based message rule on the group's Bot.
params:
pattern: regex pattern to match
returns:
*KeywordBuilder: keyword builder
type Intents ¶
Intents is an alias of disgo's gateway.Intents so callers do not need to import the gateway package directly for common cases.
type KeywordBuilder ¶
type KeywordBuilder struct {
// contains filtered or unexported fields
}
KeywordBuilder accumulates a keyword rule and its handler.
Key Fields:
- terms: substrings to match against (lower-cased on the message side)
- regex: compiled regex; mutually exclusive with terms in practice
- handler: invoked when a match is found
func (*KeywordBuilder) Reply ¶
func (k *KeywordBuilder) Reply(h MsgHandler, limits ...RateLimitConfig) *KeywordBuilder
Reply attaches the handler that runs when this rule matches.
params:
h: the handler invoked with a *MsgCtx
limits: optional rate limit config (e.g. sikasa.RateLimitInterval(3, time.Minute))
returns:
*KeywordBuilder: receiver, for chaining
func (*KeywordBuilder) ReplyFile ¶
func (k *KeywordBuilder) ReplyFile(content, filePath string, limits ...RateLimitConfig) *KeywordBuilder
ReplyFile is a shortcut for replying with a local file attachment.
params:
content: optional message body sent alongside the file
filePath: path to a file on disk
limits: optional rate limit config
returns:
*KeywordBuilder: receiver, for chaining
func (*KeywordBuilder) ReplyText ¶
func (k *KeywordBuilder) ReplyText(text string, limits ...RateLimitConfig) *KeywordBuilder
ReplyText is a shortcut for fixed text replies. The reply uses Discord's inline reply (message reference) so it links back to the user's message.
params:
text: the message body to send
limits: optional rate limit config
returns:
*KeywordBuilder: receiver, for chaining
type MsgCtx ¶
type MsgCtx struct {
// contains filtered or unexported fields
}
MsgCtx is the context passed to a keyword or regex handler.
Key Fields:
- bot: the parent Bot, exposed via Bot()
- event: the disgo MessageCreate event, exposed via Event()
func (*MsgCtx) AuthorMention ¶
AuthorMention returns the formatted mention string for the author.
func (*MsgCtx) Event ¶
func (c *MsgCtx) Event() *events.MessageCreate
Event returns the underlying disgo *events.MessageCreate as an escape hatch.
func (*MsgCtx) NewEmbed ¶
func (c *MsgCtx) NewEmbed() *EmbedBuilder
NewEmbed returns a fluent EmbedBuilder. Chain its setters and pass the result to ReplyEmbed / SendEmbed; you do not need to call .Build().
returns:
*EmbedBuilder: ready for .Title()/.Description()/.Field()/...
func (*MsgCtx) React ¶
React adds a reaction emoji to the user's message.
params:
emoji: a unicode emoji or a custom emoji in "name:id" format
returns:
error: from disgo
func (*MsgCtx) Reply ¶
Reply sends a plain-text inline reply to the user's message.
params:
text: the message body
returns:
error: from disgo
func (*MsgCtx) ReplyEmbed ¶
ReplyEmbed sends an embed as an inline reply. Accepts either a built discord.Embed or a sikasa *EmbedBuilder; pass the builder directly without calling .Build() yourself.
params:
embed: a discord.Embed or *EmbedBuilder
returns:
error: from disgo, or an error if the embed type is unsupported
func (*MsgCtx) ReplyFile ¶
ReplyFile sends a local file as an inline reply. The file is opened, attached, and closed automatically.
params:
content: optional message body sent alongside the file
filePath: path on disk to the file to upload
returns:
error: if the file cannot be opened or the message cannot be sent
func (*MsgCtx) ReplyURL ¶
ReplyURL streams a remote file directly into the reply without writing to disk.
params:
content: optional message body
url: remote file URL to fetch via HTTP GET
fileName: name shown to the recipient
returns:
error: if the fetch fails or the upstream returns non-200
func (*MsgCtx) Send ¶
Send posts a plain-text message to the same channel without referencing the user's message. Use this when the response is informational rather than a direct reply.
params:
text: the message body
returns:
error: from disgo
func (*MsgCtx) SendEmbed ¶
SendEmbed posts an embed to the same channel without referencing the user's message. Use it for informational announcements (queue listing, status panels) where an inline reply marker would be visual noise. Accepts either a discord.Embed or a *EmbedBuilder.
params:
embed: a discord.Embed or *EmbedBuilder
returns:
error: from disgo, or an error if the embed type is unsupported
func (*MsgCtx) SendEmbedWithButtons ¶
func (c *MsgCtx) SendEmbedWithButtons(embed any, components ...discord.InteractiveComponent) error
SendEmbedWithButtons posts an embed plus a single ActionRow of interactive components (typically buttons returned from BuildYTSearchEmbed or any custom picker). For multi-row component layouts, use Event() and build the message manually.
params:
embed: a discord.Embed or *EmbedBuilder
components: interactive components (max 5 per Discord ActionRow)
returns:
error: from disgo or an unsupported-embed-type error
type MsgHandler ¶
MsgHandler is the signature every message handler must satisfy.
type PersistedState ¶ added in v1.1.1
type PersistedState struct {
GuildID string `json:"guild_id"`
ChannelID string `json:"channel_id"`
AnnounceChannelID string `json:"announce_channel_id,omitempty"`
State int32 `json:"state"`
Cursor int `json:"cursor"`
Tracks []Track `json:"tracks"`
}
PersistedState represents the saved state of a guild voice playback session.
type PlaybackState ¶
type PlaybackState int32
PlaybackState describes the current state of a VoiceCtx.
const ( StateIdle PlaybackState = iota StatePlaying StatePaused StateStopped )
type PrefixBuilder ¶
type PrefixBuilder struct {
// contains filtered or unexported fields
}
PrefixBuilder accumulates a prefix command's definition and handler.
Key Fields:
- name, desc: user-visible name and description (description currently unused but reserved for a future help generator)
- aliases: extra names that resolve to this builder
- args: option list, parsed in registration order
- handler: invoked when a message matches the command
- requireSameVoice: when true, dispatch rejects callers who are not in the same voice channel as the bot for the guild
Note: Builders are not thread-safe; finish configuration before Start().
func (*PrefixBuilder) Aliases ¶
func (p *PrefixBuilder) Aliases(names ...string) *PrefixBuilder
Aliases attaches alternative names that dispatch to the same handler. For example, .Aliases("p", "pl") on a "play" command lets users type "!p" or "!pl" interchangeably.
params:
names: one or more alias strings
returns:
*PrefixBuilder: receiver, for chaining
func (*PrefixBuilder) BoolArg ¶
func (p *PrefixBuilder) BoolArg(name, description string, required bool) *PrefixBuilder
BoolArg adds a boolean option. Accepted truthy values: "true", "1", "yes", "y", "on". Falsy: "false", "0", "no", "n", "off". Anything else triggers ErrInvalidArg.
params:
name, description, required: see CommandBuilder.BoolArg
returns:
*PrefixBuilder: receiver, for chaining
func (*PrefixBuilder) Handle ¶
func (p *PrefixBuilder) Handle(h PrefixHandler) *PrefixBuilder
Handle attaches the handler that runs when this command is invoked.
params:
h: the handler invoked with a *PrefixCtx
returns:
*PrefixBuilder: receiver, for chaining
func (*PrefixBuilder) IntArg ¶
func (p *PrefixBuilder) IntArg(name, description string, required bool) *PrefixBuilder
IntArg adds an integer option to the command. Values are parsed via strconv.ParseInt; invalid values trigger ErrInvalidArg.
params:
name, description, required: see CommandBuilder.IntArg
returns:
*PrefixBuilder: receiver, for chaining
func (*PrefixBuilder) RequireSameVoice ¶
func (p *PrefixBuilder) RequireSameVoice() *PrefixBuilder
RequireSameVoice gates the command on the invoking user being in the same voice channel as the bot for that guild. When the bot is not in any voice channel, the command is allowed (so initial join commands like "play" can still run).
returns:
*PrefixBuilder: receiver, for chaining
func (*PrefixBuilder) StringArg ¶
func (p *PrefixBuilder) StringArg(name, description string, required bool) *PrefixBuilder
StringArg adds a string option to the command. The last StringArg in the declaration order consumes the entire remaining message tail, so a single trailing StringArg captures free-form text like "echo hello world".
params:
name, description, required: see CommandBuilder.StringArg
returns:
*PrefixBuilder: receiver, for chaining
type PrefixCtx ¶
type PrefixCtx struct {
*MsgCtx
// contains filtered or unexported fields
}
PrefixCtx is the context passed to a prefix command handler.
Key Fields:
- MsgCtx: embedded; provides Bot(), Event(), Reply(), Send(), etc.
- name: resolved canonical command name (Aliases are normalized away)
- args: parsed builder args, keyed by the names declared in StringArg / IntArg / BoolArg
- raw: every whitespace-separated token after the command name
- rest: the entire message tail after the command name, with original whitespace preserved
func (*PrefixCtx) Arg ¶
Arg returns the i-th token after the command name, or "" if i is out of range. Convenient when you only care about positions.
func (*PrefixCtx) Args ¶
Args returns every whitespace-separated token after the command name. Use this when builder args do not fit the parsing shape you need (e.g. a variadic list of user IDs).
func (*PrefixCtx) Bool ¶
Bool returns the value of a named BoolArg.
params:
name: the arg name as declared via BoolArg
returns:
bool: the parsed value, or false if absent
func (*PrefixCtx) Int ¶
Int returns the value of a named IntArg.
params:
name: the arg name as declared via IntArg
returns:
int64: the parsed value, or 0 if absent
func (*PrefixCtx) Name ¶
Name returns the canonical command name that matched, even if the user invoked the command via an alias.
type PrefixHandler ¶
PrefixHandler is the signature every prefix command handler must satisfy.
type RateLimitConfig ¶
RateLimitConfig represents the configuration for a rate limit. It specifies how many times an action can be performed within a given duration.
func RateLimitInterval ¶
func RateLimitInterval(count int, interval time.Duration) RateLimitConfig
RateLimitInterval is a helper to construct a RateLimitConfig.
params:
count: maximum number of allowed requests
interval: time window in which requests are counted
returns:
RateLimitConfig
type RemuxMode ¶
type RemuxMode string
RemuxMode describes the method used to remux/convert YouTube stream to Ogg-Opus.
const ( // RemuxFFmpeg executes external ffmpeg processes. // Deprecated: Use RemuxNativeGo instead. This mode is kept for compatibility // but may be removed in a future release. RemuxFFmpeg RemuxMode = "ffmpeg" // RemuxNative runs the native library remuxer using purego. // Deprecated: Use RemuxNativeGo instead. This mode is kept for compatibility // but may be removed in a future release. RemuxNative RemuxMode = "native" // RemuxNativeGo runs the pure-Go remuxer using go-mkvparse + mccoy.space/g/ogg. // This is the recommended and default remux mode. RemuxNativeGo RemuxMode = "native-go" )
type Track ¶
Track is a single queue entry.
Key Fields:
- Kind: selects the spawn strategy when this track plays
- Source: file path (TrackFile) or URL (TrackYouTube); used as the fallback label when Title is not populated
- Title: optional human-readable title; populated by metadata probes for TrackYouTube. Empty for TrackFile by default
- Author: optional uploader / channel / artist; populated alongside Title
func SearchYouTube ¶
SearchYouTube queries YouTube Music using the Go-native ytm-go library and returns the top-N results as Tracks. Bypasses yt-dlp for search.
params:
query: free-text search string
n: number of results to return
returns:
[]Track: candidate tracks in relevance order
error: innerTube query error
type VoiceCtx ¶
type VoiceCtx struct {
// contains filtered or unexported fields
}
VoiceCtx is the per-guild voice handle. All playback control flows through it.
Key Fields:
- conn: the underlying disgo voice.Conn
- state: atomic state (Idle / Playing / Paused / Stopped)
- provider: the active OpusFrameProvider; swapped on track change
- source: human-readable description of what is currently loaded
- queue: per-guild track list and cursor; auto-advances on EOF
- announceChannelID: optional channel where auto-advance announcements go; zero means announcements are disabled
Note: Methods are safe to call concurrently. The provider, source, queue, and announceChannelID fields are guarded by the mutex; state is atomic so quick reads (IsPlaying, State) are lock-free. Each guild gets its own VoiceCtx via Bot.voices, so queues are naturally isolated per session.
func (*VoiceCtx) ClearQueue ¶
func (v *VoiceCtx) ClearQueue()
ClearQueue empties the queue without affecting the currently playing track. Combine with Stop() to halt playback and reset state in one shot.
func (*VoiceCtx) Cursor ¶
Cursor returns the index of the currently playing track, or -1 if nothing has started yet.
func (*VoiceCtx) Disgo ¶
Disgo returns the underlying voice.Conn as an escape hatch for advanced operations (custom OpusFrameReceiver, raw UDP writes, etc).
func (*VoiceCtx) Enqueue ¶
Enqueue adds a Track to the queue. If nothing is currently playing the queue is advanced to this track and playback begins. Otherwise the track simply waits its turn.
params:
t: the track to enqueue
returns:
position: 0-based index of the new track in the queue
started: true if this call kicked off playback
error: spawn error if started==true and the pipeline fails
func (*VoiceCtx) InsertNext ¶
InsertNext inserts a track immediately after the currently playing one, shifting any later tracks down by one. When nothing is playing yet, the track lands at index 0 and playback starts. Useful for "play next" UX without disturbing what is currently audible.
params:
t: track to insert
returns:
position: 0-based index of the newly inserted track
started: true if this call kicked off playback (queue was idle)
error: spawn error when started==true and the pipeline fails
func (*VoiceCtx) InsertNextYouTube ¶
InsertNextYouTube probes a yt-dlp-resolvable URL (single video, playlist, channel) and inserts every resulting track immediately after the currently playing one in playlist order. Mirror of PlayYouTube's expand-then-enqueue flow but inserts instead of appending. Probe failure falls back to inserting the raw URL as a single track.
params:
url: any URL yt-dlp can resolve
returns:
firstPos: index of the first inserted track
added: number of tracks inserted (>=1)
started: true if the call kicked off playback (queue was idle)
error: spawn error when started==true and the pipeline fails
func (*VoiceCtx) JumpTo ¶
JumpTo moves the queue cursor to a specific 0-based index and plays that track. Out-of-range indices return ErrQueueEmpty without changing state.
params:
index: 0-based queue position to jump to
returns:
Track: the track that started playing
error: ErrQueueEmpty if index is out of range, or a spawn error
func (*VoiceCtx) Leave ¶
Leave closes the voice connection and removes this context from the bot's registry. After calling Leave, this VoiceCtx must not be reused.
returns:
error: always nil currently; reserved for future error paths
func (*VoiceCtx) Now ¶
Now returns the currently loaded track and true, or a zero Track and false if the queue has not started yet.
func (*VoiceCtx) Pause ¶
Pause stops sending audio frames. The FFmpeg process and parser stay alive, so Resume() picks up exactly where playback left off.
returns:
error: if no audio is currently playing
func (*VoiceCtx) Persist ¶ added in v1.1.1
func (v *VoiceCtx) Persist()
Persist saves the current VoiceCtx playback state and queue to disk.
func (*VoiceCtx) PlayFile ¶
PlayFile enqueues a local audio file. If nothing is currently playing the track starts immediately; otherwise it lands at the tail of the queue and auto-plays when the current track ends. Codec detection (.opus / .ogg passthrough vs. transcode) happens at spawn time, not enqueue time.
params:
path: filesystem path to the audio file
returns:
position: 0-based index of the track in the queue
started: true if this call began playback (queue was idle)
error: only if the queue manipulation fails (currently never)
func (*VoiceCtx) PlayYouTube ¶
PlayYouTube enqueues a yt-dlp-resolvable URL. Same enqueue-or-play semantics as PlayFile, with one extra wrinkle: playlist URLs are expanded into N tracks via yt-dlp --flat-playlist, so a single PlayYouTube call can append many queue entries at once. Probe failure is non-fatal; on error the raw URL is enqueued as a single track and the caller will see it as the bare link.
params:
url: any URL yt-dlp can resolve (single video, playlist, channel, ...)
returns:
firstPos: 0-based queue index of the first track added
added: number of tracks appended (1 for a single video, N for
a playlist)
started: true if this call began playback (queue was idle and the
first new track is now playing)
error: only if the queue manipulation fails (currently never)
func (*VoiceCtx) Prev ¶
Prev rewinds the queue cursor by one and plays that track. If the queue has been exhausted (cursor sits past the last track because the last track finished naturally), Prev jumps back to the last track instead of going all the way to the first. Returns ErrNoPrevious only when the queue is empty or the cursor is genuinely at index 0.
returns:
Track: the track that started playing
error: ErrNoPrevious or a spawn error
func (*VoiceCtx) Queue ¶
Queue returns a snapshot of the queued tracks. The current track (if any) is included at index Cursor().
func (*VoiceCtx) Reconnect ¶
Reconnect tears down the current voice connection and reopens one to the same channel, then resumes playback from the current track. Used to recover from DAVE epoch desync ("no active epoch" errors) where the connection remains physically open but encryption fails on every packet. The queue and cursor are preserved; only the audio handshake gets a fresh start.
returns:
error: if the channel cannot be re-resolved or the new handshake fails
Note: Resume from mid-track is not supported. The current track restarts from the beginning because FFmpeg has been torn down with the connection.
func (*VoiceCtx) Resume ¶
Resume continues a paused stream.
returns:
error: if the state is not Paused
func (*VoiceCtx) SetAnnounceChannel ¶
func (*VoiceCtx) Shuffle ¶
Shuffle randomizes the order of queued tracks after the currently playing one. Returns ErrQueueEmpty if there are no tracks waiting in the queue.
func (*VoiceCtx) Skip ¶
Skip stops the current track and plays the next one in the queue. If the queue has no further tracks the connection stays open but playback ends.
returns:
Track: the track that started playing (zero value if queue exhausted)
bool: true if a new track started, false if the queue is exhausted
error: spawn error if the next track fails to start
func (*VoiceCtx) Source ¶
Source returns a human-readable description of the current source (file path or URL); empty if nothing has been played yet.
func (*VoiceCtx) State ¶
func (v *VoiceCtx) State() PlaybackState
State returns the current playback state.
func (*VoiceCtx) Stop ¶
Stop halts playback and tears down the FFmpeg pipeline. The voice connection itself stays open so the next Play* call can reuse it. The queue is left intact; call ClearQueue() too if you want a fresh session.
returns:
error: always nil currently; reserved for future error paths
func (*VoiceCtx) WithJSRuntime ¶ added in v1.1.0
WithJSRuntime selects a JavaScript runtime for yt-dlp's signature decryption on this specific voice connection. See Bot.WithJSRuntime for accepted values.
params:
name: runtime name ("bun", "deno", "quickjs", or "" to disable)
path: optional absolute path to the runtime binary
returns:
*VoiceCtx: receiver, for chaining
func (*VoiceCtx) WithRemuxMode ¶
SetAnnounceChannel routes auto-advance announcements ("next track: ...", "queue ended") to the given Discord channel ID. Pass "" to disable. Manual Skip/Prev/Jump do not announce here, since the prefix handler that triggered them already replies in-place.
params:
channelID: snowflake of the text channel to post to, or "" to disable
returns:
*VoiceCtx: receiver, for chaining
WithRemuxMode configures the remuxing strategy for this voice connection. Accepted values: "ffmpeg" (deprecated), "native" (deprecated), or "native-go" (default).
params:
mode: the remuxing mode
returns:
*VoiceCtx: receiver, for chaining
type VoiceManager ¶
type VoiceManager struct {
// contains filtered or unexported fields
}
VoiceManager is returned by Bot.Voice(). It is the entry point for joining voice channels and tracks active VoiceCtx values per guild.
func (*VoiceManager) Get ¶
func (m *VoiceManager) Get(guildID string) *VoiceCtx
Get returns the active VoiceCtx for a guild, or nil if the bot is not connected to any voice channel there.
func (*VoiceManager) Join ¶
func (m *VoiceManager) Join(guildID, channelID string) (*VoiceCtx, error)
Join connects to a voice channel and returns a VoiceCtx for playback control. If the bot is already in a voice channel for the same guild, that connection is moved to the new channel via UpdateVoiceState instead of opening a second one.
params:
guildID: the guild snowflake the voice channel belongs to
channelID: the voice channel to join
returns:
*VoiceCtx: a handle for playback control
error: if the gateway voice handshake fails
type YTSearchMode ¶
type YTSearchMode int
YTSearchMode tags how the picker should hand off to the voice queue when a candidate is selected. Embedded in the session so a single OnButton route can drive both "enqueue" (k!yt) and "insert next" (k!insert) flows without separate customID namespaces.
const ( // YTSearchModeEnqueue appends the chosen track to the queue tail. YTSearchModeEnqueue YTSearchMode = iota // YTSearchModeInsertNext inserts the chosen track right after the // currently playing one, shifting later tracks down. YTSearchModeInsertNext )
Source Files
¶
- bot.go
- buttonctx.go
- buttons.go
- cmdctx.go
- command.go
- dep_installer.go
- embed.go
- errors.go
- group.go
- install_deps.go
- keyword.go
- msgctx.go
- native_remux_go.go
- native_remux_stub.go
- prefix.go
- prefixctx.go
- ratelimit.go
- songcache.go
- voice.go
- voice_check.go
- voice_ffmpeg.go
- voice_ogg.go
- voice_provider.go
- voice_queue.go
- voice_recovery.go
- voice_state.go
- voice_youtube.go
- ytsearch.go