l10n and cmd generator WIP

This commit is contained in:
2026-02-17 22:44:23 +03:00
parent 4527dd661a
commit bb51a0ecb1
4 changed files with 149 additions and 67 deletions

8
bot.go
View File

@@ -192,16 +192,16 @@ func (b *Bot) AddPlugins(plugin ...Plugin) *Bot {
func (b *Bot) AddMiddleware(middleware ...Middleware) *Bot {
b.middlewares = append(b.middlewares, 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 {
first := b.middlewares[i]
second := b.middlewares[j]
if first.Order == second.Order {
return first.Name < second.Name
if first.order == second.order {
return first.name < second.name
}
return first.Order < second.Order
return first.order < second.order
})
return b

26
cmd_generator.go Normal file
View 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
}

View File

@@ -1,6 +1,7 @@
package laniakea
import (
"encoding/base64"
"encoding/json"
"strings"
@@ -112,3 +113,32 @@ func (b *Bot) checkPrefixes(text string) (string, bool) {
}
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))
}

View File

@@ -1,72 +1,97 @@
package laniakea
import (
"log"
"regexp"
"git.nix13.pw/scuroneko/extypes"
)
type CommandExecutor func(ctx *MsgContext, dbContext *DatabaseContext)
type PluginBuilder struct {
name string
commands map[string]CommandExecutor
payloads map[string]CommandExecutor
middlewares extypes.Slice[*PluginMiddleware]
const (
CommandValueStringType CommandValueType = "string"
CommandValueIntType CommandValueType = "int"
CommandValueBoolType CommandValueType = "bool"
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 {
Name string
Commands map[string]CommandExecutor
Payloads map[string]CommandExecutor
Middlewares extypes.Slice[*PluginMiddleware]
Commands map[string]Command
Payloads map[string]Command
Middlewares extypes.Slice[PluginMiddleware]
}
func NewPlugin(name string) *PluginBuilder {
return &PluginBuilder{
name: name,
commands: make(map[string]CommandExecutor),
payloads: make(map[string]CommandExecutor),
func NewPlugin(name string) *Plugin {
return &Plugin{
Name: name,
Commands: map[string]Command{},
Payloads: map[string]Command{},
Middlewares: extypes.Slice[PluginMiddleware]{},
}
}
func (p *PluginBuilder) Command(f CommandExecutor, cmd ...string) *PluginBuilder {
for _, c := range cmd {
p.commands[c] = f
}
func (p *Plugin) AddCommand(command Command) *Plugin {
p.Commands[command.command] = command
return p
}
func (p *PluginBuilder) Payload(f CommandExecutor, payloads ...string) *PluginBuilder {
for _, payload := range payloads {
p.payloads[payload] = f
}
func (p *Plugin) AddPayload(command Command) *Plugin {
p.Payloads[command.command] = command
return p
}
func (p *PluginBuilder) AddMiddleware(middleware *PluginMiddleware) *PluginBuilder {
p.middlewares = p.middlewares.Push(middleware)
func (p *Plugin) AddMiddleware(middleware PluginMiddleware) *Plugin {
p.Middlewares = p.Middlewares.Push(middleware)
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) {
(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) {
(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 {
for _, m := range p.Middlewares {
if !m.Execute(ctx, db) {
@@ -75,42 +100,43 @@ func (p *Plugin) executeMiddlewares(ctx *MsgContext, db *DatabaseContext) bool {
}
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 {
Name string
Executor CommandExecutor
Order int
Async bool
}
type MiddlewareBuilder struct {
name string
executor CommandExecutor
exec CommandExecutor
order int
async bool
}
func NewMiddleware(name string, executor CommandExecutor) *MiddlewareBuilder {
return &MiddlewareBuilder{name: name, executor: executor, order: 0, async: false}
func NewMiddleware(name string, executor CommandExecutor) *Middleware {
return &Middleware{name, executor, 0, false}
}
func (m *MiddlewareBuilder) SetOrder(order int) *MiddlewareBuilder {
func (m *Middleware) SetOrder(order int) *Middleware {
m.order = order
return m
}
func (m *MiddlewareBuilder) SetAsync(async bool) *MiddlewareBuilder {
func (m *Middleware) SetAsync(async bool) *Middleware {
m.async = async
return m
}
func (m *MiddlewareBuilder) Build() Middleware {
return Middleware{
Name: m.name,
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)
func (m *Middleware) Execute(ctx *MsgContext, db *DatabaseContext) {
if m.async {
go m.exec(ctx, db)
} else {
m.Execute(ctx, db)
}