lowbot

package module
v1.14.4 Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2025 License: MIT Imports: 34 Imported by: 3

README

lowbot

🤖 LowBot is a Go project on GitHub that simplifies bot creation by automatically generating bot journeys from a YAML script. It offers extensive customization options, making it highly versatile and adaptable.

🤔 Why LowBot?

  • 📚 Simple and Self-Explanatory Scripting:

    LowBot follows a straightforward and intuitive YAML script format, making it easy to understand and modify for creating bot journeys.

  • 🧩 Extensibility for Custom Business Rules:

    LowBot offers extensive flexibility, allowing users to define their own custom actions and implement unique business rules tailored to their specific requirements.

  • 📡 Expandable Bot Channels:

    With LowBot, users can seamlessly integrate and implement their own channels, empowering them to connect the bot with various platforms and communication channels of their choice.

📦 Install

go get github.com/chrissgon/lowbot

🚀 Quick Start

Start a bot with Telegram and local persist

import "github.com/chrissgon/lowbot"

flow, _ := lowbot.NewFlow("your_flow.yaml")
channel, _ := lowbot.NewTelegram("your_telegram_token")
persist, _ := lowbot.NewLocalPersist()

lowbot.StartBot(flow, channel, persist)

Start a bot with Discord and custom actions

import "github.com/chrissgon/lowbot"

myActions := lowbot.ActionsMap{
    "Custom": func(flow *lowbot.Flow, channel lowbot.IChannel) (bool, error) {
        // your rules
        wait := true
        return wait, nil
    },
}

lowbot.SetCustomActions(myActions)

flow, _ := lowbot.NewFlow("your_flow.yaml")
channel, _ := lowbot.NewDiscord()
persist, _ := lowbot.NewLocalPersist()

lowbot.StartBot(flow, channel, persist)

You can create your Channel or Persist by implementing their interfaces

import "github.com/chrissgon/lowbot"

func MyNewChannel () lowbot.IChannel {
  // implements Channel
}

func MyNewPersist () lowbot.Persist {
  // implements Persist
}

flow, _ := lowbot.NewFlow("your_flow.yaml")

// And you can pass in the StartBot
lowbot.StartBot(flow, MyNewChannel(), MyNewPersist())

📚 Documentation

Read all documentation in docs folder.

🌎 Global Variables

When you run this project, you can adjusts the following environment variables.

Debug: Show debug => default: true

EnableLocalPersist: Enable local persist => default: true

🔒 Environment Variables

When you run this project, you need set the following environment variables.

  • When you to use Telegram:
    TELEGRAM_TOKEN: Telegram bot token.
  • When you to use Discord:
    DISCORD_TOKEN: Discord bot token.

📝 Anotations

Run all the tests with coverage.

go test ./... -coverprofile=coverage.out && ./coverage-ignore.sh && go tool cover -html=coverage.out

💪🏻 Contribution

This project is open source and welcomes community contributions. Feel free to fork, implement improvements, and submit a pull request. Every contribution is valued and appreciated!

We hope that lowbot proves useful to you and enhances your manga reading experience. Feel free to explore the source code, provide feedback, and report any issues you encounter.

❤️ Authors

Documentation

Index

Constants

View Source
const (
	CHANNEL_TELEGRAM_NAME        = "telegram"
	CHANNEL_WHATSAPP_TWILIO_NAME = "whatsapp-twilio"
	CHANNEL_WHATSAPP_DEVICE_NAME = "whatsapp-meow"
	CHANNEL_DISCORD_NAME         = "discord"
	FLOW_INIT_STEP_NAME          = "init"
	FLOW_END_STEP_NAME           = "end"
	FLOW_DEFAULT_STEP_NAME       = "default"
	FLOW_ERROR_STEP_NAME         = "error"
)

Variables

