plugin

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package plugin provides the public SDK for PaperValet plugins. External plugins build against this package. Internal packages should use the type aliases in internal/interfaces.

Index

Constants

This section is empty.

Variables

View Source
var ErrBotNotReady = errors.New("bot not ready")

ErrBotNotReady is returned by Bot calls before the bot has logged in.

View Source
var ErrNoMessage = &CommandError{Code: "NO_MESSAGE", Message: "no message in context"}

Functions

func Bold added in v0.1.1

func Bold(s string) string

Bold renders plain text s in bold.

func ChannelChatID added in v0.1.2

func ChannelChatID(channelID int64) int64

ChannelChatID converts a raw channel id to the -100… chat id form.

func ChatIDOf added in v0.1.2

func ChatIDOf(p tg.PeerClass) int64

ChatIDOf converts a peer to the chat id form used by MessageEvent.ChatID: users positive, basic groups -id, channels -100… .

func ChatIDOfInput added in v0.1.2

func ChatIDOfInput(p tg.InputPeerClass) int64

ChatIDOfInput converts an input peer to the MessageEvent.ChatID form.

func Code added in v0.1.1

func Code(v any) string

Code renders v as inline code. Backticks inside are kept verbatim.

func DeleteMessages added in v0.1.2

func DeleteMessages(ctx context.Context, api *tg.Client, peer tg.InputPeerClass, ids ...int) error

DeleteMessages removes messages in peer, using channels.deleteMessages in channels and supergroups (messages.deleteMessages silently does nothing there).

func Escape added in v0.1.1

func Escape(s string) string

Escape makes s render literally inside Markdown text.

func GetMessages added in v0.1.2

func GetMessages(ctx context.Context, api *tg.Client, peer tg.InputPeerClass, ids ...int) ([]*tg.Message, []tg.UserClass, []tg.ChatClass, error)

GetMessages fetches messages by id from peer. Channels and supergroups use channels.getMessages. Missing ids are absent; service messages are skipped. Users and chats returned by Telegram are included so callers can read names and access hashes.

func Invalid added in v0.1.1

func Invalid(zh, en string) error

Invalid builds a bilingual validation error for Setting.Validate.

func Italic added in v0.1.1

func Italic(s string) string

Italic renders plain text s in italic.

func Link(text, url string) string

Link renders plain text as a link to url.

func Mention added in v0.1.1

func Mention(text string, id int64) string

Mention links text to a user by id.

func ParseMarkdown added in v0.1.1

func ParseMarkdown(text string, resolve func(int64) (tg.InputUserClass, error)) (string, []tg.MessageEntityClass)

ParseMarkdown converts Markdown into plain text plus entities. Mentions of users that cannot be resolved degrade to plain text; any other failure falls back to the raw text.

func Pre added in v0.1.1

func Pre(s string) string

Pre renders s as a code block on its own lines.

func SenderID added in v0.1.2

func SenderID(msg *tg.Message) int64

SenderID returns the user id that sent msg, or 0 (channel posts, anonymous admins).

Types

type Bot added in v0.1.1

type Bot interface {
	// Ready reports whether the bot is logged in.
	Ready() bool
	// Username is the bot's @username without @, "" before login.
	Username() string
	// SetPage adds or replaces the plugin's page; nil removes it. Pages
	// and settings are removed automatically when the plugin unloads.
	SetPage(p *Page) error
	// Notify sends a view to the owner's bot chat.
	Notify(ctx context.Context, v *View) (int, error)
	// Send posts a view to a chat the bot can write to.
	Send(ctx context.Context, chatID int64, v *View) (int, error)
	// Edit replaces a message the bot sent.
	Edit(ctx context.Context, chatID int64, msgID int, v *View) error
}

Bot is the companion bot as seen by one plugin (Host().Bot(name)). It only talks to the owner, renders settings panels and plugin pages, and posts messages on the plugin's behalf. Buttons with Data on anything the plugin sends are routed back to its Page.

type BotContext added in v0.1.1

type BotContext struct {
	Ctx  context.Context
	Lang string
	Data string
	// Input is the text the owner typed after Ask.
	Input string
	// contains filtered or unexported fields
}

BotContext is passed to Page.Handle.

