Documentation
¶
Index ¶
- Constants
- Variables
- func AudioFFmpegBufferPipe(sp AudioSpeaker, inBuf io.ReadCloser, st *AudioState) error
- func AudioFFmpegCommandPipe(sp AudioSpeaker, cmd *exec.Cmd, st *AudioState) error
- func CacheScope(key string, getScope func() (int64, error)) (int64, error)
- func Clean(s string) string
- func FlagPerson(person *int64, f *Flags)
- func FlagPlace(place *int64, f *Flags)
- func Format(cmd CommandStatic, prefix string) string
- func IsValidURL(rawURL string) bool
- func OnlyOneBitSet(n int) bool
- func Split(text string, lenCnt func(string) int, lenLim int) []string
- func TypeFlag(p *CommandType, value CommandType, f *Flags)
- type AudioSpeaker
- type AudioState
- type Author
- type Command
- type CommandCategory
- type CommandRuntime
- type CommandStatic
- type CommandType
- type CommandsStatic
- type Flags
- type FrontendType
- type Frontender
- type Frontenders
- type Here
- type Message
- func (m *Message) CommandParse() (*Message, error)
- func (m *Message) CommandRun() (*Message, error)
- func (m *Message) Fields() []string
- func (m *Message) FieldsSpace() []string
- func (m *Message) Hooks()
- func (m *Message) Prefixes() ([]Prefix, bool, error)
- func (m *Message) RawArgs(n int) string
- func (m *Message) Run()
- func (m *Message) Usage() any
- func (m *Message) Write(msg any, usrErr error) (*Message, error)
- type Messenger
- type Prefix
- type SQLDB
- func (db *SQLDB) Close() error
- func (db *SQLDB) Init(schema string) error
- func (db *SQLDB) PrefixList(place int64) ([]Prefix, error)
- func (_ *SQLDB) ScopeAdd(tx *sql.Tx, frontendID string, frontend int) (int64, error)
- func (db *SQLDB) ScopeFrontend(scope int64) (int64, error)
- func (db *SQLDB) ScopeID(scope int64) (string, error)
- func (db *SQLDB) SettingPersonGet(col string, person, place int64) (any, error)
- func (db *SQLDB) SettingPersonSet(col string, person, place int64, val any) error
- func (db *SQLDB) SettingPlaceGet(col string, place int64) (any, error)
- func (db *SQLDB) SettingPlaceSet(col string, place int64, val any) error
- func (db *SQLDB) SettingsPersonGenerate(person, place int64) error
- func (db *SQLDB) SettingsPlaceGenerate(place int64) error
- type States
Constants ¶
const ( AudioPlay int = iota AudioLoop AudioPause AudioStop )
const ( CommandCategoryGames = "Games" CommandCategoryModerators = "Moderators" CommandCategoryOther = "Other" )
Variables ¶
var ( ErrMissingArgs = errors.New("not enough arguments provided") ErrSilence = errors.New("if this error is returned don't send any message") )
var ( VirtualHost string Port string TikTokSessionID string YouTubeKey string OpenAIKey string MinGodInterval time.Duration Gin = gin.Default() )
var AliasesAdd = []string{
"add",
"new",
"create",
}
var AliasesDelete = []string{
"delete",
"del",
"remove",
"rm",
}
var AliasesEdit = []string{
"edit",
"modify",
"change",
}
var AliasesList = []string{
"list",
"ls",
}
var AliasesOff = []string{
"off",
"false",
"no",
}
var AliasesOn = []string{
"on",
"true",
"yes",
}
var AliasesSearch = []string{
"search",
"find",
}
var AliasesShow = []string{
"show",
"view",
"get",
"status",
"state",
"?",
}
var Hooks = hooks{}
Hooks are a list of functions that are applied one-by-one to incoming messages. All operations are thread safe.
var Prefixes = prefixes{}
var RDB *redis.Client
Functions ¶
func AudioFFmpegBufferPipe ¶
func AudioFFmpegBufferPipe(sp AudioSpeaker, inBuf io.ReadCloser, st *AudioState) error
AudioFFmpegBufferPipe will pipe audio coming from a buffer into ffmpeg and transform into audio that the speaker can transmit.
func AudioFFmpegCommandPipe ¶
func AudioFFmpegCommandPipe(sp AudioSpeaker, cmd *exec.Cmd, st *AudioState) error
AudioFFmpegCommandPipe works exactly like FFmpegBufferPipe except it accepts a command instead of a buffer. Provided just for convenience.
func CacheScope ¶
CacheScope returns the scope by looking it up in the cache, if it doesn't exist then it fetches it from the DB using getScope and then caches it. The key should be globally unique.
func Clean ¶
Clean returns a string with every character except the ones in the a-z, A-Z and 0-9 ranges stripped from the passed string. Assumes ASCII.
func FlagPerson ¶
func Format ¶
func Format(cmd CommandStatic, prefix string) string
Format will return a string representation of the given command in a format that can be shown to a user. Generally used in help messages to point the user to a specific command in order to avoid hardcoding it. Returns the command in the following format:
<prefix><command> [sub-command...] <usage-args>
For example: !command delete <command>
func IsValidURL ¶
IsValidURL returns true if the provided string is a valid URL with an http or https scheme and a host.
func OnlyOneBitSet ¶
func Split ¶
Splits a message into submessages. Tries to not split words unless it absolutely has to in which case it splits based on grapheme clusters.
func TypeFlag ¶
func TypeFlag(p *CommandType, value CommandType, f *Flags)
Types ¶
type AudioSpeaker ¶
type AudioSpeaker interface { // Enabled returns true if the frontend supports voice chat that the bot can // connect to. Enabled() bool // The audio's expected frame rate. FrameRate() int // The audio's expected number of channels. Channels() int // Join the message author's voice channel, if they are not connected to // any then returns an error. If in a specific frontend only one voice // channel will ever exist then the user doesn't have to be connected to it // for the bot to join (for example a discord server with only one voice // channel would not apply here as other ones *could* be created at any // point). Join() error // Send audio. Must have connected to a voice channel first, otherwise // returns an error. Say(buf io.Reader, s *AudioState) error // AuthorDeafened returns true if the author that originally made the bot // join the voice channel is currently deafened. AuthorDeafened() (bool, error) // AuthorConnected returns true if the author that originally made the bot // join the voice channel is currently connected to that same voice channel. AuthorConnected() (bool, error) }
type AudioState ¶
A select with multiple ready cases chooses one pseudo-randomly. So if the goroutine is "slow" to check those channels, you might send a value on both pause and resume (assuming they are buffered) so receiving from both channels could be ready, and resume could be chosen first, and in a later iteration the pause when the goroutine should not be paused anymore.
type Author ¶
type Author interface { // ID returns the author's ID, this should be a unique, static, identifier // in that frontend. ID() string // Name returns the author's username. Name() string // DisplayName return's the author's display name. If only usernames exist // for that frontend then returns the username. DisplayName() string // Mention return's a string that mention's the author. This should ideally // ping them in some way. Mention() string // BotAdmin returns true if the author is a bot admin. Otherwise returns // false. BotAdmin() bool // Admin checks if the author is considered an admin. Should return true // only if the author has basically every permission. Admin() bool // Mod checks if the author is considered a moderator. General rule of thumb // is that if the author can ban people, then they are mods. Mod() bool // Subcriber checks if the author is considered a subscriber. General rule of // is that if they are paying money in some way, then they are subs. If no // such thing exists for the specific frontend, then always returns false. Subscriber() bool // Scope return's the author's scope. If it doesn't exist it will create it // and add it to the database. Scope() (author int64, err error) }
Author is the interface used to abstract a frontend's message author.
type Command ¶
type Command struct { CommandStatic CommandRuntime }
type CommandCategory ¶
type CommandCategory string
type CommandRuntime ¶
type CommandRuntime struct { // The "path" taken to invoke the command, i.e. which names were used. // Includes all the sub-commands e.g. ["prefix", "add"] in order to be able // to display accurate help messages. Path []string // The arguments passed, includes everything that's not part of the // command's name. Args []string // The prefix used when the command was called. Prefix string }
CommandRuntime holds a command's runtime information.
type CommandStatic ¶
type CommandStatic interface { // Type returns the command's type. Type() CommandType // Permitted will perform checks required for a command to be executed. // Returns true if the command is allowed to be executed. Usually used to // chcek a user's permissions or to restrict a command to specific // frontends. Permitted(m *Message) bool // Names return a list of all the aliases a command has. The first item in // the list is considered the main name and so should be the simplest and // most intuitive one for the average person. For example if it's a delete // subcommand the first alias should be "delete" instead of "del" or "rm". Names() []string // Description will return a short description of what the command does. Description() string // UsageArgs will return the usage arguments. Should follow this format: // - <required> // - [optional] // - (literal-string) or (many | literals) UsageArgs() string // Category returns the general category the command belongs to. Mainly // to make displaying all the commands easier and less overwhelming (as // they are split up instead of having them all in a giant list). Category() CommandCategory // Example returns a list of strings with example usages of the command. // Only the arguments passed should be included, not the prefix and the // chain of command names. Examples() []string // Parent returns a command's parent, returns nil if there is no parent. Parent() CommandStatic // Children returns the command's sub-commands, returns nil if there are no // sub-commands. Children() CommandsStatic // Init is executed during bot startup. Should be used to set things up // necessary for the command, for example DB schemas. Init() error // Run is function that is called to run the command. Run(m *Message) (resp any, usrErr error, err error) }
CommandStatic is the the interface used to implement commands.
type CommandType ¶
type CommandType int
const ( // A simplified command with might not give full control over something but // it has a very easy to use API. Normal CommandType = 1 << iota // The full command and usually consists of many subcommands which makes it // less intuitive for the average person. Advanced // Bot admin only command used to perform actions like setting an arbitrary // person's options, etc. Admin All = Normal | Advanced | Admin )
The command types.
type CommandsStatic ¶
type CommandsStatic []CommandStatic
var Commands *CommandsStatic
func (*CommandsStatic) Match ¶
func (cmds *CommandsStatic) Match(t CommandType, m *Message, args []string) (CommandStatic, int, error)
Match will return the corresponding command based on the list of arguments. The arguments don't have to match a command exactly. For example:
args = [prefix add abc]
In this case the prefix's subcommand "add" will be matched and returned. Alongside it the index of the last valid command will be returned (in this case the index of "add", which is 1).
func (CommandsStatic) Recurse ¶
func (cmds CommandsStatic) Recurse(exec func(CommandStatic))
Recurse will recursively go through all of the commands and execute the exec function on them.
func (CommandsStatic) Usage ¶
func (cmds CommandsStatic) Usage() string
Usage returns the names of the children in a format that can be used in the UsageArgs function.
func (CommandsStatic) UsageOptional ¶
func (cmds CommandsStatic) UsageOptional() string
UsageOptions returns the names of the children in a format that can be used in the UsageArgs function and indicates that the sub-commands are optional.
type FrontendType ¶
type FrontendType int
type Frontender ¶
type Frontender interface { // Type returns the frontend type ID. Type() FrontendType // Init is responsible for starting up any frontend specific services and // connecting to frontend. When it receives the stop signal then it should // disconnect from everything. Init(wgInit, wgStop *sync.WaitGroup, stop chan struct{}) // CreateMessage returns a Message object based on the given arguments. // Used to send messages that are not direct replies, e.g. reminders. CreateMessage(person, place int64, msgID string) (*Message, error) }
type Frontenders ¶
type Frontenders []Frontender
var Frontends Frontenders
func (Frontenders) CreateMessage ¶
func (fs Frontenders) CreateMessage(person, place int64, msgID string) (*Message, error)
CreateMessage returns a Message object based on the given arguments. It detects what the frontend is based on the place. Used to send messages that are not direct replies, e.g. reminders.
type Here ¶
type Here interface { // ID returns the channel's ID, this should be a unique, static, identifier // in that frontend. ID() string // Name return's the channel's name. Name() string // ScopeExact returns the here's exact scope. See the interface's doc // comment for more information on exact scopes. ScopeExact() (place int64, err error) // ScopeLogical returns the here's logical scope. See the interface's doc // comment for more information on logical scopes. ScopeLogical() (place int64, err error) }
Here is the interface used to abstract the place where message came from, e.g. channel, server, etc.
Two type's of scopes exist for places, the exact and the logical. The logical is the area where things are generally expected to work. For example: if a user adds a custom command in a discord server they would probably expect it to work in the entire server and not just in the specific channel that they added it in. If on the other hand someone adds a custom command in a discord DM, then no guild exists and thus the channel's scope would have to be used. On the other hand the exact scope is, as its name suggests, the scope of the exact place the message came from and does not account for context, so using the previous discord server example, it would be the channel's scope where the message came from instead of the server's.
type Message ¶
type Message struct { ID string Raw string Frontend Frontender Author Author Here Here Client Messenger Speaker AudioSpeaker Command *Command }
func Await ¶
Monitor incoming messages until `check` is true or until timeout. If nothing is matched then the returned object will be nil.
func (*Message) CommandParse ¶
func (*Message) CommandRun ¶
func (*Message) FieldsSpace ¶
Split text into fields that include all trailing whitespace. For example: "example of text" will be split into ["example ", "of ", "text"]
func (*Message) Prefixes ¶
Returns the logical here's prefixes and also whether or not they were taken from the database (if not then that means the default ones were used).
type Messenger ¶
type Messenger interface { Parse() (*Message, error) // Returns the ID of the passed string. The returned ID must be valid. // Generally used for verifying an ID's validity and extracting IDs from // mentions. PlaceID(s string) (id string, err error) PersonID(s, placeID string) (id string, err error) // Gets the target's scope. If it doesn't exist it will create it and add // it to the database. Person(id string) (person int64, err error) // There exist 2 types of place scopes that are used, the exact place and // the logical place. The logical is the area where things are generally // expected to work. For example: if a user adds a custom command in a // server they would probably expect it to work in the entire server and not // just in the specific channel that they added it in. If on the other hand // someone adds a custom command in a discord DM message, then no guild // exists and thus the channel's scope would have to be used. On the other // hand `PlaceExact` returns exactly the scope of the id passed and does not // account for context. PlaceExact(id string) (place int64, err error) PlaceLogical(id string) (place int64, err error) Usage(usage string) any // Send sends a message to the appropriate scope, resp could be nil // depending on the frontend. Send(msg any, usrErr error) (resp *Message, err error) // Ping works the same as Send except the user is also pinged. Ping(msg any, usrErr error) (resp *Message, err error) // Write either calls Send or Ping depending on the frontend. This is what // should be used in most cases. Write(msg any, usrErr error) (resp *Message, err error) }
The frontend abstraction layer, a frontend needs to implement this in order to be added.
type Prefix ¶
type Prefix struct { Type CommandType Prefix string }
type SQLDB ¶
var DB *SQLDB
func (*SQLDB) PrefixList ¶
Returns the list of all prefixes for a specific scope.
func (*SQLDB) ScopeFrontend ¶
Returns then given scope's frontend id
func (*SQLDB) SettingPersonGet ¶
SettingPersonGet returns the value of col in table for the specified person in the specified place.
func (*SQLDB) SettingPersonSet ¶
PlaceSettingSet sets the value of col in table for the specified person in the specified place.
func (*SQLDB) SettingPlaceGet ¶
SettingPlaceGet returns the value of col in table for the specified place.
func (*SQLDB) SettingPlaceSet ¶
SettingPlaceSet sets the value of col in table for the specified place.
func (*SQLDB) SettingsPersonGenerate ¶
SettingsPersonGenerate will check if settings for the specified person in the specified place exist, and if not will generate them.
func (*SQLDB) SettingsPlaceGenerate ¶
SettingsPlaceGenerate will check if settings for the specified place exist and if not will generate them.