View Source
var (
	DEBUG                           = false
	ERR_CHANNEL_RUNNING             = fmt.Errorf("channel is running")
	ERR_CHANNEL_NOT_RUNNING         = fmt.Errorf("channel is not running")
	ERR_UNKNOWN_TELEGRAM_TOKEN      = fmt.Errorf("unknown telegram token")
	ERR_UNKNOWN_DISCORD_TOKEN       = fmt.Errorf("unknown discord token")
	ERR_UNKNOWN_CHATGPT_TOKEN       = fmt.Errorf("unknown chatgpt token")
	ERR_CONNECT_CHATGPT             = fmt.Errorf("connect to chatgpt failed")
	ERR_UNDEFINED_CHATGPT_ASSISTANT = fmt.Errorf("undefined chatgpt assistant")
	ERR_UNKNOWN_ACTION              = fmt.Errorf("unknown action")
	ERR_UNKNOWN_ROOM                = fmt.Errorf("unknown room")
	ERR_NIL_FLOW                    = fmt.Errorf("nil flow")
	ERR_NIL_STEP                    = fmt.Errorf("nil step")
	ERR_NIL_CHANNEL                 = fmt.Errorf("nil channel")
	ERR_UNKNOWN_DEFAULT_STEP        = fmt.Errorf("unknown step: default")
	ERR_UNKNOWN_INIT_STEP           = fmt.Errorf("unknown step: init")
	ERR_UNKNOWN_NEXT_STEP           = fmt.Errorf("unknown next step")
	ERR_INVALID_STEP                = fmt.Errorf("step invalid")
	ERR_PATTERN_NEXT_STEP           = fmt.Errorf("step pattern invalid")
	ERR_ENDED_FLOW                  = fmt.Errorf("flow ended")
	ERR_ROOM_STOPPED_FLOW           = fmt.Errorf("flow finished by room")
	ERR_FILE_NOT_PUBLIC             = fmt.Errorf("file is not public")

	ERR_FEATURE_UNIMPLEMENTED = fmt.Errorf("feature unimplemented")
)
View Source
var (
	FILETYPE_AUDIO_EXT = []string{".mp3", ".wav", ".ogg", ".oga", ".opus", ".aac", ".m4a", ".weba", ".flac"}
	FILETYPE_IMAGE_EXT = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".tiff", ".ico", ".svg", ".apng", ".avif"}
	FILETYPE_VIDEO_EXT = []string{".mp4", ".mov", ".wmv", ".avi", ".webm", ".flv", ".mkv", ".mpeg", ".ogv"}
)
View Source
var CommonMIMETypes = map[string]string{

	".jpg":  "image/jpeg",
	".jpeg": "image/jpeg",
	".png":  "image/png",
	".gif":  "image/gif",
	".bmp":  "image/bmp",
	".webp": "image/webp",
	".tiff": "image/tiff",
	".ico":  "image/x-icon",
	".svg":  "image/svg+xml",
	".apng": "image/apng",
	".avif": "image/avif",

	".mp3":  "audio/mpeg",
	".wav":  "audio/wav",
	".ogg":  "audio/ogg",
	".oga":  "audio/ogg",
	".opus": "audio/opus",
	".aac":  "audio/aac",
	".m4a":  "audio/mp4",
	".weba": "audio/webm",
	".flac": "audio/flac",

	".mp4":  "video/mp4",
	".mov":  "video/quicktime",
	".wmv":  "video/x-ms-wmv",
	".avi":  "video/x-msvideo",
	".webm": "video/webm",
	".flv":  "video/x-flv",
	".mkv":  "video/x-matroska",
	".mpeg": "video/mpeg",
	".ogv":  "video/ogg",

	".pdf":  "application/pdf",
	".doc":  "application/msword",
	".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
	".xls":  "application/vnd.ms-excel",
	".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
	".ppt":  "application/vnd.ms-powerpoint",
	".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
	".odt":  "application/vnd.oasis.opendocument.text",

	".txt":  "text/plain",
	".csv":  "text/csv",
	".html": "text/html",
	".htm":  "text/html",
	".css":  "text/css",
	".js":   "application/javascript",
	".json": "application/json",
	".xml":  "application/xml",
	".yaml": "text/yaml",
	".yml":  "text/yaml",

	".zip": "application/zip",
	".rar": "application/vnd.rar",
	".tar": "application/x-tar",
	".gz":  "application/gzip",
	".7z":  "application/x-7z-compressed",

	".ttf":   "font/ttf",
	".otf":   "font/otf",
	".woff":  "font/woff",
	".woff2": "font/woff2",

	".apk": "application/vnd.android.package-archive",
	".exe": "application/vnd.microsoft.portable-executable",
	".swf": "application/x-shockwave-flash",
}
View Source
var FlowPersist = NewMemoryFlowPersist()
View Source
var WhatsMeowReplyDuration time.Duration = 2 * time.Second
View Source
var WhatsappTwilioDefaultPath string = "/runner/whatsapp/twilio"
View Source
var WhatsappTwilioReplyDuration time.Duration = 4 * time.Second

Functions

func FormatTestError

func FormatTestError(expect, have any) string

func GetMIMEByExtension added in v1.14.3

func GetMIMEByExtension(filename string) string

func InitWhatsappMeowChannel added in v1.13.2

func InitWhatsappMeowChannel(ctx context.Context, db *sql.DB, dialect string, log waLog.Logger) error

func InitWhatsappTwilioChannel added in v1.13.1

func InitWhatsappTwilioChannel(webhook *gin.Engine, path string)

func IsURL

func IsURL(str string) bool

func ParseTemplate

func ParseTemplate(texts []string) string

func PrintLog added in v1.13.2

func PrintLog(msg string)

func RunActionButton added in v1.10.0

func RunActionButton(flow *Flow, channel IChannel, interaction Interaction) (bool, error)

func RunActionFile added in v1.10.0

func RunActionFile(flow *Flow, channel IChannel, interaction Interaction) (bool, error)

func RunActionInput added in v1.10.0

func RunActionInput(flow *Flow, channel IChannel, interaction Interaction) (bool, error)

func RunActionText added in v1.10.0

func RunActionText(flow *Flow, channel IChannel, interaction Interaction) (bool, error)

func RunActionWebhook added in v1.14.0

func RunActionWebhook(flow *Flow, channel IChannel, interaction Interaction) (bool, error)

func RunNextAction added in v1.14.0

func RunNextAction(flow *Flow, channel IChannel, interaction Interaction) (bool, error)

func SendInteraction added in v1.10.0

func SendInteraction(channel IChannel, interaction Interaction) error

func SetCustomActions added in v1.1.0

func SetCustomActions(custom ActionsMap)

Types

type ActionFunc added in v1.11.0

type ActionFunc func(*Flow, IChannel, Interaction) (bool, error)

type ActionsMap

type ActionsMap map[string]ActionFunc

type Bot added in v1.11.0

type Bot struct {
	BotID    uuid.UUID
	Flow     *Flow
	Channels map[uuid.UUID]IChannel
	Running  bool
}

func NewBot added in v1.11.0

func NewBot(flow *Flow, channels map[uuid.UUID]IChannel) *Bot

func (*Bot) Start added in v1.11.0

func (bot *Bot) Start() error

func (*Bot) StartChannel added in v1.11.0

func (bot *Bot) StartChannel(channel IChannel) error

func (*Bot) StartListenChannel added in v1.14.0

func (bot *Bot) StartListenChannel(channel IChannel)

func (*Bot) Stop added in v1.11.0

func (bot *Bot) Stop() error

type Broadcast added in v1.10.0

type Broadcast[T any] struct {
	// contains filtered or unexported fields
}

func NewBroadcast added in v1.10.0

func NewBroadcast[T any]() *Broadcast[T]

func (*Broadcast[T]) Close added in v1.10.0

func (b *Broadcast[T]) Close() error

func (*Broadcast[T]) Listen added in v1.10.0

func (b *Broadcast[T]) Listen() chan T

func (*Broadcast[T]) Send added in v1.10.0

func (b *Broadcast[T]) Send(v T)

type Channel

type Channel struct {
	ChannelID uuid.UUID
	Name      string
	Running   bool
	Broadcast *Broadcast[Interaction]
}

type DiscordChannel added in v1.7.1

type DiscordChannel struct {
	*Channel
	// contains filtered or unexported fields
}

func (*DiscordChannel) GetChannel added in v1.7.1

func (channel *DiscordChannel) GetChannel() *Channel

func (*DiscordChannel) RespondInteraction added in v1.7.1

func (channel *DiscordChannel) RespondInteraction(in *discordgo.Interaction)

func (*DiscordChannel) SendAudio added in v1.7.1

func (channel *DiscordChannel) SendAudio(interaction Interaction) error

func (*DiscordChannel) SendButton added in v1.7.1

func (channel *DiscordChannel) SendButton(interaction Interaction) error

func (*DiscordChannel) SendDocument added in v1.7.1

func (channel *DiscordChannel) SendDocument(interaction Interaction) error

func (*DiscordChannel) SendFile added in v1.7.1

func (channel *DiscordChannel) SendFile(sessionID string, interaction Interaction) error

func (*DiscordChannel) SendImage added in v1.7.1

func (channel *DiscordChannel) SendImage(interaction Interaction) error

func (*DiscordChannel) SendText added in v1.7.1

func (channel *DiscordChannel) SendText(interaction Interaction) error

func (*DiscordChannel) SendVideo added in v1.7.1

func (channel *DiscordChannel) SendVideo(interaction Interaction) error

func (*DiscordChannel) Start added in v1.11.0

func (channel *DiscordChannel) Start() error

func (*DiscordChannel) Stop added in v1.11.0

func (channel *DiscordChannel) Stop() error

type File added in v1.7.1

type File struct {
	FileID    uuid.UUID
	FileType  FileType
	Name      string
	Bytes     []byte
	Path      string
	URL       string
	Extension string
	Mime      string
	Err       error
}

func (*File) GetFile added in v1.7.1

func (file *File) GetFile() *File

func (*File) IsAudio added in v1.7.1

func (file *File) IsAudio() bool

func (*File) IsDocument added in v1.7.1

func (file *File) IsDocument() bool

func (*File) IsImage added in v1.7.1

func (file *File) IsImage() bool

func (*File) IsVideo added in v1.7.1

func (file *File) IsVideo() bool

func (*File) Read added in v1.7.1

func (file *File) Read() error

func (*File) SetFilePath added in v1.7.1

func (file *File) SetFilePath(path string)

func (*File) SetFileType added in v1.7.1

func (file *File) SetFileType()

type FileType added in v1.7.1

type FileType string
const (
	FILETYPE_AUDIO    FileType = "FILETYPE_AUDIO"
	FILETYPE_DOCUMENT FileType = "FILETYPE_DOCUMENT"
	FILETYPE_IMAGE    FileType = "FILETYPE_IMAGE"
	FILETYPE_VIDEO    FileType = "FILETYPE_VIDEO"
)

type Flow

type Flow struct {
	FlowID          uuid.UUID
	Name            string `yaml:"name" json:"name"`
	Replier         string `yaml:"replier" json:"replier"`
	Description     string `yaml:"description" json:"description"`
	Steps           Steps  `yaml:"steps" json:"steps"`
	CurrentStep     Step
	CurrentStepName string

	Waiting bool
}

func NewFlow

func NewFlow(path string) (*Flow, error)

func NewFlowByJSON added in v1.10.0

func NewFlowByJSON(strJSON string) (*Flow, error)

func NewFlowByJSONFile added in v1.10.0

func NewFlowByJSONFile(path string) (*Flow, error)

func (*Flow) Continue added in v1.14.0

func (flow *Flow) Continue(interaction Interaction) error

func (*Flow) Ended added in v1.10.0

func (flow *Flow) Ended() bool

func (*Flow) Next

func (flow *Flow) Next(interaction Interaction) error

func (*Flow) NextError added in v1.14.0

func (flow *Flow) NextError()

func (*Flow) NoHasNext added in v1.4.0

func (flow *Flow) NoHasNext() bool

func (*Flow) Start

func (flow *Flow) Start() error

func (*Flow) Wait added in v1.14.0

func (flow *Flow) Wait(interaction Interaction) error

type IChannel added in v1.7.1

type IChannel interface {
	GetChannel() *Channel
	Stop() error
	Start() error
	SendAudio(Interaction) error
	SendButton(Interaction) error
	SendDocument(Interaction) error
	SendImage(Interaction) error
	SendText(Interaction) error
	SendVideo(Interaction) error
}

func NewDiscordChannel added in v1.7.1

func NewDiscordChannel(token string) (IChannel, error)

func NewTelegramChannel added in v1.7.1

func NewTelegramChannel(token string) (IChannel, error)

func NewWhatsappMeowChannel added in v1.13.2

func NewWhatsappMeowChannel(ctx context.Context, JID types.JID, qrcodeChan chan WhatsappMeowUpdate) (IChannel, error)

func NewWhatsappTwilioChannel added in v1.11.0

func NewWhatsappTwilioChannel(number, token, SID string) (IChannel, error)

type IFile added in v1.7.1

type IFile interface {
	GetFile() *File
	Read() error
	IsAudio() bool
	IsDocument() bool
	IsImage() bool
	IsVideo() bool
}

func NewFile added in v1.7.1

func NewFile(path, url string) IFile

type IFlowPersist added in v1.14.0

type IFlowPersist interface {
	Set(any, *Flow) error
	Get(any) (*Flow, error)
}

func NewMemoryFlowPersist added in v1.7.1

func NewMemoryFlowPersist() IFlowPersist

type Interaction

type Interaction struct {
	// FlowID     uuid.UUID
	To      *Who
	From    *Who
	Replier *Who

	Type       InteractionType
	Parameters InteractionParameters
	Timestamp  int64
	Custom     map[string]any
}

func NewInteractionMessageButton

func NewInteractionMessageButton(buttons []string, text string) Interaction

func NewInteractionMessageFile added in v1.7.1

func NewInteractionMessageFile(text, path, url string) Interaction

func NewInteractionMessageText

func NewInteractionMessageText(text string) Interaction

func (*Interaction) IsEmptyText added in v1.13.7

func (interaction *Interaction) IsEmptyText() bool

func (*Interaction) SetFrom added in v1.11.0

func (interaction *Interaction) SetFrom(from *Who) *Interaction

func (*Interaction) SetReplier added in v1.7.1

func (interaction *Interaction) SetReplier(replier *Who) *Interaction

func (*Interaction) SetTo added in v1.11.0

func (interaction *Interaction) SetTo(to *Who) *Interaction

type InteractionParameters

type InteractionParameters struct {
	Buttons []string
	File    IFile
	Text    string

	Custom map[string]any
}

type InteractionType

type InteractionType string
const (
	MESSAGE_BUTTON InteractionType = "MESSAGE_BUTTON"
	MESSAGE_FILE   InteractionType = "MESSAGE_FILE"
	MESSAGE_TEXT   InteractionType = "MESSAGE_TEXT"
	EVENT_TYPING   InteractionType = "EVENT_TYPING"
)

type MemoryFlowPersist added in v1.7.1

type MemoryFlowPersist struct {
	Sessions map[any]*Flow
}

func (*MemoryFlowPersist) Get added in v1.7.1

func (memory *MemoryFlowPersist) Get(ID any) (*Flow, error)

func (*MemoryFlowPersist) Set added in v1.7.1

func (memory *MemoryFlowPersist) Set(ID any, flow *Flow) error

type Step

type Step struct {
	Action     string            `yaml:"action" json:"action"`
	Next       map[string]string `yaml:"next" json:"next"`
	Parameters StepParameters    `yaml:"parameters" json:"parameters"`
}

func GetCurrentStep added in v1.14.0

func GetCurrentStep(sessionID any) (Step, error)

type StepParameters

type StepParameters struct {
	Buttons []string          `yaml:"buttons" json:"buttons"`
	Path    string            `yaml:"path" json:"path"`
	URL     string            `yaml:"url" json:"url"`
	Text    string            `yaml:"text" json:"text"`
	Texts   []string          `yaml:"texts" json:"texts"`
	Headers map[string]string `yaml:"headers" json:"headers"`
	Timeout int               `yaml:"timeout" json:"timeout"`
	Custom  map[string]any    `yaml:"custom" json:"custom"`
}

type Steps

type Steps map[string]Step

type TelegramChannel added in v1.7.1

type TelegramChannel struct {
	*Channel
	// contains filtered or unexported fields
}

func (*TelegramChannel) GetChannel added in v1.7.1

func (channel *TelegramChannel) GetChannel() *Channel

func (*TelegramChannel) SendAudio added in v1.7.1

func (channel *TelegramChannel) SendAudio(interaction Interaction) error

func (*TelegramChannel) SendButton added in v1.7.1

func (channel *TelegramChannel) SendButton(interaction Interaction) error

func (*TelegramChannel) SendDocument added in v1.7.1

func (channel *TelegramChannel) SendDocument(interaction Interaction) error

func (*TelegramChannel) SendImage added in v1.7.1

func (channel *TelegramChannel) SendImage(interaction Interaction) error

func (*TelegramChannel) SendText added in v1.7.1

func (channel *TelegramChannel) SendText(interaction Interaction) error

func (*TelegramChannel) SendVideo added in v1.7.1

func (channel *TelegramChannel) SendVideo(interaction Interaction) error

func (*TelegramChannel) Start added in v1.11.0

func (channel *TelegramChannel) Start() error

func (*TelegramChannel) Stop added in v1.11.0

func (channel *TelegramChannel) Stop() error

type WebhookChannel added in v1.13.2

type WebhookChannel struct {
	*Channel
}

type WhatsappMeowChannel added in v1.13.2

type WhatsappMeowChannel struct {
	*Channel

	JID *types.JID
	// contains filtered or unexported fields
}

func (*WhatsappMeowChannel) GetChannel added in v1.13.2

func (channel *WhatsappMeowChannel) GetChannel() *Channel

func (*WhatsappMeowChannel) SendAudio added in v1.13.2

func (channel *WhatsappMeowChannel) SendAudio(interaction Interaction) error

func (*WhatsappMeowChannel) SendButton added in v1.13.2

func (channel *WhatsappMeowChannel) SendButton(interaction Interaction) error

func (*WhatsappMeowChannel) SendDocument added in v1.13.2

func (channel *WhatsappMeowChannel) SendDocument(interaction Interaction) error

func (*WhatsappMeowChannel) SendImage added in v1.13.2

func (channel *WhatsappMeowChannel) SendImage(interaction Interaction) error

func (*WhatsappMeowChannel) SendText added in v1.13.2

func (channel *WhatsappMeowChannel) SendText(interaction Interaction) error

func (*WhatsappMeowChannel) SendVideo added in v1.13.2

func (channel *WhatsappMeowChannel) SendVideo(interaction Interaction) error

func (*WhatsappMeowChannel) Start added in v1.13.2

func (channel *WhatsappMeowChannel) Start() error

func (*WhatsappMeowChannel) Stop added in v1.13.2

func (channel *WhatsappMeowChannel) Stop() error

type WhatsappMeowUpdate added in v1.13.4

type WhatsappMeowUpdate struct {
	QR  whatsmeow.QRChannelItem
	JID *types.JID
}

type WhatsappTwilioChannel added in v1.11.0

type WhatsappTwilioChannel struct {
	*Channel
	// contains filtered or unexported fields
}

func (*WhatsappTwilioChannel) GetChannel added in v1.11.0

func (channel *WhatsappTwilioChannel) GetChannel() *Channel

func (*WhatsappTwilioChannel) SendAudio added in v1.11.0

func (channel *WhatsappTwilioChannel) SendAudio(interaction Interaction) error

func (*WhatsappTwilioChannel) SendButton added in v1.11.0

func (channel *WhatsappTwilioChannel) SendButton(interaction Interaction) error

func (*WhatsappTwilioChannel) SendDocument added in v1.11.0

func (channel *WhatsappTwilioChannel) SendDocument(interaction Interaction) error

func (*WhatsappTwilioChannel) SendImage added in v1.11.0

func (channel *WhatsappTwilioChannel) SendImage(interaction Interaction) error

func (*WhatsappTwilioChannel) SendText added in v1.11.0

func (channel *WhatsappTwilioChannel) SendText(interaction Interaction) error

func (*WhatsappTwilioChannel) SendVideo added in v1.11.0

func (channel *WhatsappTwilioChannel) SendVideo(interaction Interaction) error

func (*WhatsappTwilioChannel) Start added in v1.11.0

func (channel *WhatsappTwilioChannel) Start() error

func (*WhatsappTwilioChannel) Stop added in v1.11.0

func (channel *WhatsappTwilioChannel) Stop() error

type WhatsappTwilioNumbersManager added in v1.14.2

type WhatsappTwilioNumbersManager struct {
	// contains filtered or unexported fields
}

type Who added in v1.7.1

type Who struct {
	WhoID  string
	Name   string
	Custom map[string]any
}

func NewWho added in v1.7.1

func NewWho(whoID string, name string) *Who

Directories

Path Synopsis
docs
examples/hello command
examples/types command

Jump to

Keyboard shortcuts

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