func (*BotContext) Alert added in v0.1.1

func (c *BotContext) Alert(text string)

Alert shows a notice the owner must dismiss.

func (*BotContext) Ask added in v0.1.1

func (c *BotContext) Ask(key string)

Ask waits for the owner to type text. The next message comes back to Handle with Data set to key and Input set to the text. The returned View should say what to type.

func (*BotContext) Asked added in v0.1.1

func (c *BotContext) Asked() string

Asked returns the key passed to Ask; read by the host after Handle.

func (*BotContext) Context added in v0.1.1

func (c *BotContext) Context() context.Context

Context returns the request context.

func (*BotContext) Notice added in v0.1.1

func (c *BotContext) Notice() (string, bool)

Notice returns the toast text and whether it is an alert; read by the host after Handle.

func (*BotContext) Tlocal added in v0.1.1

func (c *BotContext) Tlocal(zh, en string) string

Tlocal picks between a Chinese and an English string.

func (*BotContext) Toast added in v0.1.1

func (c *BotContext) Toast(text string)

Toast shows a short notice on the pressed button.

type Button added in v0.1.1

type Button struct {
	Text string
	// Data comes back to Page.Handle as BotContext.Data, at most 32 bytes.
	Data string
	URL  string
	// Style colors the button on clients that support it; others show
	// it plain.
	Style ButtonStyle
}

Button is one inline button. Set Data for a callback or URL for a link.

func Btn added in v0.1.1

func Btn(text, data string) Button

Btn builds a callback button.

func LinkBtn added in v0.1.1

func LinkBtn(text, url string) Button

LinkBtn builds a URL button.

func Row added in v0.1.1

func Row(b ...Button) []Button

Row groups buttons into one keyboard row.

func (Button) Danger added in v0.1.1

func (b Button) Danger() Button

Danger returns the button colored for a destructive action.

func (Button) Primary added in v0.1.1

func (b Button) Primary() Button

Primary returns the button colored for the main action.

func (Button) Success added in v0.1.1

func (b Button) Success() Button

Success returns the button colored for a positive action.

type ButtonStyle added in v0.1.1

type ButtonStyle uint8

ButtonStyle is a button background color.

const (
	StylePlain ButtonStyle = iota
	StylePrimary
	StyleSuccess
	StyleDanger
)

Button colors: Primary for the main action, Success for adding or confirming, Danger for removing.

type Choice added in v0.1.1

type Choice struct {
	Value   string
	Label   string
	LabelEN string
}

Choice is one option of a SettingChoice.

type Command

type Command struct {
	Name        string
	Aliases     []string
	Description string
	// DescEN is the English description; when set it wins over Description
	// for English users (Description stays the Chinese/primary text).
	DescEN string
	Usage  string
	// UsageEN is the English usage line; when empty Usage is reused.
	UsageEN   string
	Plugin    string
	Category  string
	OwnerOnly bool
	Hidden    bool
	RateLimit int
	Handler   Handler
}

Command represents a registered command.

type CommandContext

type CommandContext struct {
	Command      string
	Args         []string
	RawArgs      string
	Message      *MessageEvent
	Session      *SessionContext
	API          *tg.Client
	PeerResolver PeerResolver
	Emitter      Emitter
	Media        MediaSender
	Downloader   MediaDownloader
	PluginName   string
	// SelfID is the account user id; Message.UserID == SelfID means the
	// owner sent the command, otherwise it is delegated sudo traffic.
	SelfID int64
	// Lang is the active language ("zh-CN"/"en-US") for this command,
	// resolved from the i18n manager. Lets plugins localize static help
	// text without registering a full catalog.
	Lang      string
	StartTime time.Time
	Metadata  map[string]any
	Ctx       context.Context
	Logger    Logger
	I18n      func(key string, args ...any) string
}

CommandContext is passed to every command handler.

func (*CommandContext) ArgCount

func (c *CommandContext) ArgCount() int

func (*CommandContext) Context

func (c *CommandContext) Context() context.Context

func (*CommandContext) Delete

func (c *CommandContext) Delete() error

Delete removes the command message.

func (*CommandContext) DeleteMessages added in v0.1.1

func (c *CommandContext) DeleteMessages(ids ...int) error

DeleteMessages removes messages in the current chat. Channels and supergroups need channels.deleteMessages; messages.deleteMessages there silently does nothing.

func (*CommandContext) Edit

func (c *CommandContext) Edit(text string) error

func (*CommandContext) GetArg

func (c *CommandContext) GetArg(index int) string

func (*CommandContext) GetArgs

func (c *CommandContext) GetArgs() string

func (*CommandContext) HasArg

func (c *CommandContext) HasArg(arg string) bool

func (*CommandContext) IsSelf added in v0.1.1

func (c *CommandContext) IsSelf() bool

IsSelf reports whether the command came from the owner's own account.

func (*CommandContext) Reply

func (c *CommandContext) Reply(text string) error

func (*CommandContext) ReplyMedia

func (c *CommandContext) ReplyMedia(path, caption string) error

ReplyMedia sends a local file (photo/document) as a reply and deletes nothing.

func (*CommandContext) ReplyMessage added in v0.1.2

func (c *CommandContext) ReplyMessage() (*tg.Message, error)

ReplyMessage fetches the message the command replies to.

func (*CommandContext) ResolvePeer

func (c *CommandContext) ResolvePeer() (tg.InputPeerClass, error)

func (*CommandContext) T

func (c *CommandContext) T(key string, args ...any) string

T translates a message key using the command's i18n resolver. Falls back to the raw key when no resolver is wired.

func (*CommandContext) Tlocal added in v0.1.1

func (c *CommandContext) Tlocal(zh, en string) string

Tlocal picks between a Chinese and an English string by active language.

func (*CommandContext) Typing

func (c *CommandContext) Typing() error

type CommandError

type CommandError struct {
	Code    string
	Message string
	Err     error
}

func (*CommandError) Error

func (e *CommandError) Error() string

func (*CommandError) Unwrap

func (e *CommandError) Unwrap() error

type DescENProvider added in v0.1.1

type DescENProvider interface {
	DescEN() string
}

DescENProvider is optionally implemented by plugins that carry an English description alongside the primary one.

type Emitter

type Emitter interface {
	Emit(ctx context.Context, eventType string, data any) error
}

Emitter is the subset of the event bus that plugins/commands need.

type Handler

type Handler func(ctx *CommandContext) error

Handler is the signature for command handlers.

type Host added in v0.1.1

type Host interface {
	API() *tg.Client
	PeerResolver() PeerResolver
	Media() MediaSender
	Downloader() MediaDownloader
	// SelfID is the logged-in account id (0 before login completes).
	SelfID() int64
	// Logger returns a logger named after the plugin.
	Logger(name string) Logger
	// DataDir returns data/<plugin>, creating it if needed.
	DataDir(plugin string) (string, error)
	// Send posts a Markdown message to chatID and returns its message id.
	// replyTo 0 sends a plain message.
	Send(ctx context.Context, chatID int64, text string, replyTo int) (int, error)
	// Lang is the active language for userID: zh-CN or en-US.
	Lang(userID int64) string
	// Settings registers the plugin's settings panel (spec.Plugin must be
	// the plugin name) and returns its values. Call it from Init. Options
	// are edited in the bot panel, never through commands.
	Settings(spec *SettingsSpec) (Settings, error)
	// Bot returns the companion bot scoped to the named plugin.
	Bot(plugin string) Bot
	// Listen registers a listener for every new and edited message. The
	// returned func removes it; unloading the plugin removes it too.
	Listen(plugin string, fn MessageListener) (remove func())
	// RunCommand runs msg.Text as a command with owner rights, as if the
	// owner had sent it in msg's chat (msg.Message must be a real message,
	// e.g. one the plugin just sent). It reports whether a command matched.
	RunCommand(ctx context.Context, msg *MessageEvent) (bool, error)
	// Prefixes returns the command prefixes, main prefix first.
	Prefixes() []string
}

Host gives plugins the same services a CommandContext carries, without needing a triggering message. Safe to keep from Init; network calls work once Start runs (the client is connected by then).

type InvalidError added in v0.1.1

type InvalidError struct{ Zh, En string }

InvalidError is a validation error with text in both languages.

func (*InvalidError) Error added in v0.1.1

func (e *InvalidError) Error() string

func (*InvalidError) Text added in v0.1.1

func (e *InvalidError) Text(lang string) string

Text returns the message in lang ("zh-CN" or "en-US").

type Logger

type Logger interface {
	Debug(msg string, keysAndValues ...any)
	Info(msg string, keysAndValues ...any)
	Warn(msg string, keysAndValues ...any)
	Error(msg string, keysAndValues ...any)
	Named(name string) Logger
	With(keysAndValues ...any) Logger
}

Logger is the minimal logging interface for plugins.

type Manager

type Manager interface {
	RegisterPlugin(p Plugin) error
	RegisterCommand(cmd *Command) error
	UnregisterCommand(name string)
	UnregisterPlugin(name string)
	Commands() RegistryProvider
	GetInfo(name string) (PluginInfo, bool)
	GetAllInfo() []PluginInfo
	GetPlugin(name string) (Plugin, bool)
	Emit(ctx context.Context, eventType string, data any) error
	InitAll(ctx context.Context) error
	StartAll(ctx context.Context) error
	StopAll(ctx context.Context) error
	// Host exposes long-lived runtime services so plugins can work outside
	// command handlers (schedulers, restored jobs, event listeners).
	Host() Host
}

Manager is the plugin manager interface exposed to external plugins.

type MediaDownloader

type MediaDownloader interface {
	DownloadMedia(ctx context.Context, msg *MessageEvent) (string, error)
}

MediaDownloader downloads media from a message into a local regular file.

type MediaSender

type MediaSender interface {
	SendFile(ctx context.Context, chatID int64, path string, caption string, replyTo int) error
}

MediaSender sends local files to chats. Implemented by internal/media.Manager.

type MessageEvent

type MessageEvent struct {
	Update    tg.UpdatesClass
	Message   *tg.Message
	Text      string
	UserID    int64
	ChatID    int64
	IsOut     bool
	IsReply   bool
	ReplyToID int
	Entities  []tg.MessageEntityClass
	Media     tg.MessageMediaClass
	Date      int
	PeerID    tg.PeerClass
	Raw       any
}

MessageEvent represents a processed message event.

func EventFromMessage added in v0.1.2

func EventFromMessage(msg *tg.Message) *MessageEvent

EventFromMessage builds a MessageEvent for msg (for RunCommand or Downloader.DownloadMedia on a fetched message).

type MessageListener added in v0.1.2

type MessageListener func(ctx context.Context, msg *MessageEvent, edited bool)

MessageListener sees every new message, incoming and outgoing, just before command parsing (command messages included; check Host.Prefixes to skip them). Edited is true for edits. Listeners run one after another on the update path, so anything slow belongs in a goroutine. Register with Host.Listen.

type Middleware

type Middleware func(next Handler) Handler

Middleware wraps a handler.

type Page added in v0.1.1

type Page struct {
	Title   string
	TitleEN string
	// Command optionally opens the page with /<Command> in the bot chat.
	Command string
	// Handle renders the page. Data is "" when the page opens, otherwise
	// the Data of the pressed button (or the Ask key with Input set).
	// The bot appends a back button; do not add one.
	Handle func(ctx *BotContext) (*View, error)
}

Page is a custom screen a plugin adds to the bot menu, for things that are more than a setting: lists with per-item buttons, dashboards, wizards. Buttons on messages the plugin posts with Notify, Send or Edit also come back to Handle.

type PeerResolver

type PeerResolver interface {
	ResolveFromChatID(ctx context.Context, chatID int64) (tg.InputPeerClass, error)
	ResolveUserInChannel(ctx context.Context, channelPeer tg.InputChannelClass, userID int64) (tg.InputPeerClass, error)
	ResolveUserFromMessage(ctx context.Context, peer tg.InputPeerClass, msgID int, userID int64) (tg.InputPeerClass, error)
	ResolveUsername(ctx context.Context, username string) (tg.InputPeerClass, error)
}

PeerResolver resolves chat/user IDs to InputPeer.

type Plugin

type Plugin interface {
	Name() string
	Description() string
	Init(ctx context.Context, mgr Manager) error
	Start(ctx context.Context) error
	Stop(ctx context.Context) error
}

Plugin is the interface all plugins must implement.

type PluginInfo

type PluginInfo struct {
	Name        string
	Description string
	DescEN      string
	Status      PluginStatus
}

PluginInfo holds plugin metadata.

type PluginMetadata

type PluginMetadata struct {
	Name        string
	Description string
	DescEN      string
	Version     string
	Author      string
	MinVersion  string
}

PluginMetadata holds plugin metadata for external .so plugins. External plugins export this as `var Metadata *PluginMetadata`.

type PluginStatus

type PluginStatus int

PluginStatus represents plugin lifecycle state.

const (
	StatusInactive PluginStatus = iota
	StatusActive
	StatusError
)

func (PluginStatus) String

func (s PluginStatus) String() string

type RegistryProvider

type RegistryProvider interface {
	Get(name string) (*Command, bool)
	GetAll() map[string]*Command
	GetByPlugin(plugin string) map[string]*Command
	GetPrefix() string
	GetPrefixes() []string
	// SetPrefixes replaces all command prefixes (main prefix first).
	SetPrefixes(prefixes []string)
	// AddUserAlias registers a runtime alias expanded once during parsing.
	// The value is a full command line, e.g. "exec ./deploy.sh".
	AddUserAlias(name, cmd string)
	RemoveUserAlias(name string)
	UserAliases() map[string]string
}

RegistryProvider provides command registry access.

type Session

type Session struct {
	UserID    int64
	ChatID    int64
	State     string
	Data      map[string]any
	Timestamp int64
}

Session holds per-(user,chat) conversation state.

type SessionContext

type SessionContext struct {
	Session *Session
	Context context.Context
	Data    map[string]any
}

SessionContext wraps a Session with a request context.

func NewSessionContext

func NewSessionContext(s *Session, ctx context.Context) *SessionContext

NewSessionContext creates a new SessionContext.

func (*SessionContext) Ctx

func (s *SessionContext) Ctx() context.Context

func (*SessionContext) Delete

func (s *SessionContext) Delete(key string)

func (*SessionContext) Get

func (s *SessionContext) Get(key string) (any, bool)

func (*SessionContext) Set

func (s *SessionContext) Set(key string, value any)

type Setting added in v0.1.1

type Setting struct {
	// Key is the stable id, lowercase letters, digits and _ only.
	Key     string
	Label   string
	LabelEN string
	// Hint is one line shown on the edit screen.
	Hint   string
	HintEN string
	Kind   SettingKind
	// Default must match Kind: bool, string or int.
	Default any
	Choices []Choice
	// Min and Max bound a SettingNumber; both 0 means unbounded.
	Min, Max int
	// Validate checks typed text (SettingText) and may normalize it.
	// Return Invalid(zh, en) so the owner reads the reason in their language.
	Validate func(string) (string, error)
	// Secret masks the value in the panel.
	Secret bool
}

Setting declares one option.

type SettingKind added in v0.1.1

type SettingKind int

SettingKind selects how the bot panel edits a setting.

const (
	// SettingToggle is a bool flipped with one tap.
	SettingToggle SettingKind = iota
	// SettingChoice is a string picked from Choices.
	SettingChoice
	// SettingText is a string the owner types.
	SettingText
	// SettingNumber is an int the owner types, kept within Min..Max.
	SettingNumber
)

type Settings added in v0.1.1

type Settings interface {
	Bool(key string) bool
	String(key string) string
	Int(key string) int
	// Set stores a value; its type must match the setting's Kind.
	Set(key string, value any) error
}

Settings reads and writes a plugin's options, persisted in data/<plugin>/settings.json. Reads of unknown keys return zero values.

type SettingsSpec added in v0.1.1

type SettingsSpec struct {
	Plugin   string
	Title    string // defaults to the plugin name
	TitleEN  string
	Settings []Setting
	// OnChange runs after a value changes, from the panel or from Set.
	OnChange func(key string)
}

SettingsSpec is a plugin's settings panel.

type View added in v0.1.1

type View struct {
	Text    string
	Buttons [][]Button
}

View is one bot screen: Markdown text plus button rows.

Jump to

Keyboard shortcuts

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