l10n and cmd generator WIP
This commit is contained in:
8
bot.go
8
bot.go
@@ -192,16 +192,16 @@ func (b *Bot) AddPlugins(plugin ...Plugin) *Bot {
|
|||||||
func (b *Bot) AddMiddleware(middleware ...Middleware) *Bot {
|
func (b *Bot) AddMiddleware(middleware ...Middleware) *Bot {
|
||||||
b.middlewares = append(b.middlewares, middleware...)
|
b.middlewares = append(b.middlewares, middleware...)
|
||||||
for _, m := range middleware {
|
for _, m := range middleware {
|
||||||
b.logger.Debugln(fmt.Sprintf("middleware with name \"%s\" registered", m.Name))
|
b.logger.Debugln(fmt.Sprintf("middleware with name \"%s\" registered", m.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(b.middlewares, func(i, j int) bool {
|
sort.Slice(b.middlewares, func(i, j int) bool {
|
||||||
first := b.middlewares[i]
|
first := b.middlewares[i]
|
||||||
second := b.middlewares[j]
|
second := b.middlewares[j]
|
||||||
if first.Order == second.Order {
|
if first.order == second.order {
|
||||||
return first.Name < second.Name
|
return first.name < second.name
|
||||||
}
|
}
|
||||||
return first.Order < second.Order
|
return first.order < second.order
|
||||||
})
|
})
|
||||||
|
|
||||||
return b
|
return b
|
||||||
|
|||||||
26
cmd_generator.go
Normal file
26
cmd_generator.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package laniakea
|
||||||
|
|
||||||
|
import "git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
|
|
||||||
|
func generateBotCommand(cmd Command) tgapi.BotCommand {
|
||||||
|
return tgapi.BotCommand{
|
||||||
|
Command: cmd.command, Description: cmd.command,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateBotCommandForPlugin(pl Plugin) []tgapi.BotCommand {
|
||||||
|
cmds := make([]tgapi.BotCommand, 0)
|
||||||
|
for _, cmd := range pl.Commands {
|
||||||
|
cmds = append(cmds, generateBotCommand(cmd))
|
||||||
|
}
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) AutoGenerateCommands() error {
|
||||||
|
commands := make([]tgapi.BotCommand, 0)
|
||||||
|
for _, pl := range b.plugins {
|
||||||
|
commands = append(commands, generateBotCommandForPlugin(pl)...)
|
||||||
|
}
|
||||||
|
_, err := b.api.SetMyCommands(tgapi.SetMyCommandsP{Commands: commands})
|
||||||
|
return err
|
||||||
|
}
|
||||||
30
handler.go
30
handler.go
@@ -1,6 +1,7 @@
|
|||||||
package laniakea
|
package laniakea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -112,3 +113,32 @@ func (b *Bot) checkPrefixes(text string) (string, bool) {
|
|||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeJsonPayload(d CallbackData) (string, error) {
|
||||||
|
b, err := json.Marshal(d)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(b), nil
|
||||||
|
}
|
||||||
|
func decodeJsonPayload(s string) (CallbackData, error) {
|
||||||
|
var data CallbackData
|
||||||
|
err := json.Unmarshal([]byte(s), &data)
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
func encodeBase64Payload(d CallbackData) (string, error) {
|
||||||
|
data, err := encodeJsonPayload(d)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dst := make([]byte, base64.StdEncoding.EncodedLen(len([]byte(data))))
|
||||||
|
base64.StdEncoding.Encode(dst, []byte(data))
|
||||||
|
return string(dst), nil
|
||||||
|
}
|
||||||
|
func decodeBase64Payload(s string) (CallbackData, error) {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
return CallbackData{}, err
|
||||||
|
}
|
||||||
|
return decodeJsonPayload(string(b))
|
||||||
|
}
|
||||||
|
|||||||
152
plugins.go
152
plugins.go
@@ -1,72 +1,97 @@
|
|||||||
package laniakea
|
package laniakea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"regexp"
|
||||||
|
|
||||||
"git.nix13.pw/scuroneko/extypes"
|
"git.nix13.pw/scuroneko/extypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandExecutor func(ctx *MsgContext, dbContext *DatabaseContext)
|
type CommandExecutor func(ctx *MsgContext, dbContext *DatabaseContext)
|
||||||
|
|
||||||
type PluginBuilder struct {
|
const (
|
||||||
name string
|
CommandValueStringType CommandValueType = "string"
|
||||||
commands map[string]CommandExecutor
|
CommandValueIntType CommandValueType = "int"
|
||||||
payloads map[string]CommandExecutor
|
CommandValueBoolType CommandValueType = "bool"
|
||||||
middlewares extypes.Slice[*PluginMiddleware]
|
CommandValueAnyType CommandValueType = "any"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
CommandRegexInt = regexp.MustCompile("\\d+")
|
||||||
|
CommandRegexString = regexp.MustCompile("\\.+")
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommandValueType string
|
||||||
|
type CommandArg struct {
|
||||||
|
valueType CommandValueType
|
||||||
|
text string
|
||||||
|
regex *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommandArg(text string, valueType CommandValueType) CommandArg {
|
||||||
|
regex := CommandRegexString
|
||||||
|
switch valueType {
|
||||||
|
case CommandValueIntType:
|
||||||
|
regex = CommandRegexInt
|
||||||
|
}
|
||||||
|
return CommandArg{valueType, text, regex}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Command struct {
|
||||||
|
command string
|
||||||
|
exec CommandExecutor
|
||||||
|
args []CommandArg
|
||||||
|
middlewares []Middleware
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommand(exec CommandExecutor, command string, args ...CommandArg) *Command {
|
||||||
|
return &Command{command, exec, args, make([]Middleware, 0)}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Plugin struct {
|
type Plugin struct {
|
||||||
Name string
|
Name string
|
||||||
Commands map[string]CommandExecutor
|
Commands map[string]Command
|
||||||
Payloads map[string]CommandExecutor
|
Payloads map[string]Command
|
||||||
Middlewares extypes.Slice[*PluginMiddleware]
|
Middlewares extypes.Slice[PluginMiddleware]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlugin(name string) *PluginBuilder {
|
func NewPlugin(name string) *Plugin {
|
||||||
return &PluginBuilder{
|
return &Plugin{
|
||||||
name: name,
|
Name: name,
|
||||||
commands: make(map[string]CommandExecutor),
|
Commands: map[string]Command{},
|
||||||
payloads: make(map[string]CommandExecutor),
|
Payloads: map[string]Command{},
|
||||||
|
Middlewares: extypes.Slice[PluginMiddleware]{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PluginBuilder) Command(f CommandExecutor, cmd ...string) *PluginBuilder {
|
func (p *Plugin) AddCommand(command Command) *Plugin {
|
||||||
for _, c := range cmd {
|
p.Commands[command.command] = command
|
||||||
p.commands[c] = f
|
|
||||||
}
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PluginBuilder) Payload(f CommandExecutor, payloads ...string) *PluginBuilder {
|
func (p *Plugin) AddPayload(command Command) *Plugin {
|
||||||
for _, payload := range payloads {
|
p.Payloads[command.command] = command
|
||||||
p.payloads[payload] = f
|
|
||||||
}
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PluginBuilder) AddMiddleware(middleware *PluginMiddleware) *PluginBuilder {
|
func (p *Plugin) AddMiddleware(middleware PluginMiddleware) *Plugin {
|
||||||
p.middlewares = p.middlewares.Push(middleware)
|
p.Middlewares = p.Middlewares.Push(middleware)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PluginBuilder) Build() Plugin {
|
|
||||||
if len(p.commands) == 0 && len(p.payloads) == 0 {
|
|
||||||
log.Printf("no command or payloads for %s", p.name)
|
|
||||||
}
|
|
||||||
return Plugin{
|
|
||||||
p.name, p.commands,
|
|
||||||
p.payloads, p.middlewares,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Plugin) Execute(cmd string, ctx *MsgContext, dbContext *DatabaseContext) {
|
func (p *Plugin) Execute(cmd string, ctx *MsgContext, dbContext *DatabaseContext) {
|
||||||
(p.Commands[cmd])(ctx, dbContext)
|
command := p.Commands[cmd]
|
||||||
|
if !command.validateArgs(ctx.Args) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
command.exec(ctx, dbContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Plugin) ExecutePayload(payload string, ctx *MsgContext, dbContext *DatabaseContext) {
|
func (p *Plugin) ExecutePayload(payload string, ctx *MsgContext, dbContext *DatabaseContext) {
|
||||||
(p.Payloads[payload])(ctx, dbContext)
|
pl := p.Payloads[payload]
|
||||||
|
if !pl.validateArgs(ctx.Args) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pl.exec(ctx, dbContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Plugin) executeMiddlewares(ctx *MsgContext, db *DatabaseContext) bool {
|
func (p *Plugin) executeMiddlewares(ctx *MsgContext, db *DatabaseContext) bool {
|
||||||
for _, m := range p.Middlewares {
|
for _, m := range p.Middlewares {
|
||||||
if !m.Execute(ctx, db) {
|
if !m.Execute(ctx, db) {
|
||||||
@@ -75,42 +100,43 @@ func (p *Plugin) executeMiddlewares(ctx *MsgContext, db *DatabaseContext) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
func (c *Command) validateArgs(args []string) bool {
|
||||||
|
if len(args) != len(c.args) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, arg := range c.args {
|
||||||
|
if arg.regex == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !arg.regex.MatchString(args[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
Name string
|
name string
|
||||||
Executor CommandExecutor
|
exec CommandExecutor
|
||||||
Order int
|
order int
|
||||||
Async bool
|
async bool
|
||||||
}
|
|
||||||
type MiddlewareBuilder struct {
|
|
||||||
name string
|
|
||||||
executor CommandExecutor
|
|
||||||
order int
|
|
||||||
async bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMiddleware(name string, executor CommandExecutor) *MiddlewareBuilder {
|
func NewMiddleware(name string, executor CommandExecutor) *Middleware {
|
||||||
return &MiddlewareBuilder{name: name, executor: executor, order: 0, async: false}
|
return &Middleware{name, executor, 0, false}
|
||||||
}
|
}
|
||||||
func (m *MiddlewareBuilder) SetOrder(order int) *MiddlewareBuilder {
|
func (m *Middleware) SetOrder(order int) *Middleware {
|
||||||
m.order = order
|
m.order = order
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
func (m *MiddlewareBuilder) SetAsync(async bool) *MiddlewareBuilder {
|
func (m *Middleware) SetAsync(async bool) *Middleware {
|
||||||
m.async = async
|
m.async = async
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
func (m *MiddlewareBuilder) Build() Middleware {
|
func (m *Middleware) Execute(ctx *MsgContext, db *DatabaseContext) {
|
||||||
return Middleware{
|
if m.async {
|
||||||
Name: m.name,
|
go m.exec(ctx, db)
|
||||||
Executor: m.executor,
|
|
||||||
Order: m.order,
|
|
||||||
Async: m.async,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (m Middleware) Execute(ctx *MsgContext, db *DatabaseContext) {
|
|
||||||
if m.Async {
|
|
||||||
go m.Executor(ctx, db)
|
|
||||||
} else {
|
} else {
|
||||||
m.Execute(ctx, db)
|
m.Execute(ctx, db)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user