incommands

package
v1.5.1-0...-475df33 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package incommands ports server/plugins/inputs/*.ts: the client-initiated slash commands (/join, /msg, /nick, etc). Node registers each module in a `Map<string, Plugin>` keyed by every alias it handles (server/plugins/ inputs/index.ts) and Client.input looks up `cmd` in that map, gating on `allowDisconnected` before invoking `plugin.input.apply(client, [network, chan, cmd, args, opts])`. Dispatch (registry.go) is that same lookup+gate, and each *.go file below mirrors one *.ts file's `input` function.

Node's `input` runs with `this` bound to the owning Client, which is how alias.ts reads/writes `this.config.clientSettings.aliases`, several commands call `this.save()`, and quit.ts/connect.ts reach into `this.networks`/`this.connectToNetwork`. internal/auth.Client does not yet own any live Network/Chan state (see its doc comment - server/client.ts's `networks` field is round-tripped as opaque JSON until a later stage wires real ownership), so this package cannot depend on a concrete Client. What each command needs from it is threaded through Deps instead, following the same nil-tolerant interface-seam pattern internal/irchandlers uses for STSUpdater:

  • Deps.Save persists the owning client's on-disk config; nil until Client ownership lands (client.save() becomes a no-op meanwhile).
  • Deps.Aliases abstracts config.clientSettings.aliases for alias.go; nil until the same wiring lands.
  • Deps.Connect/Deps.ConnectToNetwork stand in for `irc.connect()` and `client.connectToNetwork(...)` - dialing a fresh underlying connection is an orchestration concern (wiring ircbridge.NewBridge + irchandlers.Register + a goroutine running Bridge.Run) that doesn't exist as a callable unit yet outside tests; nil reports a clear "not available yet" error instead of silently no-op'ing.

Self-echo fabrication (msg.ts/notice.ts/action.ts's "if the network doesn't echo our own messages back, simulate it" branches, needed so the sender sees their own line without a server-assigned msgid when echo-message isn't negotiated) is done by publishing a synthetic event through ircbridge.Bridge.Publish, so it flows through the exact same irchandlers dispatch real events do rather than duplicating message-building logic here.

Not ported here, consistently: chan.loadMessages (Stage 8), and Chan. destroy's link-preview dereferencing (tied to Stage 9's prefetch reference counting) - each is the same kind of Client/later-stage concern irchandlers already deferred past Stage 6. client.mentions bookkeeping and ClientCertificate.remove are wired, via Deps.RemoveMentions/ Deps.RemoveNetwork respectively.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Commands

func Commands() []string

Commands returns every registered command name (without the leading '/'), mirroring index.ts's getCommands (minus the client-side-only and pass-through names, which aren't this package's concern).

func Dispatch

func Dispatch(deps Deps, network *model.Network, chn *model.Chan, cmd string, args []string, opts InputOpts) (ok bool)

Dispatch mirrors the userInputs.get(cmd) lookup, the connected/ allowDisconnected gate, and the invocation in Client.input (server/client.ts). ok reports whether cmd matched a registered command at all - a caller still needs to fall back to the pass-through commands list and the raw-IRC-line default Client.input applies for anything else, neither of which is this package's concern.

Types

type Alias

type Alias struct {
	Name    string
	Command string
}

Alias mirrors one entry of ClientSettings.aliases (`{name, command}`).

type AliasStore

type AliasStore interface {
	Aliases() []Alias
	SetAliases([]Alias) error
}

AliasStore abstracts the per-client alias list (config.clientSettings. aliases in server/client.ts) alias.go needs to read and persist. See the package doc comment for why this is a seam rather than a concrete Client dependency.

type Deps

type Deps struct {
	Hub    *wsproto.Hub
	Cfg    *config.Loaded
	NextID func() int

	// Stats renders Relay's local-only /relaystats output. It must not write to
	// the IRC connection; the command displays the returned text as a local
	// notice in the current Relay window.
	Stats func(network *model.Network) string

	StatsEnabled func() bool

	// NextChanID mirrors Client.createChannel's per-account channel id
	// counter - see internal/irchandlers.Deps's identical field for why
	// every openChannel call needs one.
	NextChanID func() int

	// STS lets disconnect.go/quit.go refresh an active policy's expiration
	// on quit, mirroring Network.Quit's sts parameter. nil is accepted
	// (skips the refresh) exactly like model.Network.Validate/Quit already
	// tolerate a nil STSLookup pending Stage 10's internal/sts.
	STS model.STSLookup

	// Save persists the owning client's on-disk config (client.save() in
	// Node). nil until Client owns live Network/Chan state - see the
	// package doc comment.
	Save func()

	// Aliases backs alias.go. nil until Client ownership lands.
	Aliases AliasStore

	Encryption EncryptionStore

	// Connect dials a fresh underlying connection for an existing,
	// currently-disconnected network (connect.ts's no-args branch,
	// `irc.connect()`). nil reports "not available yet" rather than
	// silently doing nothing - see the package doc comment.
	Connect func(network *model.Network) error

	// ConnectToNetwork adds and connects a brand new network (connect.ts's
	// with-args branch, `client.connectToNetwork(...)`). nil reports "not
	// available yet".
	ConnectToNetwork func(host, port string, tls bool) error

	// RemoveNetwork mirrors quit.ts's `this.networks = _.without(this.networks,
	// network)` + `ClientCertificate.remove(network.uuid)` tail end: drops the
	// network from the owning client and cleans up its client certificate.
	// nil until Client ownership lands - see the package doc comment.
	RemoveNetwork func(network *model.Network)

	// RemoveMentions mirrors Client.part's `client.mentions = client.
	// mentions.filter((msg) => !(msg.chanId === chan.id))` line, called by
	// part.go's partChannel. nil until Client ownership lands.
	RemoveMentions func(chanID int)

	// IsOpen mirrors chan.pushMessage's own `_.find(client.attachedClients,
	// {openChannel: chanId})` lookup (Client.IsChannelOpen) - see
	// internal/irchandlers.Deps's identical field. nil is treated as
	// "never open".
	IsOpen func(chanID int) bool

	// LoadHistory mirrors msg.ts's `newChan.loadMessages(this, network)`
	// call right after opening a new query window - seeds it from
	// message-storage history the same way internal/session.
	// loadChannelMessages does at startup/network-add, via the seam
	// pattern this Deps struct uses throughout (this package can't import
	// internal/session, which owns the real implementation, without an
	// import cycle - session already imports this package to wire up
	// Dispatch). nil skips the load, leaving the channel empty exactly
	// like before this seam existed.
	LoadHistory func(network *model.Network, chn *model.Chan)
}

Deps carries every dependency a command handler needs, gathered in one place so Dispatch's signature doesn't grow a parameter per command file - the same shape internal/irchandlers.Deps uses for the same reason.

type EncryptionStore

type EncryptionStore interface {
	Generate(networkID, target string) (string, error)
	Set(networkID, target, encodedKey string) error
	SetPassphrase(networkID, target, passphrase string) (string, error)
	Remove(networkID, target string) (bool, error)
	List(networkID string) []string
	Encrypt(networkID, kind, target, plaintext string) (string, error)
	Enabled(networkID, target string) bool
}

EncryptionStore is the account-scoped key and wire-format seam. The session package owns persistence and master-key protection; commands only need target-scoped operations and message encryption.

type InputOpts

type InputOpts struct {
	ReplyTo string

	// ShowEncryptionKey delivers a generated key directly to the requesting
	// WebSocket. It must not be rendered as a chat message because chat
	// history is persisted.
	ShowEncryptionKey func(target, key string)
}

InputOpts mirrors PluginInputOpts: the one extra per-call option Client. input threads through (the client-supplied message id a "say" is a reply to, used by msg.go to tag the outgoing PRIVMSG with a +reply tag).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL